Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

metal

Package Overview
Dependencies
Maintainers
3
Versions
38
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

metal

Build UI components in a solid, flexible way

  • 1.0.0-alpha
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
1.7K
increased by5%
Maintainers
3
Weekly downloads
 
Created
Source

Metal.js

Build Status Dependencies Status DevDependencies Status

Metal.js is a JavaScript library for building from simple widgets to full scale applications.

Even though it's powerful, Metal.js is very small, being only around 9kb after compressed with gzip. It's also really well tested, currently with 99% coverage of unit tests, besides having great performance.

Architecture

Metal.js's main classes are Attribute and Component. Component actually extends from Attribute, thus containing all its features. The main difference between the two is that Component's extra features are related to rendering. So you could just use Attribute directly if your module doesn't do any rendering. But if your module does need rendering logic though, then Component will work better for you.

One thing that can be really useful for a developer when building a component, is to separate the rendering logic from the business logic. This can be achieved on Metal.js by modules built on top of Component, that integrate with template engines. Metal.js already provides an implementation called SoyComponent that integrates with Soy Templates, also referred to as Closure Templates.

Architecture Chart

Dependencies

  • For build tooling, you'll need Node.js v0.10 or newer.
  • For Soy templates compilation, you'll need Java SDK v1.7 or newer.

Setup

  1. Install Bower, if you don't have it yet.
  2. Run bower install metal. The code will be available at bower_components/metal.

Usage

With the code already available, you can use Metal.js by just importing the desired module on your js file and calling what you wish on it. For example:

import core from './bower_components/metal/src/core';

// You can now call any function from Metal.js's core module.
core.isString('Hello World');

Note that Metal.js is written in ES6 (a.k.a ECMAScript 2015), so you can also use ES6 on your code like we did on the example. Since ES6 isn't fully implemented on browsers yet though, either a polyfill or a build process is necessary before using Metal.js on a website. See the Build Tasks section for more details.

Alias

Having to supply the relative path to bower_components is not cool and, besides that, it may cause problems when a module doing that is imported later as a bower dependency of another project.

Knowing that, Metal.js allows the use of aliases to refer to bower dependencies. It basically allows importing dependencies by just using a prefix instead of the whole path to the bower folder location. Note that this will only work when using Metal.js's build tools or adding a similar logic to your build process yourself (though we provide a helper function on Metal.js's npm package).

With aliases, the previous example can be rewritten like this:

import core from 'bower:metal/src/core';

Attribute

The Attribute class provides a way of defining attributes for the classes that extend it, as well as watching these attributes for value changes.

The following example is a class that extends from Attribute and defines an attribute named foo on itself:

import Attribute from '../bower_components/metal/src/attribute/Attribute';

class MyAttributes extends Attribute {
	constructor(opt_config) {
		super(opt_config);
	}
}

MyAttributes.ATTRS = {
	foo: {
		value: 'Initial value'
	}
}

If you're familiar with YUI, you'll notice that this is very similar to how attributes are defined there. You basically just need to list all attributes you'll be using on the ATTRS static variable, and provide their configuration options, like initial value and validator. For a list of all valid options, take a look at Attribute's docs.

You can access or change an object's attributes in the same way you'd access or change any object property.

var obj = new MyAttributes();
console.log(obj.foo); // Prints 'Initial value'

obj.foo = 'New value';
console.log(obj.foo); // Prints 'New value'

You can also listen to attribute value changes by listening to the appropriate event.

obj.on('fooChanged', function(event) {
	// event.prevVal has the previous value.
	// event.newVal has the new value.
});

To see all features of the Attribute class, take a look at its unit tests.

SoyComponent

This section will explain how to build rich widgets on Metal.js, by taking advantage of the SoyComponent class. By using SoyComponent, you'll be able to easily separate business logic from rendering logic, as it provides an integration with soy templates.

Building a widget with SoyComponent is simple, you just need to create two files: one with your soy templates, and the other with your JavaScript logic.

So, for example, let's say we want to create a widget called MyWidget, that has a body and a footer with content. The JavaScript file would look like this:

import SoyComponent from '../bower_components/metal/src/soy/SoyComponent';
import from './myWidget.soy.js';

class MyWidget extends SoyComponent {
	constructor(opt_config) {
		super(opt_config);
	}
}

MyWidget.ATTRS = {
	bodyContent: {
		value: 'Initial body content.'
	},
	footerContent: {
		value: SoyComponent.sanitizeHtml('<footer>Initial footer content.</footer>')
	}
};

This file just defines a class named MyWidget, makes it extend from SoyComponent, imports the compiled soy templates and defines two attributes. Note that html strings need to be properly sanitized, otherwise they will be escaped by default before rendering.

Now we just need a soy file for MyWidget's rendering logic. It would look like this:

{namespace Templates.MyWidget}

