I'm all about Ember.js recently

How to Make an Ember Module Resolve as Another One

I wanted to write another short, and hopefully useful, post just as I did recently for binding the style attribute.

No configuration is simpler than no configuration

About a month ago I was working to add authorization to the Rock and Roll with Ember application. I used my favorite addon, Torii, to help with that and opted to do the authorization via the google-oauth2-bearer provider. To restore the session, Torii looks up the application (Torii) adapter, but the session initialization and closing code used the google-oauth2-bearer adapter. So I had two separate files, which I was not happy about and I did not want to merge everything into the application adapter, as it does not give a hint about its contents then.

My idea was to make it possible to use another adapter to restore the session from, via a configuration option. Matthew Beale hinted at a solution that removes the need for a configuration option and since I haven’t seen this before, I want to share it with you.

Import from target module, then reexport

The Ember resolver is the piece that maps qualified full names (like route:blog or controller:bands) to module names.

In my case, Torii makes the resolver look up torii-adapter:application to fetch the session from and I wanted this to be resolved to torii-adapter:google-oauth2-bearer. In the Ember CLI project, that is equivalent of having the app/torii-adapters/application.js file export what is exported by app/torii-adapters/google-oauth2-bearer.js.

When phrased like this, the solution is near and I am somewhat embarrassed it took me a few attempts to arrive at this.

So the solution is to import in app/torii-adapters/application.js what app/torii-adapters/google-oauth2-bearer.js exports and then reexport it:

1
2
3
4
// app/torii-adapters/application.js
import GoogleOAuth2BearerAdapter from './google-oauth2-bearer';

export default GoogleOAuth2BearerAdapter;
1
2
3
4
// app/torii-adapters/google-oauth2-bearer.js
export default Ember.Object.extend({
  (...)
});

Voila, we have “tricked” the resolver without adding any configuration (and thus complexity) to the addon.

Complex Components in Ember.js - Part 2 - Towards a More Reactive Component

This is part 2 of my Complex Component Design series. Here are the preceding posts:


In the previous part of this series, the implementation of the main user flows were explained in detail. I ended the post by saying that I was not content with the implementation for several reasons, the most crucial of which was that parent components needed to be passed down to children, so that children can register themselves with their parent. That, in turn, allowed parents to reach their children and call methods on them directly instead of using events, actions and data bindings for communication. In this post, we’ll see how to get rid of these and replace them with more reactive solutions.

Remove the need for direct access to the input

Currently, the autocomplete component (the parent) yields itself to its children. auto-complete-input binds its own autocomplete attribute to it so that it can register itself with its parent when inserted:

<!-- tests/dummy/app/templates/index.hbs -->
{{#auto-complete
      on-select=(action "selectArtist")
      on-input=(action "filterArtists")
      class="autocomplete-container" as
        |autocomplete isDropdownOpen inputValue
         toggleDropdown onSelect onInput|}}
  <div class="input-group">
    {{auto-complete-input
        autocomplete=autocomplete
        value=inputValue
        on-change=onInput
        type="text"
        class="combobox input-large form-control"
        placeholder="Select an artist"}}
    (...)
  </div>
(...)
{{/auto-complete}}
1
2
3
4
5
6
7
8
9
10
11
// addon/components/auto-complete-input.js
import Ember from 'ember';

export default Ember.TextField.extend({
  autocomplete: null,

  registerWithAutocomplete: Ember.on('didInsertElement', function() {
    this.get('autocomplete').registerInput(this);
  }),
  (...)
});

This is needed when the item is autocompleted and the autocompleted segment is pre-selected so that the user can type over it if it’s not the item they had in mind:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// addon/components/auto-complete.js
export default Ember.Component.extend({
  (...)
  actions: {
    inputDidChange(value) {
      (...)
      Ember.run.scheduleOnce('afterRender', this, function() {
        (...)
        const firstOption = this.get('list.firstOption');
        if (firstOption) {
          const autocompletedLabel = firstOption.get('label');
          this.set('focusedOption', firstOption);
          this.get('on-select')(firstOption.get('item'));
          this.set('inputValue', autocompletedLabel);
          Ember.run.next(() => {
            this.get('input.element').setSelectionRange(value.length, autocompletedLabel.length);
          });
        }
      });
    }
  }
});

On the very last line, the component accesses the input directly, to select (and highlight) the portion of the item that was autocompleted. That’s why we need the whole registration process.

Since inputDidChange is triggered from the auto-complete-input component, we could get rid of this direct coupling if there was a way to react to the action’s result in the auto-complete-input itself. That way is called closure actions.

Fire, but don’t forget

As opposed to the fire-and-forget nature of “ordinary” (aka. element) actions, closure actions provide a way to react to the action’s outcome at the source, where the action was fired from.

Since closure actions are functions, they can have return values. If the action triggers an async action, it’s best to return a promise from the upstream handler to which the event source can attach its handler to.

Let’s see how that works in our case.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// addon/components/auto-complete.js
export default Ember.Component.extend({
  (...)
  actions: {
    inputDidChange(value) {
      this.get('on-input')(value);
      this.set('isDropdownOpen', true);
      return new Ember.RSVP.Promise((resolve, reject) => {
        (...)
        Ember.run.scheduleOnce('afterRender', this, function() {
          const firstOption = this.get('list.firstOption');
          if (firstOption) {
            const autocompletedLabel = firstOption.get('label');
            this.set('focusedOption', firstOption);
            this.get('on-select')(firstOption.get('item'));
            this.set('inputValue', autocompletedLabel);
            Ember.run.next(() => {
              resolve({ start: value.length, end: autocompletedLabel.length });
            });
          }
        });
      });
    }
  }
});

The code did not change a lot, but now a promise is returned on line 8. It is resolved on 18, where start and end designate the cursor positions of the selection.

The action handler in the auto-complete-input component needs to be modified to set the selection higlight itself:

1
2
3
4
5
6
7
8
9
10
11
// addon/components/auto-complete-input.js
import Ember from 'ember';

export default Ember.TextField.extend({
  valueDidChange: Ember.on('input', function() {
    const value = this.$().val();
    this.get('on-change')(value).then(({ start, end }) => {
      this.get('element').setSelectionRange(start, end);
    });
  })
});

Calling on-change will call the above inputDidChange function. Instead of firing the (element) action and forgetting about it, we now call the (closure) action and then “wait” for the resulting promise to be resolved. Once it does, we set the selection range.

We could now remove all the registration code and the passing down of the autocomplete instance to the input component.

Remove the need for direct access to the list options

There is still another instance of the same. It serves to give access to the autocomplete component to the auto-complete-option, through the auto-complete-list.

<!-- tests/dummy/app/templates/index.hbs -->
{{#auto-complete
      on-select=(action "selectArtist")
      on-input=(action "filterArtists")
      class="autocomplete-container" as |autocomplete isDropdownOpen inputValue
                                         toggleDropdown onSelect onInput|}}
  <div class="input-group">
    {{auto-complete-input
        value=inputValue
        on-change=onInput
        type="text"
        class="combobox input-large form-control"
        placeholder="Select an artist"}}
    {{#auto-complete-list autocomplete=autocomplete isVisible=isDropdownOpen class="typeahead typeahead-long dropdown-menu" as |list|}}
      {{#each matchingArtists as |artist|}}
        {{#auto-complete-option
            id=artist.id
            label=artist.name
            item=artist
            list=list
            on-click=onSelect
            activeId=selectedArtist.id}}
          <a href="#">{{artist.name}}</a>
        {{/auto-complete-option}}
      {{/each}}
    {{/auto-complete-list}}
    (...)
  </div>
{{/auto-complete}}

I am not copying all the registration code here as it’s very boilerplatey. Each option, when inserted into the DOM, registers itself with its list, while the list registers itself with the auto-complete component. The latter has an options property to access the options:

1
2
// addon/components/auto-complete.js
options: Ember.computed.readOnly('list.options')

This access is needed to be able to cycle through the options by using the cursor keys and then select one of them by using the return key. Here is the code that handles keypresses (more precisely, keydowns):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// addon/components/auto-complete.js
export default Ember.Component.extend({
  (...)
  keydownMap: {
    8:  'startBackspacing', // backspace
    13: 'selectOption',  // return
    27: 'closeDropdown', // escape
    38: 'focusPrevious', // up key
    40: 'focusNext', // down key
  },

  handleKeydown: Ember.on('keyDown', function(event) {
    const map = this.get('keydownMap');
    const code = event.keyCode;
    const method = map[code];
    if (method) {
      return this[method](event);
    }
  }),
  (...)
});

This is pretty simple so far. If a key we care about was pressed, we call the appropriate method to handle it. Let’s see how focusing works:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// addon/components/auto-complete.js
export default Ember.Component.extend({
  (...)
  options: Ember.computed.readOnly('list.options'),

  focusPrevious: function(event) {
    event.preventDefault();
    const focused = this.get('focusedOption');
    let index = this.get('options').indexOf(focused);
    if (this.get('isDropdownOpen')) {
      index = index - 1;
    }
    this.focusOptionAtIndex(index);
  },

  focusNext: function(event) {
    event.preventDefault();
    let index = 0;
    const focused = this.get('focusedOption');
    if (focused) {
      index = this.get('options').indexOf(focused);
      if (this.get('isDropdownOpen')) {
        index = index + 1;
      }
    }
    this.focusOptionAtIndex(index);
  },

  focusOptionAtIndex: function(index) {
    const options = this.get('options');
    if (index === -1) {
      index = options.get('length') - 1;
    } else if (index === options.get('length')) {
      index = 0;
    }
    const option = this.get('options').objectAt(index);
    if (!option) {
      return;
    }
    this.focusOption(option);
  },

  focusOption: function(option) {
    const focused = this.get('focusedOption');
    if (focused) {
      focused.blur();
    }
    this.set('focusedOption', option);
    option.focus();
  },
  (...)
});

focusPrevious and focusNext make sure that the focused index is kept within the bounds of the avaiable number of options and then focus the previous (or next) one by calling option.focus() directly (line 49).

There is one more key press concerning related to options, the return key. It should select the currently focused option, if there is one:

1
2
3
4
5
6
7
8
9
10
11
12
13
// addon/components/auto-complete.js
export default Ember.Component.extend({
  (...)
  options: Ember.computed.readOnly('list.options'),
  selectOption: function(event) {
    event.preventDefault();
    const focused = this.get('focusedOption');
    if (focused) {
      this.send('selectItem', focused.get('item'), focused.get('label'));
    }
    this.set('isDropdownOpen', false);
  },
});

This code also leverages the access to the options, indirectly through this.get('focusedOption'). Furthermore, it assumes that each option has an item and label properties. Not stellar.

It won’t be a piece of cake to get rid of direct coupling in all of these, so let’s get to it.

Change the focused option without accessing the options

In the first step, we’ll change the focused option without directly commanding the options to focus/unfocus. We’ll then tackle selecting the focused option.

We can use simple data binding to have the focused option available. By maintaining and yielding a focusedIndex in the “control center”, the autocomplete component, autocomplete-option components can bind to it and know whether they are focused or not.

Here is how code the templates need to change:

<!-- addon/templates/components/autocomplete.hbs -->
{{yield isDropdownOpen
        inputValue
        focusedIndex
        selectedIndex
        (action "toggleDropdown")
        (action "selectItem")
        (action "inputDidChange")}}
<!-- tests/dummy/app/templates/index.hbs -->
{{#auto-complete
      on-select=(action "selectArtist")
      on-input=(action "filterArtists")
      options=matchingArtists
      displayProperty="name"
      class="autocomplete-container" as |isDropdownOpen inputValue
                                         focusedIndex selectedIndex
                                         toggleDropdown onSelect onInput|}}
  <div class="input-group">
    {{auto-complete-input
        value=inputValue
        on-change=onInput
        type="text"
        class="combobox input-large form-control"
        placeholder="Select an artist"}}
    {{#auto-complete-list
        isVisible=isDropdownOpen
        class="typeahead typeahead-long dropdown-menu" as |list|}}
      {{#each matchingArtists as |artist index|}}
        {{#auto-complete-option
            label=artist.name
            item=artist
            on-click=onSelect
            isFocused=(eq focusedIndex index)
            isSelected=(eq selectedIndex index)}}
          <a href="#">{{artist.name}}</a>
        {{/auto-complete-option}}
      {{else}}
        <li><a href="#">No results.</a></li>
      {{/each}}
    {{/auto-complete-list}}
    (...)
  </div>
{{/auto-complete}}

Note the new focusedIndex and selectedIndex attributes, yielded by the top-level component that isFocused and isSelected in the auto-complete-option are bound to.

The eq helper comes from ember-truth-helpers and will evaluate to true if its params are equal which is exactly what we want.

The autocomplete component needs to change to manage the new indexes instead of setting its focusedOption and calling option.set directly:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// addon/components/auto-complete.js
export default Ember.Component.extend({
  (...)
  optionsLength: Ember.computed.readOnly('options.length'),
  focusPrevious: function(event) {
    event.preventDefault();
    const currentIndex = this.get('focusedIndex');
    let newIndex;
    if (Ember.isNone(currentIndex)) {
      newIndex = this.get('optionsLength') - 1;
    } else if (currentIndex === 0) {
      newIndex = this.get('optionsLength') - 1;
    } else {
      newIndex = currentIndex - 1;
    }
    this.set('focusedIndex', newIndex);
    this.set('isDropdownOpen', true);
  },

  focusNext: function(event) {
    event.preventDefault();
    const currentIndex = this.get('focusedIndex');
    const lastIndex = this.get('optionsLength') - 1;
    let newIndex;
    if (Ember.isNone(currentIndex)) {
      newIndex = 0;
    } else if (currentIndex === lastIndex) {
      newIndex = 0;
    } else {
      newIndex = currentIndex + 1;
    }
    this.set('focusedIndex', newIndex);
    this.set('isDropdownOpen', true);
  },

  selectOption: function(event) {
    event.preventDefault();
    const focusedIndex = this.get('focusedIndex');
    if (Ember.isPresent(focusedIndex)) {
      this.set('selectedIndex', focusedIndex);
    }
    this.set('isDropdownOpen', false);
  },
});

That is simpler and less intrusive than before. (Setting isDropdown to true has been added as before the option’s focus method did the opening).

What’s missing is for the selected item to be sent to the outer world (in other words, for the selectItem to be triggered). Before, it was done by sending the selectItem action with the focused option’s item and label (see line 9 in the last snippet of the previous section) but we can no longer indulge in accessing the options directly. Consequently, it was replaced by setting the selectedIndex to the focusedIndex (see line 39 above).

The problem now is that selectItem needs to be called with the item and the label (the name of the selected artist to be set as the input’s value) and only the selected auto-complete-option component has that knowledge. So we need to set up a way for the auto-complete-option components to know when they become selected and then call that action. As these components are not the source of the event that lead to an option being selected by key press, we choose to use an observer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// addon/components/auto-complete-option.js
import Ember from 'ember';

export default Ember.Component.extend({
  tagName: 'li',
  classNames: 'ember-autocomplete-option',
  classNameBindings: Ember.String.w('isSelected:active isFocused:focused'),

  label: null,
  item: null,
  'on-click': null,
  isFocused: false,
  isSelected: false,

  didClick: Ember.on('click', function() {
    this._selectItem();
  }),

  didBecomeSelected: Ember.observer('isSelected', function() {
    const isSelected = this.get('isSelected');
    if (isSelected) {
      this._selectItem();
    }
  }),

  _selectItem() {
    const item = this.get('item');
    this.get('on-click')(item, this.get('label'));
  }
});

Line 21 and 22 is where the option realizes it has become the selected option, and then calls the corresponding (closure) action on line 28.

We’re done, we got rid of all the direct passing of component instances, registrations and direct property access and method calling. Even though we’re Demeter compliant, there are things that could be improved.

In the next episode…

One of these things is the observer. Observers fell out of favor some time ago, and for a good reason. They can be over eager and lead to scenarios where it is hard to see what’s going on. To prove my point, let me show you a bug I’ve just accidentally introduced. I call it the “JPJ is too good to be replaced” bug:

JPJ is too good to be replaced

(The code for this series is publicly available on Github here. I’ve tagged where we are now with ccd-part-two.)

So we’re not done yet. In the next post of the series, we’re going to fix that bug by replacing the observer and make other worthy improvements. Stay tuned!

Binding Style Attributes Warning in Ember

One warning Ember might print in your console concerns binding a property to the style attribute of a DOM element, like this:

<div class="progress-bar" style="{{barWidth}}">...</div>
export default Ember.Controller.extend({
  progress: 0,
  barWidth: Ember.computed('progress', {
    return 'width:' + this.get('progress') + '%';
  });
});

Handlebars escapes all html content put in double curlies but it does not do that with CSS, and thus the above makes possible a cross-site scripting attack. That is the reason for the warning and the fix for that is to convert the property (in the above case, barWidth) to a SafeString, which tells Ember that the content is safe to display. You should only do that after you have verified that the content you mark as safe cannot be injected by a malicious user. The guide describes how to do that:

export default Ember.Controller.extend({
  progress: 0,
  barWidth: Ember.computed('progress', {
    return new Ember.Handlebars.SafeString('width:' + this.get('progress') + '%');
  });
});

(Alternatively, you can call Ember.String.htmlSafe with the string you want to mark as safe, to the same effect.)

When I did this conversion in a recent project, though, the warning persisted. After spending a substantial amount of time pouring over the docs and even stepping through the warning stacktrace, I still could not find out what was wrong. What helped (as so many times already) was a good night sleep and taking another look at it in the morning.

Marking the string as safe was done correctly, but when binding it to the style attribute, I used double quotes around it, probably inhibiting Ember from seeing it as a SafeString:

<div class="progress-bar" style="{{barWidth}}">...</div>

So all I had to do to make the warning go away was to remove the quotes:

<div class="progress-bar" style={{barWidth}}>...</div>

I hope this saves you some time if you come across a similar situation in your work.

Rock and Roll With Ember.js Updated to Use Ember & Co 2.3

I have just released a new update to the Rock and Roll Ember.js book, which brings it up to Ember 2.3.0, ED 2.3.2 and Ember CLI 2.3.0-beta.1.

Other improvements made in this release are:

  • Update the PageFront section as it has now become even simpler to deploy apps to PageFront
  • Reset the song creation process when switching bands
  • Use ember-bootstrap and remove the section on manually adding assets to the build
  • Use consistent quoting in snippets and include import Ember from 'ember' wherever needed.
  • Remove a few paragraphs that talk about how things were in the past (like bind-attr)

Happy reading!

Complex Components in Ember.js - Part 1 - Analyzing User Flows

In this post I continue the Complex Component Design series I started back in September. I slightly renamed the series title as the original idea was to design and develop the component in the parts of the series but since the component is mostly “done”, I prefer to show how it works and how the different pieces fit together. I think this way of presenting things is still (perhaps equally) valuable and we’ll have a few open issues to work on “together” to further improve the component.

The component I described in the intro post serves to select an item from a list of items, either via a dropdown or by starting to type its name and then selecting it. Here is a very short demo about how that looks in practice:

Selecting an artist

We’ll go through the main UI flows and see how they are implemented via communication of the different layers of the component.

Getting familiar with the component

The template we’ll use (and which the above demo uses) to understand the functioning of the component looks like this:

<!-- tests/dummy/app/templates/index.hbs -->
<div class="form-group">
  <label>Choose an artist</label>
  {{#auto-complete
        on-select=(action "selectArtist")
        on-input=(action "filterArtists")
        class="autocomplete-container" as |autocomplete isDropdownOpen inputValue
                                           toggleDropdown onSelect onInput|}}
    <div class="input-group">
      {{auto-complete-input
          autocomplete=autocomplete
          value=inputValue
          on-change=onInput
          type="text"
          class="combobox input-large form-control"
          placeholder="Select an artist"}}
      {{#auto-complete-list autocomplete=autocomplete isVisible=isDropdownOpen
              class="typeahead typeahead-long dropdown-menu" as |list|}}
        {{#each matchingArtists as |artist|}}
          {{#auto-complete-option
              id=artist.id
              label=artist.name
              item=artist
              list=list
              on-click=onSelect
              activeId=selectedArtist.id}}
            <a href="#">{{artist.name}}</a>
          {{/auto-complete-option}}
        {{else}}
          <li><a href="#">No results.</a></li>
        {{/each}}
      {{/auto-complete-list}}
      {{#auto-complete-dropdown-toggle on-click=toggleDropdown
              class="input-group-addon dropdown-toggle"}}
        <span class="caret"></span>
      {{/auto-complete-dropdown-toggle}}
    </div>
  {{/auto-complete}}
</div>

This might seem somewhat daunting at first but as we grow acquainted with its details, our intimidation will subside.

The top-level component is auto-complete. This is the “command center”, the piece that manages the “global” state of the whole widget, like whether the dropdown is visible and what the current value of the input field is.

You might, with good reason, wonder why these are not handled by the sub-component where it’d feel more appropriate: the current value of the input field by auto-complete-input and the opened/closed state of the dropdown by auto-complete-dropdown-toggle.

The answer is that a change in these states can be triggered from multiple places and that several child components might need to know about them. The dropdown can be closed by the user clicking on one of the items in the dropdown (not on the little arrow of the toggle), while the current text in the input can be modified by inferring the item when the user starts to type (not just by actually typing out the whole text).

Data down, actions up - all the way down (and up)

That slight violation of separation of concerns (or is it at all?) fits perfectly with the most important component communication paradigm: Data down, actions up.

The input, when its value changes, sends an action up to its parent, notifying it of the change. The parent can then react to this, and communicate any data (state) changes via the attribute bindings it has to the input. This is why auto-complete needs to handle, or at least access, state that is used downstream by its sub-components.

The classical way of passing down data (and establishing a binding) from the parent to the child is through block parameters of the parent component. The auto-complete component has quite some:

<!-- tests/dummy/app/templates/index.hbs -->
{{#auto-complete
      on-select=(action "selectArtist")
      on-input=(action "filterArtists")
      class="autocomplete-container" as |autocomplete isDropdownOpen inputValue
                                         toggleDropdown onSelect onInput|}}
  (...)
{{/auto-complete}}

The block parameters are those found between the pipes, after the as keyword. You have to look into the component’s own template to see where they come from:

<!-- addon/templates/components/auto-complete.hbs -->
{{yield this isDropdownOpen inputValue
        (action "toggleDropdown") (action "selectItem") (action "inputDidChange")}}

Parameters are matched by position, so what is yielded in the first position becomes the first block parameter. In this case, we yield the component itself as the first parameter, the aforementioned component states as the 2nd and 3rd and then (closure) actions that will trigger functions in the auto-complete component when called in one of the child components. These serve as “remote controls” (a term used by Miguel Camba in his awesome presentation at EmberCamp) for child components to control their parent.

The way of upward communication from child components is calling these actions when appropriate.

We now have sufficient knowledge to follow the implemention of basic user flows, so let’s get into it.

Understanding UX flows

Manual selection from the dropdown

The most basic thing one can do with the widget is to pop open the list of options.

I discarded the parts that are not relevant to understand this, so we’re left with the following:

<!-- tests/dummy/app/templates/index.hbs -->
<div class="form-group">
  <label>Choose an artist</label>
  {{#auto-complete
        on-select=(action "selectArtist")
        on-input=(action "filterArtists")
        class="autocomplete-container" as |autocomplete isDropdownOpen inputValue
                                           toggleDropdown onSelect onInput|}}
    <div class="input-group">
      {{#auto-complete-list autocomplete=autocomplete isVisible=isDropdownOpen
              class="typeahead typeahead-long dropdown-menu" as |list|}}
        (...)
      {{/auto-complete-list}}
      {{#auto-complete-dropdown-toggle on-click=toggleDropdown
              class="input-group-addon dropdown-toggle"}}
        <span class="caret"></span>
      {{/auto-complete-dropdown-toggle}}
    </div>
  {{/auto-complete}}
</div>

The auto-complete-dropdown-toggle is the component that can be clicked to open or close the list of items. At a glance it seems like its on-click attribute is the action that will be triggered when the user clicks it but let’s see for sure:

1
2
3
4
5
6
7
8
9
10
11
12
13
// addon/components/auto-complete-dropdown-toggle.js
import Ember from 'ember';

export default Ember.Component.extend({
  tagName: 'span',
  classNames: 'ember-autocomplete-toggle',
  'data-dropdown': 'dropdown',
  'on-click': null,

  toggleDropdown: Ember.on('click', function() {
    this.get('on-click')();
  })
});

Indeed, it just calls the action that was passed into it, which is the toggleDropdown action of the topmost auto-complete component:

1
2
3
4
5
6
7
8
9
10
11
// addon/components/auto-complete.js
import Ember from 'ember';

export default Ember.Component.extend({
  (...)
  actions: {
    toggleDropdown() {
      this.toggleProperty('isDropdownOpen');
    },
  }
});

The toggleProperty method flips the value of its parameter, so if it was false it now becomes true. isDropdownOpen is yielded as a block parameter so when it becomes true, auto-complete-list will rerender as one of its attributes, isVisible has changed. That will then open the dropdown:

<!-- tests/dummy/app/templates/index.hbs -->
<div class="form-group">
  <label>Choose an artist</label>
  {{#auto-complete
      (...)
      class="autocomplete-container" as |autocomplete isDropdownOpen inputValue
                                           toggleDropdown onSelect onInput|}}
    <div class="input-group">
      {{#auto-complete-list autocomplete=autocomplete isVisible=isDropdownOpen
              class="typeahead typeahead-long dropdown-menu" as |list|}}
        (...)
      {{/auto-complete-list}}
    </div>
  {{/auto-complete}}
</div>

The same process is triggered when the toggle is clicked again, only this time isDropdownOpen goes back to false and thus the dropdown is closed.

Picking an item

The second feature we’ll look at is more like the second half of the first one: selecting an item by clicking (tapping) on it.

I have again restrained the template to the relevant bits, throwing away the input and the toggle:

<!-- tests/dummy/app/templates/index.hbs -->
<div class="form-group">
  <label>Choose an artist</label>
  {{#auto-complete
        on-select=(action "selectArtist")
        on-input=(action "filterArtists")
        class="autocomplete-container" as |autocomplete isDropdownOpen inputValue
                                           toggleDropdown onSelect onInput|}}
    <div class="input-group">
      (...)
      {{#auto-complete-list autocomplete=autocomplete isVisible=isDropdownOpen
              class="typeahead typeahead-long dropdown-menu" as |list|}}
        {{#each matchingArtists as |artist|}}
          {{#auto-complete-option
              id=artist.id
              label=artist.name
              item=artist
              list=list
              on-click=onSelect
              activeId=selectedArtist.id}}
            <a href="#">{{artist.name}}</a>
          {{/auto-complete-option}}
        {{else}}
          <li><a href="#">No results.</a></li>
        {{/each}}
      {{/auto-complete-list}}
      (...)
    </div>
  {{/auto-complete}}
</div>

When one of the items is clicked, the on-click attribute (which is the onSelect closure action provided by auto-complete) is called in the auto-complete-option component:

1
2
3
4
5
6
7
8
9
// addon/components/auto-complete-option.js
import Ember from 'ember';

export default Ember.Component.extend({
  (...)
  selectOption: Ember.on('click', function() {
    this.get('on-click')(this.get('item'), this.get('label'));
  }),
});

So where is onSelect defined? It is one of the block parameters yielded by auto-complete, more precisely the (action "selectItem") action:

<!-- addon/templates/components/auto-complete.hbs -->
{{yield this isDropdownOpen inputValue
        (action "toggleDropdown") (action "selectItem") (action "inputDidChange")}}

selectItem is quite straightforward:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// addon/components/auto-complete.js
import Ember from 'ember';

export default Ember.Component.extend({
  (...)
  actions: {
    selectItem(item, value) {
      this.get('on-select')(item);
      this.set('isDropdownOpen', false);
      this.set('inputValue', value);
    },
    (...)
  }
});

It first calls the on-select action that was passed into it from the “outside” (the controller), which just sets selectedArtist to the artist object encapsulated in the list item. It then sets the isDropdownOpen flag to false (which, by the mechanism seen in the previous point, closes the list) and sets the text in the input to the item’s label (the artist’s name).

Auto-completing an item

As the final example, let’s see a more complicated use case. When the user starts to type, the items that do not match the typed string will not be shown as options. Also, the first matching item will be auto-completed and selected, and the dropdown will be closed.

No surprises here, the same design principle will be applied as before. Pass down an action that should be called from a child, then change some property in the parent component that trickles down to the child which then rerenders itself because of the changed attribute.

Let’s see the relevants parts of the template:

<!-- tests/dummy/app/templates/index.hbs -->
<div class="form-group">
  <label>Choose an artist</label>
  {{#auto-complete
        on-select=(action "selectArtist")
        on-input=(action "filterArtists")
        class="autocomplete-container" as |autocomplete isDropdownOpen inputValue
                                           toggleDropdown onSelect onInput|}}
    <div class="input-group">
      {{auto-complete-input
          autocomplete=autocomplete
          value=inputValue
          on-change=onInput
          type="text"
          class="combobox input-large form-control"
          placeholder="Select an artist"}}
      {{#auto-complete-list autocomplete=autocomplete isVisible=isDropdownOpen
              class="typeahead typeahead-long dropdown-menu" as |list|}}
        {{#each matchingArtists as |artist|}}
          {{#auto-complete-option
              (...)
          {{/auto-complete-option}}
        {{else}}
          <li><a href="#">No results.</a></li>
        {{/each}}
      {{/auto-complete-list}}
      (...)
    </div>
  {{/auto-complete}}
</div>

We’ll start by the auto-complete-input this time where the input event, triggered by the user’s typing, is handled:

1
2
3
4
5
6
7
8
9
10
// addon/components/auto-complete-input.js
import Ember from 'ember';

export default Ember.TextField.extend({
  (...)
  valueDidChange: Ember.on('input', function() {
    const value = this.$().val();
    this.get('on-change')(value);
  })
});

This is almost the exact copy of calling the on-select action we saw before from auto-complete-option. Here, the on-change function is called that was passed down from the block param of auto-complete.

If we take a look in the template of auto-complete we see it creates a (action 'inputDidChange') closure action and yield that, so that should be the next thing to look at. Here is where most of the stuff happens:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// addon/components/auto-complete.js
import Ember from 'ember';

export default Ember.Component.extend({
  (...)
  actions: {
    inputDidChange(value) {
      this.get('on-input')(value);
      this.set('isDropdownOpen', true);
      const firstOption = this.get('list.firstOption');
      if (firstOption) {
        const autocompletedLabel = firstOption.get('label');
        this.get('on-select')(firstOption.get('item'));
        this.set('inputValue', autocompletedLabel);
        this.get('input.element').setSelectionRange(value.length, autocompletedLabel.length);
      }
    }
  }
});

We first call the on-input action which filters out the artists that do not match the typed prefix. The result of that is that matchingArtists will only contain the artists that do match. The dropdown is then opened to display these items (or an explanatory blurb if none matches). If there is at least one matching item, the first one is selected (and becomes selectedArtist).

As an UX improvement, the “inferred” range from the label in the input is selected, so that the user can continue typing and thus select another artist if the first one was not what they meant. (See when I type “J” in the demo).

Design concepts

I’m not totally happy with the current state of the component because of the following:

1) The auto-complete component reaches inside the auto-complete-input one (set in its input property) to call setSelectionRange on it (see the last code snippet).

2) The same component retrieves the options from the list and gets its item to select it. Again, this is quite intrusive and will break if the internals of auto-complete-option change.

3) Still the auto-complete component yields an instance of itself as a block parameter. This enables “downstream consumers” to access any of its properties and methods, breaking its encapsulation.

In presenting about these concepts at the Global Ember Meetup and at Ember.js Belgium, I said that I like to think about components as the objects of the UI. Thinking about them as objects helps to deliver the point that some (most?) object oriented practices should be applied to components, too. If this assumption is correct, we can leverage OOP design concepts and guidelines that we’ve been developing for decades, giving us a headstart on how to design (and what to watch out for) complex component hierarchies.

For example, I consider the set of block parameters yielded by a component as its public API. This means that yielding this from a component’s template is considered bad practice as it breaks encapsulation. In some cases, it’s relatively easy to find a way around it, in others it’s much more difficult. We’ll see if I can pull it off in the above case.

As a closing thought, notice how 95% of the feature’s implementation relied on block parameters and closure actions. They are fantastic tools to work with and I don’t know how anything could be achieved without them before they existed.

Pointers

Incidentally, Miguel Camba seems to think about components lately, too. I already mentioned his fantastic talk at EmberCamp this year called “Composable components”, but above that he has released ember-power-select, which serves the same purpose as the auto-complete component in my blog post series.

However, it’s much more mature and flexible so if you need a select dropdown in your app, use ember-power-select, as my component is for learning and demonstration purposes only. That said, I published it on Github under balinterdi/ember-cli-autocomplete if you want to take a look or follow along the blog posts while looking at its source code. I put a tag called ccd-part-one on the repo for this blog post.

In the next episode…

… of the series, I’d like to address (some of) my concerns I mentioned above and see how to fix them. Stay tuned!

Rock and Roll With Ember.js Updated to Ember 2.2.0

I have updated the app developed in the Rock and Roll with Ember.js book to Ember 2.2 and Ember CLI 1.13.12. I also replaced the now defunct Divshot deployment section with another great & simple deployment option, PageFront.

Above that, there is a great deal of “bug fixes and stability improvements” in this release, the list of which can be viewed on Github.

Thank you to everybody who reported issues and helped to improve the book.

The Tale of Two Bindings

Two weeks ago I had a presentation called “Complex component design” at the Global Ember Meetup.

When I had given the title and abstract of this presentation, I had wanted to speak about how to implement more involved, nested components in Ember 2 (but actually, more like 1.13+), which is a fascinating and complex (pun intended) topic. I had something like a highly reusable autocomplete input component in mind something that I had decided to explore further.

When I sat down to think about it, I had realized there is a related and equally fascinating topic, that of communication between components that live on the same page. As everything is soon becoming a component in Ember, the two are very similar. A difference is that communicating components in an app do not need such a high degree of flexibility as a reusable component (like an Ember addon). In any case, it does not hurt, since making them flexible facilitates their reuse.

In this post, I’ll show an example of simple component communication and focus on how moving from two-way bindings to one-way bindings changes that. Spending some time on playing around with this, I was very pleasantly surprised in what this shift enables.

The example

If you know me a bit, you’d expect the “app” to be Rock & Roll themed and you’d be right. I reduced the app in the book to a very simple interface where you can pick a band and then edit it on a form:

Edit band on a form

In the remainder of the post, we’ll see different implementations to achieve the validation and saving of the band. There will be 3 scenarios: the traditional one, using two-way bindings, the Glimmer version, using one-way bindings, DOM events and Data Down, Actions up (DDAU) and finally 1WAY Deluxe™: adding a few features on top of the second scenario that one-way bindings make easy (or possible at all).

Take 1: Traditional, two-way bound

Ignoring the list of bands on the left, the template belonging to the band route, where the band can be edited, contains the band-form (on the right of the screenshot), and some minimal markup. We pass in the band object, the on-save and the on-star-click closure actions to the band form:

<div class="panel panel-default band-panel">
  <div class="panel-body">
    {{band-form band=model
                errors=errors
                on-save=(action "saveBand")
                on-star-click=(action "updateRating")}}
  </div>
</div>

The controller has these actions, sets up the errors object and contains the validation logic. The hasErrors property will be true if the band’s name is empty:

import Ember from 'ember';

export default Ember.Controller.extend({
  hasValidName: Ember.computed.notEmpty('model.name'),
  hasErrors: Ember.computed.not('hasValidName'),

  setupErrors: Ember.on('init', function() {
    this.set('errors', Ember.Object.create());
  }),

  validate() {
    this.set('errors.name', this.get('hasValidName') ? null : "Name is required.");
  },

  actions: {
    updateRating(params) {
      const { item: band, rating } = params;
      band.set('rating', rating);
    },

    saveBand() {
      this.validate();
      if (this.get('hasErrors')) {
        return;
      }

      const band = this.get('model');
      return band.save().then(() => {
        console.log("Band is saved");
      });
    }
  }
});

Upon validation, the errors are set but this is only needed to be able to show the error in the template. this.get('hasErrors') is already true if the band’s name is an empty string.

The missing piece is the band-form template:

<div class={{if errors.name "form-group has-error" "form-group"}}>
  <label for="band-name">Name</label>
  {{input type="text" class="form-control" id="band-name" value=band.name}}
</div>
<div class="form-group">
  <label for="band-year">Formed in</label>
  {{input type="text" class="form-control" id="band-year" value=band.year}}
</div>
<div class="form-group">
  <label for="band-rating">Rating</label>
  {{star-rating id="band-rating" item=band rating=band.rating on-click=on-star-click}}
</div>
<button type="submit" class="btn btn-primary pull-right" {{action "saveBand"}}>Submit</button>

It uses the input helper which established two-way bindings between the value of the input and the property that was passed to it. When the user modifies the input, band.name changes in sync. Since band in the component is the model of the controller, the band name in the list changes as the name is edited:

Two-way bindings in action

In this scenario, communication between the top-level component (controller, if you will) and the band form is quite blunt. As data is two-way bound, there is no simple, “in-framework” way of not changing the name in the list when the name on the form is modified. There is shared state and the components do not act via messages: they pull two ends of the same string.

(In cases where you had to prevent that in the pre-Glimmer era, you had to resort to using a separate property, like band.newName, or using BufferedProxy.)

So let’s take a step forwards and see how this can be improved.

Take 2: One-way bound with DDAU

We’ll first replace the two-way binding with a one-way one and manually synchronize the upstream direction using DDAU. It will not seem like a big gain but it will enable us to go further and attain 1WAY Deluxe™.

The top-level template only needs a slight change. We no longer pass in an on-star-click action but instead an on-update one. This will serve for the upstream synchronization, setting what changed in the component on the band object (the model) of the controller.

<div class="panel panel-default band-panel">
  <div class="panel-body">
    {{band-form band=model
                errors=errors
                on-save=(action "saveBand")
                on-update=(action "updateBandProperty")}}
  </div>
</div>

In accordance, the only thing that has changed in the controller is that the updateStarRating action has been replaced by updateBandProperty. This is the manual syncing:

export default Ember.Controller.extend({
  (...)
  actions: {
    updateBandProperty(property, value) {
      this.get('model').set(property, value);
    },

    (...)
  }
});

In the template, the two-way bound input helpers are out, substituted by regular input tags. We attach event listeners to them which will trigger the synchronization proces (I wrote a post about how that works a few months ago):

<div class={{if errors.name "form-group has-error" "form-group"}}>
  <label for="band-name">Name</label>
  <input type="text" class="form-control" id="band-name" value={{band.name}}
    oninput={{action "nameDidChange" value="target.value"}} />
</div>
<div class="form-group">
  <label for="band-year">Formed in</label>
  <input type="text" class="form-control" id="band-year" value={{band.year}}
    oninput={{action "yearDidChange" value="target.value"}} />
</div>
<div class="form-group">
  <label for="band-rating">Rating</label>
  {{star-rating id="band-rating" item=band rating=band.rating on-click=(action "ratingDidChange")}}
</div>
<button type="submit" class="btn btn-primary pull-right" {{action "saveBand"}}>Submit</button>

nameDidChange, yearDidChange and ratingDidChange all end up calling the passed in closure action, on-update, with the name of the property that has changed and its new value. This calls updateBandProperty in the controller we already saw:

import Ember from 'ember';

export default Ember.Component.extend({
  tagName: 'form',
  band: null,
  errors: null,
  "on-update": null,
  "on-save": null,

  actions: {
    saveBand() {
      this.attrs['on-save']();
    },

    nameDidChange(value) {
      this.attrs['on-update']('name', value);
    },
    yearDidChange(value) {
      this.attrs['on-update']('year', value);
    },
    ratingDidChange(params) {
      const { rating } = params;
      this.attrs['on-update']('rating', rating);
    },
  }
});

From the outside, the app works just as before. The band name changes in the list as we edit it in the text field:

One-way bindings in action

However, we know that under the hood our code took control of propagating data changes. We have undone the string that kept the two sides (two components) tied strongly together. In the third and final iteration, we’ll leverage that to move validation where it belongs and add a micro-feature.

Take 3: 1WAY Deluxe™

Now, for the cool part. Now that we’re free to change band-related properties on the component without affecting the properties of the band object (the model of the controller), we no longer have a shared state.

The first thing we’ll do is to move the validation into the band-form component. band-form will be also less chatty. It will only send property updates when the form is submitted. That means we don’t need to pass in the errors object or an on-update action:

<div class="panel panel-default band-panel">
  <div class="panel-body">
    {{band-form band=model on-save=(action "saveBand")}}
  </div>
</div>

That implies that the controller can be really slimmed down to the saveBand action:

import Ember from 'ember';

export default Ember.Controller.extend({
  actions: {
    saveBand(properties) {
      const band = this.get('model');
      band.setProperties(properties);

      return band.save().then(() => {
        console.log("Band is saved");
      });
    }
  }
});

Note how the input field values in the band-form template are now bound to properties on the component as opposed to that of the passed in band object:

<div class={{if errors.name "form-group has-error" "form-group"}}>
  <label for="band-name">Name</label>
  <input type="text" class="form-control" id="band-name" value={{name}}
    oninput={{action "nameDidChange" value="target.value"}} />
</div>
<div class="form-group">
  <label for="band-year">Formed in</label>
  <input type="text" class="form-control" id="band-year" value={{year}}
    oninput={{action "yearDidChange" value="target.value"}} />
</div>
<div class="form-group">
  <label for="band-rating">Rating</label>
  {{star-rating id="band-rating" item=band rating=rating on-click=(action "ratingDidChange")}}
</div>
<div class="form-group button-panel">
  <button type="submit" class="btn btn-primary pull-right" {{action "saveBand"}}>Submit</button>
  <button type="button" class="btn btn-danger pull-right" {{action "reset"}}>Reset</button>
</div>

Little else has changed but a second button, Reset, already gives you a taste of things to come. Let’s see the component definition:

import Ember from 'ember';

export default Ember.Component.extend({
  tagName: 'form',
  band: null,
  "on-save": null,

  name:    null,
  year:    null,
  rating:  null,
  errors:  null,

  // Validation code comes here, copied verbatim from the controller

  resetOnInit: Ember.on('init', function() {
    this.resetFromBand();
  }),

  resetFromBand() {
    ['name', 'year', 'rating'].forEach((field) => {
      const valueInBand = this.get('band').get(field);
      this.set(field, valueInBand);
    });
  },

  actions: {
    saveBand() {
      this.validate();
      if (this.get('hasErrors')) {
        return;
      }

      return this.attrs['on-save'](this.getProperties(['name', 'year', 'rating']));
    },

    nameDidChange(value) {
      this.set('name', value);
    },
    yearDidChange(value) {
      this.set('year', value);
    },
    ratingDidChange(params) {
      const { rating } = params;
      this.set('rating', value);
    },
    reset() {
      this.resetFromBand();
    }
  }
});

I cut out the code responsible for validation since that has just been brought over from the controller.

The interesting stuff happens in resetFromBand which is both called when the component comes to life and when the Reset button is clicked. It copies the name, year and rating properties of the band onto those of the component, effectively resetting them to their original value. That’s the only reason we still need to pass in the band object into the component.

Also notice how the name and the rating are not updated on the band object as we interact with the form:

1WAY Deluxe in action

Having the data validated by the form acts as a guard. The save action is only triggered if the data was found to be valid. It is only then that the form’s data will overwrite that of the band object. Data flows both ways but in a very controlled way.

To take this further, thanks to closure actions, we could even display an error in the band-form component if the save operation fails on the controller:

export default Ember.Component.extend({
  (...)
  actions: {
    saveBand() {
      this.validate();
      if (this.get('hasErrors')) {
        return;
      }
      const properties = this.getProperties(['name', 'year', 'rating']);
      return this.attrs['on-save'](properties)
        .catch((error) => {
          this.set('errors.base', error.get('message'));
        });
    },

    (...)
  }
});

UPDATE

Take 4: 1WAY Deluxe™ without input cursor wackiness

The above 1WAY Deluxe™ has a bug that Robert Jackson pointed out and that I did not realize while building the demo app. The cursor in the text field always jumps back at the end of the text after each change:

1WAY Deluxe input wackiness

During the Glimmer rewrite he spend a lot of time tracking down that bug, the result of which is the ember-one-way-input Ember addon.

So that’s what we should use instead of regular input tags. We first install the addon with ember install ember-one-way-input. That gives us a one-way-input component that takes an update action which will be triggered at each change of the input’s value (more precisely, on both change and input events).

Let’s replace the input tags in the component’s template:

<div class={{if errors.name "form-group has-error" "form-group"}}>
  <label for="band-name">Name</label>
  {{one-way-input type="text" class="form-control" id="band-name" value=name
    update=(action "nameDidChange")}}
</div>
<div class="form-group">
  <label for="band-year">Formed in</label>
  {{one-way-input type="text" class="form-control" id="band-year" value=year
    update=(action "yearDidChange")}}
</div>
<div class="form-group">
  <label for="band-rating">Rating</label>
  {{star-rating id="band-rating" item=band rating=rating on-click=(action "ratingDidChange")}}
</div>
<div class="form-group button-panel">
  <button type="submit" class="btn btn-primary pull-right" {{action "saveBand"}}>Submit</button>
  <button type="button" class="btn btn-danger pull-right" {{action "reset"}}>Reset</button>
</div>

Nothing else needs to change for the cursor position weirdness to go away:

1WAY Deluxe input without cursor wackiness

Thank you to Robert Jackson and Toran Billups for spotting this and pointing me to the solution.

Conclusion

I’m really excited and curious about how many things this makes possible. As I said in my presentation, we’re (but surely: I am) only figuring out both the possibilities “managed” one-way bindings open up and the best way to work with them. So if you have thoughts or insights, please do share them in the comments.

NOTE: I published the demo app of this post on Github.

Rock and Roll With Ember 2 Just Went Live

The 2nd edition of my critically acclaimed book, Rock and Roll with Ember.js, has just been published. The number 2 does not only denote the 2nd edition but also the fact that it runs on Ember (and Ember Data) 2.0 and that it will follow the 2.x development path of the framework.

The 2nd edition adds 4 new chapters: Animations, Making an Ember addon, Writing modern JavaScript (ES2015) and Deployment. Here is the complete set list:

  1. Introduction to Ember.js
  2. Ember CLI
  3. Templates and data bindings
  4. Routing
  5. Nested routes
  6. Actions
  7. Components
  8. Controllers
  9. Advanced routing
  10. Talking to a backend - with Ember Data
  11. Testing
  12. Sorting and searching with query params
  13. Loading and error routes
  14. Helpers
  15. Animations
  16. Making an Ember addon
  17. ES2015 - Writing modern JavaScript
  18. Deployment

As many readers have said, keeping the book up-to-date with the latest stable Ember release also makes it a great reference book on current Ember idioms and features.

Oh, and did I mention I am running a 20% discount on all packages until Monday, 10/05, 23:59 PST? There are also a few other goodies in the bag, so I encourage you to check out the book now before prices revert to their normal level.

Select in Ember With Multiple Selection

A few weeks ago I wrote a blog post about how to do a select in Ember 2, that seemed to be popular. I also received good comments about advanced versions of the same problem, namely how the solution would have to change to deal with the case if the items to select from are objects and how to tackle multiple selections. I thus decided to do a Part 2, showing a solution for these cases. Comment are welcome, as always.

Multiple selection with simple strings as items

Let’s tackle the easier problem first, being able to select more than one items, but the items are simple string values. The values will serve both as the value and the content of the options.

I added some extra Bootstrap markup and a list to see which items are selected:

<div class="container">
  <div class="row">
    <div class="col-sm-8">
      <h2>Select some bands</h2>
      <select style="height:100px" class="form-control" multiple onchange={{action "selectBand"}}>
        {{#each bands as |bandChoice|}}
        <option value={{bandChoice}} selected={{include selectedBands bandChoice}}>{{bandChoice}}</option>
        {{/each}}
      </select>
    </div>
    <div class="col-sm-4">
      {{#if selectedCount}}
        <h2>Selected bands ({{selectedCount}})</h2>
      {{else}}
        <h2>Selected bands</h2>
      {{/if}}
      <ul class="list-group">
        {{#each selectedBands as |band|}}
          <li class="list-group-item">{{band}}</li>
        {{else}}
          <li class="list-group-item">No band selected.</li>
        {{/each}}
      </ul>
    </div>
  </div>
</div>

I added the multiple attribute to the select tag to allow multiple selections. Not much has changed from the earlier example. When the user selects an option, whether in a way that clears the earlier selection (simple click) or adds to it (ctrl/cmd + click), the onchange event is fired, and our selectBand handler will handle it. We expect that handler to set selectedBands so that the list of selected bands gets updated correctly. So let’s see the controller:

export default Ember.Controller.extend({
  bands: ['Pearl Jam', 'Tool', 'Long Distance Calling', 'Led Zeppelin'],

  selectedBands: [],

  selectedCount: Ember.computed.reads('selectedBands.length'),

  actions: {
    selectBand(event) {
      const selectedBands = Ember.$(event.target).val();
      this.set('selectedBands', selectedBands || []);
    }
  }
});

For multiple selections, jQuery, aliased as Ember.$, returns an array of the selected options values as the select’s value, so all we have to do is assign this to the selectedBands property. In case nothing is selected, val() returns null, so we guard against transferring this to selectedBands by defaulting to an empty array.

There is one more thing you might have noticed, and that is the include helper in the template. We want to mark the option as selected if its value is included in the selectedBands:

<select style="height:100px" class="form-control" multiple onchange={{action "selectBand"}}>
  {{#each bands as |bandChoice|}}
  <option value={{bandChoice}} selected={{include selectedBands bandChoice}}>{{bandChoice}}</option>
  {{/each}}
</select>

The include helper is not provided by Ember but it is rather easy to write ourselves:

import Ember from 'ember';

export function include(params) {
  const [items, value] = params;
  return items.indexOf(value) > -1;
}

export default Ember.Helper.helper(include);

That is all there is to it:

Multiple selection

Multiple selection with objects as items

This is just a tad more difficult, as we cannot directly have objects as options values. Let’s assume that these objects have a property that identifies them unambiguously (which is a fair assumption to make), usually referred to as id:

import Ember from 'ember';

export default Ember.Controller.extend({
  bands: [
    Ember.Object.create({ id: "1", name: 'Pearl Jam', formedIn: 1990 }),
    Ember.Object.create({ id: "2", name: 'Tool', formedIn: 1991 }),
    Ember.Object.create({ id: "3", name: 'Long Distance Calling', formedIn: 2003 }),
    Ember.Object.create({ id: "4", name: 'Led Zeppelin', formedIn: 1970 })
  ],
  (...)
});

We’ll use the id as the option value and display the name:

(...)
<select style="height:100px" class="form-control" multiple onchange={{action "selectBand"}}>
  {{#each bands as |bandChoice|}}
    <option value={{bandChoice.id}} selected={{include selectedBandIds bandChoice.id}}>{{bandChoice.name}}</option>
  {{/each}}
</select>
(...)

On the controller, we collect the id of each selected band, and if we need to display their names, we simply make the mapping between these two:

export default Ember.Controller.extend({
  (...)
  selectedBandIds: [],

  selectedBands: Ember.computed('selectedBandIds.[]', function() {
    return this.get('selectedBandIds').map((bandId) => {
      return this.get('bands').findBy('id', bandId);
    });
  }),
  (...)
});

bands.findBy is our makeshift store service, which allows us to find an object in a collection by its id. If we used Ember Data, it would become store.findRecord('band', bandId) or store.peekRecord('band', bandId). The only other difference from before is that we set selectedBandIds instead of selectedBands in the action handler:

export default Ember.Controller.extend({
  (...)
  actions: {
    selectBand(event) {
      const selectedBandIds = Ember.$(event.target).val();
      this.set('selectedBandIds', selectedBandIds || []);
    }
  }
});

Rock and Roll With Ember 2 Is Coming Soon!

tl;dr

I will publish a second, extended edition of my book, Rock and Roll with Ember in about two weeks, at the end of September. Here is what you need to know:

  • I make the same promise as with the current edition: you buy it once, you get all updates for free until 3.0 comes out
  • 4 extra chapters are added about advanced Ember topics, bringing the number of chapters to 18
  • There will be a short launch period where the book packages are offered at a discount.

To be in-the-loop about the launch and know about the launch discount, sign up below, at the end of the post. Read on for more details.

The short past, present and future of Rock and Roll with Ember

Almost exactly 7 months ago, I published my book, Rock and Roll with Ember.js, that helps people learn the glorious framework. Ember has since went through quite some changes and I kept the book up-to-date with roughly an update each month.

Ember 2 was released about a month ago, on the 13rd of August. True to the “stability without stagnation” philosophy, the 2.0 release did not contain any new features but dropped a lot of deadweight, API methods and syntaxes that had been deprecated somewhere along the 1.x process.

Thanks to the transparency of the road ahead through RFCs and discussions on other forums, it can already be clearly seen that the 2.x series will bring a great deal of new and fascinating features. Routable components, angle-bracket components and contextual lookup in components are some of the bigger things that we can expect on the road to 3.0. The expected simplifications (for example, routable components replacing controllers) will come with major changes in how one writes an idiomatic app in Ember 2.

I’ve been following along the 1.x series with fascination and a keen eye to apply the changes to my book and the included application. I want to do the same along the Ember 2.x line and thus I hereby announce the second edition of the book, Rock and Roll with Ember 2!

Free updates policy

To clarify, with Rock and Roll with Ember 2, I intend to follow the “book development and release” process of the first edition of the book that I’ve now named “Buy once, get all updates for free (until the next major version)”. I assert that if you buy any of my book packages, you get all subsequent updates for free, until 3.0 lands (which is probably 12-18 months away. Geez, do I really want to do that? :) ).

New content

Keeping the book’s current content up-to-date is not all. The second edition will contain 4 extra chapters on some advanced topics (Animations, Making an Ember CLI addon, Deployment and ES2015 & computed macros) bringing the total chapter count to 18 (the Getting ready for Ember 2.0 is retired).

Further content changes are expected as Ember 2 evolves (see above for what can already be known about these changes), so the content on launch day will by no means be the final content of the book. It will constantly improve and be kept in-sync with the latest stable 2.x version of Ember.

When does it launch?

Very soon, probably in two weeks, mark the date (with a pencil, for the moment): September 30, 2015.

Stand ready, Rock and Roll with Ember 2 is coming to a virtual bookstore near you!