Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon

Classes and Instances of Ember Object Model

Save for later
  • 12 min read
  • 05 Feb 2016

article-image

In this article by Erik Hanchett, author of the book Ember.js cookbook, Ember.js is an open source JavaScript framework that will make you more productive. It uses common idioms and practices, making it simple to create amazing single-page applications. It also let's you create code in a modular way using the latest JavaScript features. Not only that, it also has a great set of APIs in order to get any task done. The Ember.js community is welcoming newcomers and is ready to help you when required.

(For more resources related to this topic, see here.)

Working with classes and instances

Creating and extending classes is a major feature of the Ember object model. In this recipe, we'll take a look at how creating and extending objects works.

How to do it

  1. Let's begin by creating a very simple Ember class using extend(), as follows:
    const Light = Ember.Object.extend({
      isOn: false
    });
    

    This defines a new Light class with a isOn property. Light inherits the properties and behavior from the Ember object such as initializers, mixins, and computed properties.

    Ember Twiddle Tip

    At some point of time, you might need to test out small snippets of the Ember code. An easy way to do this is to use a website called Ember Twiddle. From that website, you can create an Ember application and run it in the browser as if you were using Ember CLI. You can even save and share it. It has similar tools like JSFiddle; however, only for Ember. Check it out at http://ember-twiddle.com.

  2. Once you have defined a class you'll need to be able to create an instance of it. You can do this by using the create() method. We'll go ahead and create an instance of Light.
    constbulb = Light.create();

Accessing properties within the bulb instance

  1. We can access the properties of the bulb object using the set and get accessor methods. Let's go ahead and get the isOn property of the Light class, as follows:
    console.log(bulb.get('isOn'));

    The preceding code will get the isOn property from the bulb instance.

  2. To change the isOn property, we can use the set accessor method:
    bulb.set('isOn', true)

    The isOn property will now be set to true instead of false.

Initializing the Ember object

The init method is invoked whenever a new instance is created. This is a great place to put in any code that you may need for the new instance.

In our example, we'll go ahead and add an alert message that displays the default setting for the isOn property:

const Light = Ember.Object.extend({
  init(){
    alert('The isON property is defaulted to ' + 
      this.get('isOn'));
  },
  isOn: false
});

As soon as the Light.create line of code is executed, the instance will be created and this message will pop up on the screen.

The isON property is defaulted to false.

Subclass

Be aware that you can create subclasses of your objects in Ember. You can override methods and access the parent class by using the _super() keyword method. This is done by creating a new object that uses the Ember extend method on the parent class.

Another important thing to realize is that if you're subclassing a framework class such as Ember.Component and you override the init method, you'll need to make sure that you call this._super(). If not, the component may not work properly.

Reopening classes

At anytime, you can reopen a class and define new properties or methods in it. For this, use the reopen method.

In our previous example, we had an isON property. Let's reopen the same class and add a color property, as follows:

  1. To add the color property, we need to use the reopen() method:
    Light.reopen({
          color: 'yellow'
        });
    
  2. If required, you can add static methods or properties using reopenClass, as follows:
    Light.reopen({
          wattage: 40
        });
    

    You can now access the static property:

    Light.wattage

How it works

In the preceding examples, we have created an Ember object using extend. This tells Ember to create a new Ember class. The extend method uses inheritance in the Ember.js framework. The Light object inherits all the methods and bindings of the Ember object.

The create method also inherits from the Ember object class and returns a new instance of this class. The bulb object is the new instance of the Ember object that we created.

There's more

To use the previous examples, we can create our own module and have it imported to our project.

To do this, create a new MyObject.js file in the app folder, as follows:

// app/myObject.js
import Ember from 'ember';
export default function() {
    const Light = Ember.Object.extend({
        init(){
            alert('The isON property is defaulted to ' + this.get('isOn'));
        },
        isOn: false
    });

    Light.reopen({
        color: 'yellow'
    });

    Light.reopenClass({
    wattage: 80
    });

    const bulb = Light.create();

    console.log(bulb.get('color'));
    console.log(Light.wattage);
}

This is the module that we can now import to any file of our Ember application.

In the app folder, edit the app.js file. You'll need to add the following line at the top of the file:

// app/app.js
import myObject from './myObject';

At the bottom, before the export, add the following line:

myObject();

This will execute the myObject function that we created in the myObject.js file. After running the Ember server, you'll see the isOn property defaulted to the false pop-up message.

Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime

Working with computed properties

In this recipe, we'll take a look at the computed properties and how they can be used to display data, even if that data changes as the application is running.

How to do it

Let's create a new Ember.Object and add a computed property to it, as shown in the following:

  1. Begin by creating a new description computed property. This property will reflect the status of isOn and color properties:
    const Light = Ember.Object.extend({
      isOn: false,
      color: 'yellow',
    
      description: Ember.computed('isOn','color',function() {
        return 'The ' + this.get('color') + ' light is set to ' 
          + this.get('isOn');
      })
    
    });
    
  2. We can now create a new Light object and get the computed property description:
    const bulb = Light.create();
    bulb.get('description'); //The yellow light is set to false
    

    The preceding example creates a computed property that depends on the isOn and color properties. When the description function is called, it returns a string describing the state of the light.

    Computed properties will observe changes and dynamically update whenever they occur.

  3. To see this in action, we can change the preceding example and set the isOn property to false. Use the following code to accomplish this:
    bulb.set('isOn', true);
    bulb.get('description') //The yellow light is set to true
    

    The description has been automatically updated and will now display that the yellow light is set to true.

Chaining the Light object

Ember provides a nice feature that allows computed properties to be present in other computed properties. In the previous example, we created a description property that outputted some basic information about the Light object, as follows:

  1. Let's add another property that gives a full description:
    const Light = Ember.Object.extend({
      isOn: false,
      color: 'yellow',
      age: null,
    
      description: Ember.computed('isOn','color',function() {
        return 'The ' + this.get('color') + ' light is set to ' + this.get('isOn');
      }),
    
      fullDescription: Ember.computed('description','age',function() {
        return this.get('description') + ' and the age is ' + this.get('age')
      }),
    
    });
    
  2. The fullDescription function returns a string that concatenates the output from description with a new string that displays the age:
    const bulb = Light.create({age:22});
    bulb.get('fullDescription'); //The yellow light is set to false and the age is 22
    

    In this example, during instantiation of the Light object, we set the age to 22. We can overwrite any property if required.

Alias

The Ember.computed.alias method allows us to create a property that is an alias for another property or object.

Any call to get or set will behave as if the changes were made to the original property, as shown in the following:

const Light = Ember.Object.extend({
  isOn: false,
  color: 'yellow',
  age: null,
  description: Ember.computed('isOn','color',function() {
    return 'The ' + this.get('color') + ' light is set to ' + this.get('isOn');
  }),
  fullDescription: Ember.computed('description','age',function() {
    return this.get('description') + ' and the age is ' + this.get('age')
}),
  aliasDescription: Ember.computed.alias('fullDescription')

});

const bulb = Light.create({age: 22});
bulb.get('aliasDescription'); //The yellow light is set to false and the age is 22.

The aliasDescription will display the same text as fullDescription since it's just an alias of this object. If we made any changes later to any properties in the Light object, the alias would also observe these changes and be computed properly.

How it works

Computed properties are built on top of the observer pattern. Whenever an observation shows a state change, it recomputes the output. If no changes occur, then the result is cached.

In other words, the computed properties are functions that get updated whenever any of their dependent value changes. You can use it in the same way that you would use a static property. They are common and useful throughout Ember and it's codebase.

Keep in mind that a computed property will only update if it is in a template or function that is being used. If the function or template is not being called, then nothing will occur. This will help with performance.

Working with Ember observers in Ember.js

Observers are fundamental to the Ember object model. In the next recipe, we'll take our light example and add in an observer and see how it operates.

How to do it

  1. To begin, we'll add a new isOnChanged observer. This will only trigger when the isOn property changes:
    const Light = Ember.Object.extend({
      isOn: false,
      color: 'yellow',
      age: null,
      description: Ember.computed('isOn','color',function() {
        return 'The ' + this.get('color') + ' light is set to ' + this.get('isOn')
      }),
      fullDescription: Ember.computed('description','age',function() {
        return this.get('description') + ' and the age is ' + this.get('age')
      }),
      desc: Ember.computed.alias('description'),
      isOnChanged: Ember.observer('isOn',function() {
        console.log('isOn value changed')
      })
    });
    
    const bulb = Light.create({age: 22});
    
    bulb.set('isOn',true); //console logs isOn value changed
    

    The Ember.observer isOnChanged monitors the isOn property. If any changes occur to this property, isOnChanged is invoked.

    Computed Properties vs Observers

    At first glance, it might seem that observers are the same as computed properties. In fact, they are very different. Computed properties can use the get and set methods and can be used in templates. Observers, on the other hand, just monitor property changes. They can neither be used in templates nor be accessed like properties. They also don't return any values. With that said, be careful not to overuse observers. For many instances, a computed property is the most appropriate solution.

  2. Also, if required, you can add multiple properties to the observer. Just use the following command:
    Light.reopen({  
    isAnythingChanged: Ember.observer('isOn','color',function() {
        console.log('isOn or color value changed')
      })
    });
    
    const bulb = Light.create({age: 22});
    bulb.set('isOn',true); // console logs isOn or color value changed
    bulb.set('color','blue'); // console logs isOn or color value changed
    

    The isAnything observer is invoked whenever the isOn or color properties changes. The observer will fire twice as each property has changed.

Synchronous issues with the Light object and observers

It's very easy to get observers out of sync. For example, if a property that it observes changes, it will be invoked as expected. After being invoked, it might manipulate a property that hasn't been updated yet. This can cause synchronization issues as everything happens at the same time, as follows:

  1. The following example shows this behavior:
    Light.reopen({
        checkIsOn: Ember.observer('isOn', function() {
          console.log(this.get('fullDescription'));
        })
    });
    
    const bulb = Light.create({age: 22});
    bulb.set('isOn', true);
    

    When isOn is changed, it's not clear whether fullDescription, a computed property, has been updated yet or not. As observers work synchronously, it's difficult to tell what has been fired and changed. This can lead to unexpected behavior.

    To counter this, it's best to use the Ember.run.once method. This method is a part of the Ember run loop, which is Ember's way of managing how the code is executed.

  2. Reopen the Light object and you can see the following occurring:
    Light.reopen({
        checkIsOn: Ember.observer('isOn','color', function() {
          Ember.run.once(this,'checkChanged');
        }),
        checkChanged: Ember.observer('description',function() {
          console.log(this.get('description'));
        })
    });
    const bulb = Light.create({age: 22});
    bulb.set('isOn', true);
    bulb.set('color', 'blue');
    

    The checkIsOn observer calls the checkChanged observer using Ember.run.once. This method is only run once per run loop. Normally, checkChanged would be fired twice; however, since it's be called using Ember.run.once, it only outputs once.

How it works

Ember observers are mixins from the Ember.Observable class. They work by monitoring property changes. When any change occurs, they are triggered. Keep in mind that these are not the same as computed properties and cannot be used in templates or with getters or setters.

Summary

In this article you learned classes and instances. You also learned computed properties and how they can be used to display data.

Resources for Article:


Further resources on this subject: