Article  |  Development

Computed Macros in Ember

Reading time: ~ 2 minutes

Computed Macros in Ember

Mandatory Disclaimer: The primary example for this post is an anti-pattern. Not to say that a developer who will remain nameless has not unknowingly stumbled upon it at one point in their Ember career. In this particular case it should just be taken as an example... not a recommendation of “best practice.”

Computed properties in Ember are used in many different ways. In model definitions when you are accessing values on itself, or in controllers for defining variables for the template that are otherwise illegal due to the limited logic supported by HTMLBars markup. A convenient subset of these are the computed macros, which allow you to define controller, or component wide variables accessing properties on an object. What is unique about the macros are the distinct subset of controls that they give you over things like one-way binding, aliasing, or basic functionality like returning only unique objects from a collection when dealing with controller models, or properties on models.

In the following example, we set the computed property fullName, which we then alias to givenName utilizing the computed macro ‘alias’.

Something along these lines:

//Model computed properties
  Ember.DSModel.extend({
    firstName: DS.attr('string'),
    lastName: DS.attr('string'),

    fullName: function() {
      return [this.get('firstName'), this.get('lastName')].join(' ');
    }.property('firstName', 'lastName')

    givenName: Ember.computed.alias(fullName)
  });

  obj.setProperties({'firstName': 'Planet',
                     'lastName': 'Argon'
                   });
  obj.get('fullName'); // -> "Planet Argon"
  obj.get('givenName'); // -> "Planet Argon"

Let’s assume the following component is used at consistent depth in relationship to its parent controller. In this case, just for example, we are going to bury it 2 layers deep. Controller -> Component -> Component...

Computed properties are also incredibly useful for when you need to access an often used object that’s in a hard-to-reach place. A good example of this is accessing DS.Store from within a component. This can be done by referencing the targetObject of the parentView of the component like so:

  export default Ember.Component.extend({
    actions: {  
      findUserWithSameName: function(name) {
        this.get('parentView.targetObject.store').filter('user', function(user) {
          return user.get('firstName') === name;
        });
      },

      //Accessing the store worked so well the first time!
      findMeThisUser: function(id) {
        this.get('parentView.targetObject.store').peekRecord('user', id);
      }
    }
  });

As you continue along your path of “We’ll fix it later”, this can get unwieldy as references to the Store increase within the component. Unbeknownst to you, a redesign was just approved that will change the where the component lives on the page, resulting in it coming up a level in the hierarchy.

Fortunately, by moving this parentView.targetObject.store reference into an Ember.computed macro we can easily prepare for the unpredictable nature of development!

  export default Ember.Component.extend({
    // store: Ember.computed.alias('parentView.targetObject.store'),
    store: Ember.computed.alias('targetObject.store'),

    actions: {  
      findPartnerWithSameName: function(name) {
        this.get('store').filter('user', function(user) {
          return user.get('firstName') === name;
        });
      },

      findMeThisUser: function(id) {
        this.get('store').peekRecord('user', id);
      }
    }
  });

For real, though: The “Actions up, Data down” pattern within Ember implies that retrieval of objects from the Store should happen within Routes, which then pass objects down the chain of command, as opposed to Components fetching their models explicitly as highlighted above. Hindsight is nearly 20:20 and learning needs to happen somewhere. Handling the choices of developers before you is the nature of life in an agency, and systemic refactors can be a big ask for a client.

All jokes aside, computed property macros can be a useful tool for a wide variety of applications, from declaring oneWay bindings, to cleaning up long chained '&&' or '||' statements. EvilTrout has a wonderful post Computed Property Macros highlighting the majority of the use cases.

Computed property macros are a powerful tool, one that can be easily missed in the documentation. They serve as succinct patterns to build references to commonly used attributes that may be wordy, or otherwise clutter your code.

How are you using Ember.computed macros?

Have a project that needs help?