Sat Dec 10 2016
Paul Shan
I’m using Ember 2.10.0 here
So we are in the part 4 of out Ember.js tutorial series. By now you would have already know how to install & scaffold ember and also about ember template syntaxes. In this part we will discuss about ember objects and why instead of POJO you need to use Ember.Object
only.
Objects are very important in ember. Every element in ember is extended from Ember.Object
; be it a controller
, model
, router
or component
. Ember also provides ways to write constructors and mixins like normal JavaScript objects.
While creating a class, you must inherit it from the base ember Object class, Ember.Object
with the method .extend()
.
const Student = Ember.Object.extend({
firstName: null,
lastName: null,
getFullName(){
return `${this.firstName} ${this.lastName}`;
},
getStudentType(){
return "General Student";
}
});
As you can see, we’ve inherited the class Student from Ember.Object
. So, the extend()
method will be there in Student
class as well. So let’s inherit another class PhDStudent
from the class Student
and try to override the methods.
const PhDStudent = Student.extend({
title: "Dr.",
getFullName(){
let studentFullName = this._super(); //same method of parent class can be called by ._super()
return `${this.title} ${studentFullName}`;
},
getStudentType(){
return "PhD Student";
}
});
An instance of any class can be created with the method .create()
. Lets code some examples.
let jack = Student.create({
firstName: "Jack",
lastName: "Bauer"
});
let david = PhDStudent.create({
firstName: "David",
lastName: "Palmer"
});
jack.getFullName(); //Jack Bauer
david.getFullName(); //Dr. David Palmer
jack.getStudentType(); //General Student
david.getStudentType(); //PhD Student
As I said previously also, that you must obey the Ember way
. You must do things the way ember wants you to do. Setters and getters are the perfect example for this.
While retrieving or setting a property of an object you need to use .get()
an .set()
.
jack.get('firstName'); //Jack
jack.set('firstName', 'Jacky');
jack.get('firstName'); //Jacky
If you don’t use setters and getters and try to set or retrieve the value directly just as normal JavaScript object, the observers and computed properties will not work properly.
You don’t have to declare the entire class at one go. You can use the .reopen()
to define the class partially. Below is an example.
const Student = Ember.Object.extend({
firstName: null,
lastName: null
});
Student.reopen({
school: "Void Canvas"
});
let stu = Student.create();
stu.get('school'); //Void Canvas
Likewise you can .reopen()
the class multiple times and add/override the properties and methods.
Method .reopenClass()
can be used to add/override static properties or methods to a class. The syntax is pretty similar to .reopen()
.
const Student = Ember.Object.extend({
});
Student.reopenClass({
website: "http://voidcanvas.com/"
});
let stu = Student.create();
stu.get('website'); //undefined
Student.website; // http://voidcanvas.com/
As you can see, using .reopenClass()
we’ve introduced a static property
named website
. As this is a static one, so it can not be accessed by the instance stu
. However you can get that from the class itself, which is Student
. You can also declare static methods using .reopenClass()
.
The two methods .reopen()
and .reopenClass()
looks similar, but they do not do similar things.
.reopen()
is used to add/override properties and methods of any class..reopenClass()
is used to add/override static
properties and methods..reopen()
can not be accessed by the class name, however the ones added using .reopenClass()
can only be accessed using the class.An example below will clear the confusion.
const Student = Ember.Object.extend({
});
Student.reopenClass({
isActive: false
});
// override property of Student instance
Student.reopen({
isActive: true
});
Student.isActive; // false - because it is static property
let stu = Student.create();
stu.get('isActive'); // true
Computed properties are special kind of properties, which are dependent on other properties and calculate its value. Let me show you an example with our Student class.
const Student = Ember.Object.extend({
firstName: null,
lastName: null,
fullName: Ember.computed('firstName', 'lastName', function(){
return `${this.firstName} ${this.lastName}`;
}),
init(){
this._super();
setTimeout(()=>{
this.set('firstName', 'David');
}, 5000);
}
});
let stu = Student.create({
firstName: "Paul",
lastName: "Shan"
});
stu.get('fullName');//Paul Shan
//after 5000 milliseconds
stu.get('fullName');//David Shan
As you can see, fullName
is not a function here. It’s a property. You can .get()
the property. Whenever the firstName
or lastName
property of the Student instance stu will change, the value of fullName
will also change.
The method above just observes a property of the ember object. But what if you want to observe each element of an array; or if someone inserted or deleted some elements from an array? Well, for that you can use @each
. Below is an example.
const Student = Ember.Object.extend({
marks: null,
totalMarks: Ember.computed('[email protected]', function(){
let total = 0;
this.get('marks').forEach((mark)=>{
total+=mark.score;
});
return total;
}),
init(){
this._super();
this.set('marks', [
Ember.Object.create({subject: "Math", score: 60 }),
Ember.Object.create({subject: "Chemistry", score: 70 }),
Ember.Object.create({subject: "Physics", score: 80 })
]);
}
});
let stu = Student.create();
stu.get('totalMarks'); //210
As computed properties are not normal properties where you can .set()
a value; cause it’s a compute function; but there are ways to set the value. Here is an example.
const Student = Ember.Object.extend({
firstName: null,
lastName: null,
fullName: Ember.computed('firstName', 'lastName', {
get(key) {
return `${this.get('firstName')} ${this.get('lastName')}`;
},
set(key, value) {
let [firstName, lastName] = value.split(/\s+/);
this.set('firstName', firstName);
this.set('lastName', lastName);
return value;
}
})
});
let stu = Student.create({
firstName: "Paul",
lastName: "Shan"
});
stu.get('fullName');//Paul Shan
stu.set('fullName', 'David Palmer');
stu.get('fullName');//David Palmer
As you can see, here the last parameter of Ember.computed
is not a function, but an object. This object has two methods get
and set
. The method get
is just like the previous function in Ember.computed
which just returns the aggregated value. However the set
is a little tricky.
In set
, it’s taking two parameters. First one is key
which is the name of the computed property itself and the second one is value, which is the new value you want to set. So if we distribute the value
properly to the properties on which our computed property is dependent upon, the problem will be solved. And that’s what we just did. We split the value and created two variables firstName and lastName and set them in the Ember Object’s firstName and lastName property.
There are so many common scenarios of computed properties in the world; considering whom, ember gave you some predefined macro
. Below is one example of how a macro works, with the macro match
which helps matching regx.
const Student = Ember.Object.extend({
email: null,
hasValidEmail: Ember.computed.match('email', /^.+@.+\..+$/)
});
let stu = Student.create();
stu.get('hasValidEmail'); //false
stu.set('email','abcde');
stu.get('hasValidEmail'); //false
stu.set('email','[email protected]');
stu.get('hasValidEmail'); //true
The macros are really helpful and reduces the number of lines in your code. The list of the ember macros can be found in api documentation.
As you already know about computed properties, it won’t be harder for you to understand observers. As the name suggests, the observers are there to observe the change of value in any property, including the computed properties.
The difference between computed property and observer is, you can not .get()
an observer. It’s just a function to be executed whenever any of the property it’s observing changes. And computed property is a property and which will only recalculate if anywhere in your application that computed property is being .get()
.
const Student = Ember.Object.extend({
firstName: null,
lastName: null,
onFirstNameChange: Ember.observer('firstName', function(){
console.log("first name changed!");
}),
init(){
this._super();
setTimeout(()=>{
this.set('firstName', 'David');
}, 5000);
}
});
let stu = Student.create({
firstName: "Paul",
lastName: "Shan"
});
//after 5000 milliseconds the console will display
//first name changed!
Remember observer are heavy weighted elements; thus may cause performance issue if used excessively. Generally most of the problems can be solved using computed properties.
In ember an enumerable is an object which can contain multiple child objects in it. The most used enumerable is Array.
As ember means data binding and it needs to observe whenever a data is changed, it always forces you to go with the ember way. Usage of .set()
and .get()
are the example of it. Now, likewise in case of Array enumerable, you need to use .pushObject()
and .popObject()
instead of the default .push()
and .pop()
. There are alternative for other methods too.
Standard Method | Ember Method |
---|---|
push | pushObject |
pop | popObject |
reverse | reverseObjects |
shift | shiftObject |
unshift | unshiftObject |
You can also get the first and the last object of an array with .get('firstObject')
and .get('lastObject')
.
Few examples are as below.
let students = [
Student.create({
firstName: "Paul",
lastName: "Shan"
}),
Student.create({
firstName: "Jack",
lastName: "Bauer"
}),
Student.create({
firstName: "Sandip",
lastName: "Roy"
}),
Student.create({
firstName: "David",
lastName: "Palmer"
})
];
students.get('firstObject').get('firstName'); //Paul
students.get('lastObject').get('firstName'); //David
students.pushObject(Student.create({
firstName: "Rajinikanth"
}));
students.get('lastObject').get('firstName'); //Rajinikanth
You can check all Enumerable related apis in ember’s api documentation
Now you are well aware of ember objects and how they work. If you went through the api links we shared inside the article, you are probably aware of all syntaxes related to Objects. In the next part of this Ember.js full tutorial series, we will talk about Routers.
SHARE THIS ARTICLE
Thu Mar 10 2016
OAuth authentications are pretty popular now a days and another thing which is popular is JavaScript. This article shows how to plugin google’s oAuth api for authentication in your own node application.Sat Mar 01 2014
This is a continuation of my CSS3 loader snippet collection series. I've provided spinning css3 animation loader in the part 1 of this series and here in part 2, I'm providing various square type loading