/**
 * This renders the component's whole content.
 */
{template .content}
	{delcall MyWidget.body data="all" /}
	{delcall MyWidget.footer data="all" /}
{/template}

/**
 * This renders the body part of the component.
 * @param bodyContent
 */
{template .body}
	<p>{$bodyContent}</p>
{/template}

/**
 * This renders the footer part of the component.
 * @param footerContent
 */
{template .footer}
	<footer>{$footerContent}</footer>
{/template}

Looking at that you can see that it's just a basic soy file that defines some templates. For this soy file to work well with SoyComponent its namespace just needs to be in the format: Templates.{name of widget}.

Note that, on the soy file, we have divided the main template into subtemplates, one for the body content and one for the footer. This is not necessary, but can be really helpful, as SoyComponent will handle these as special parts of the widget, automatically rerendering them when one of the attributes listed as params of a template changes. In MyWidget's case this means that whenever the value of the bodyContent attribute is changed, the body template will be called, and that part of the widget will be updated, even though there is no JavaScript code on MyWidget to handle this logic. The same goes for the footerContent attribute and the footer template.

SoyComponent's logic for updating the widget's contents automatically is very smart, so it won't cause a rerender unless it's necessary. So if a change causes a template to be called again, but the resulting HTML from the template is the same that was rendered for the last time, it will be ignored. This is done by compressing and caching the hash code of a template's results when it's called, and later using it to compare with new results to decide if a new content should be rendered or not.

Finally, to render an instance of MyWidget, just call render, passing any attribute values that you want to initialize:

new MyWidget({headerContent: 'My Header'}).render(parentElement);

For a more complete and working example, take a look at the metal-boilerplate repo. Among other things, it lists all optional lifecycle functions that can be implemented for SoyComponent.

Nested Components

Since we want to be able to separate business logic from rendering, it'd be really useful to be able to reference components on the template files. That would make it easier to correctly place the child component at the right position inside the parent, and would make the template more complete so it would be able to render the whole component by itself (see Decorate).

This can already be done with SoyComponent. For example, let's say we have the Modal and Button components, and the modal wants to render buttons on its footer. Inside modal.soy we'd see the following:

{template .footer}
	{delcall Button}
		{param id: 'ok' /}
		{param label: 'Ok' /}
	{/delcall}
	{delcall Button}
		{param id: 'cancel' /}
		{param label: 'Cancel' /}
	{/delcall}
{/template}

When Modal is rendered, the two specified buttons will be rendered as well. Also, the button instances can be accessed from the components property inside the modal instance, indexed by their ids:

modal.components.ok // The instance for the 'Ok' button
modal.components.cancel // The instance for the 'Cancel' button

Inline events

Another feature Metal.js has that can be very useful is the ability to declare events inside templates, directly on the desired element. Besides being simple and intuitive, this feature allows Metal.js to handle attaching events itself, and so this can be done in the best way possible, with delegates for example, without the user having worry about that at all.

By using SoyComponent, for example, you can add inline listeners like this:

{template .button}
	<button data-onclick="handleClick"></button>
{/template}

Then, you just need to define a handleClick method on your component, and it will be called whenever the event is triggered.

Decorate

Progressive enhancement is a feature that is very important for a lot of people. Knowing about this, Metal.js is prepared to deal with content that already comes rendered from the server. In that case, instead of calling render the developer can call decorate instead. This will skip the rendering phase of the component, running only the code that enhances it with JavaScript behavior instead.

For SoyComponent, this means that the template won't be rendered, but the attached lifecycle method will still be called.

It's important to note that building components with SoyComponent also helps with progressive enhancement in another way: by providing a faithful template that can be run by the server without having to duplicate the rendering code or run JavaScript at all.

Performance

Metal.js was built from the first with performance in mind. We've run performance tests to compare it with other libraries and got really good results that show the benefits of using it.

In one of the tests we made, we built a simple list widget on three different libraries: Metal.js, YUI and React. We then measured the time it took to render those widgets with 1000 items each on three different situations:

  • First Render - Creating and rendering the list for the first time, on a blank element.
  • Decorate - Creating and decorating a list that was previously rendered on the DOM.
  • Update - Changing the contents of the first item of the list, causing a rerender.

The chart below shows the results we obtained on Safari:

Performance Test - List

In this previous test, the list widget was built on all three libraries as a single component that renders each list item itself. We also did another similar test, with a list widget that was built using nested components instead, on which the list component renders other components that represent list items. Since YUI doesn't have this concept of nested components, this test was only done for Metal.js and React.

Once again, the chart below shows the results we obtained on Safari:

Performance Test - List

Tools

Metal.js comes together with a set of gulp tasks designed to help develop with it. To learn more about them and use them, take a look at: https://github.com/metal/metal-tasks.

Browser Support

Sauce Test Status

Keywords

FAQs

Package last updated on 09 Jun 2015

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc