The Path to Contextual Components: Understanding positional parameters.

Ember.js 2.3 introduces a brand new feature: contextual components. This feature aims to better component composition and the usage of components for DSL.

Before diving into this new feature, we will go through some of the details of current components invocation. In this post, we will see how positional params are used.

Positional params Vs. Attributes

Positional params are the secret behind liquid-fire or the new link-to component. It allows the programmer to automatically assign to an attribute the value of a positional parameter.

Positional params are declared as class properties in the component. The value of positionalParams will determine how and to which attributes the parameters will be mapped to.

There are two types of positional parameters: named and rest positional parameters.

Named positional parameters map one value to one attribute. Let’s see and example:

// app/components/my-if.js
import Ember from 'ember';

const { computed } = Ember;

const MyIfComponent = Ember.Component.extend({});

MyIfComponent.reopenClass({
positionalParams: ['predicateValue']
});

export default MyIfComponent;
{{! app/templates/components/my-if.hbs }}

{{#if predicateValue}}
Yay!
{{yield}}
{{ else }}
????
{{yield to="inverse"}}
{{/if}}

We are setting positionalParams to an array containing only one value: the string 'predicateValue'. When the component is rendered, Ember.js extracts and process the positional params, setting the property in the component’s instance.

Calling the component using this syntax {{my-if booleanValue}} is virtually the same as calling it with an explicit attribute {{my-if predicateValue=booleanValue}}.

Then, we can use this new component like the following example:

{{! app/templates/application.hbs }}

{{#my-if currentBooleanValue}}
This is true
{{ else }}
This is false
{{/my-if}}

You can see a complete working example in this twiddle (files in this gist).

When you don’t know the number of parameters

Some components behave in different ways depending on the number of parameters. LinkToComponent treat a parameter in different ways depending on the number it received.

Rest positional parameters sets the property in positionalParams to an array containing all the properties.

Let’s take a look at the list-properties:

// app/components/list-properties.js
import Ember from 'ember';

const { computed } = Ember;

const ListPropertiesComponent = Ember.Component.extend({
object: computed.readOnly('params.firstObject'),
properties: computed(
'params.[]',
{
get() {
let [,...props] = this.get('params');
return props;
}
}
)
});

ListPropertiesComponent.reopenClass({
positionalParams: 'params'
});

export default ListPropertiesComponent;
{{! app/templates/components/list-properties.hbs }}

{{#each properties as |prop|}}
{{yield prop (get object prop)}}
{{/each}}

And then use it in a template in the following way:

{{#list-properties obj 'a' 'c' 'b'
tagName='dl' as |key value|}}

{{key}}{{value}}

{{/list-properties}}

In this example, positionalParams property is just a string. Ember.js will take the array of all positional parameters ([obj, 'a', 'b', 'c']) and set it in the params property in our component’s instance. Then we create two computed properties, object and properties, based on the head and tail of said property.

Gotchas

  1. If a component invocation finds both a positional parameter and an attribute for the same property, then an assertion is raised.

Follow up in Understanding dynamic components and Contextual Components!

Author: Serabe

Mathematician, and Ruby and JavaScript programmer. Sometimes I speak at conferences and local meetups.

Leave a Reply

Your email address will not be published. Required fields are marked *