4. WHAT IS
EMBER.JS
➤ A framework for creating ambitious web application
➤ Built for productivity (provides architecture)
➤ Opinionated (sometimes perceived as difficult to learn)
➤ Convention over Configuration
(besides awesome!)
7. APPS
NEEDS
➤ Keep it organized
➤ Modularize your code
➤ Separate concerns
➤ Establish conventions for smooth
teamwork
➤ Keep it testable
ARCHITECTUREBIG
8. EMBER CORE
CONCEPTS
➤ Classes and Instances
➤ Computed Properties
➤ Bindings
➤ Templates
➤ Route
➤ Models
➤ Components
➤ Data Down - Actions Up
➤ Lots of other features - we’ll just barely scratch the surface in
today’s session
10. EMBER
OBJECT1 import Ember from 'ember';
2
3 let Person = Ember.Object.extend({
4 say(message) {
5 alert(message);
6 }
7 });
8
9 let Bob = Person.create();
10 Bob.say('Hello World');
11 // alerts "Hello World"
12
13 let Man = Person.extend({
14 say(message) {
15 message += ', says the man.';
16 this._super(message);
17 }
18 });
19
20 let Tom = Man.create();
21 Tom.say('Hello World');
11. EMBER
OBJECT
➤ Obj.create() instead of new instances
➤ Obj.extend() for single inheritance (mixins exists as well)
➤ this._super() calls overridden implementation
➤ Obj.reopen() to edit class definition
➤ Obj.get(‘key’) and Obj.set(‘key’)
➤ Allows dynamically created property values
➤ Object can listen for properties changes
➤ Use of .setProperties({ key: value }) for multiple at time.
12. COMPUTED
PROPERTIES➤ Used to build a property that depends on other properties
➤ Provide any property key you access and the property value
will be recomputed if change
➤ Should not contain application behavior, and should generally
not cause any side-effects when called.
1 import Ember from 'ember';
2
3 let Person = Ember.Object.extend({
4 firstName: '',
5 lastName: '',
6
7 fullName: Ember.computed('firstName', 'lastName', function() {
8 return this.get('firstName') + ' ' + this.get('lastName');
9 })
10 });
11
12 let bob = Person.create({
13 firstName: 'Bob',
14 lastName: 'Esponja'
15 });
16
17 bob.get('fullName');
18 // "Bob Esponja"
14. BINDINGS
➤ Unlike most other frameworks that include some sort of binding
implementation, bindings in Ember can be used with any object.
➤ A one-way binding only propagates changes in one direction, using
computed.oneWay()
1 wife = Ember.Object.create({
2 householdIncome: 80000
3 });
4
5 Husband = Ember.Object.extend({
6 householdIncome: Ember.computed.alias('wife.householdIncome')
7 });
8
9 husband = Husband.create({
10 wife: wife
11 });
12
13 husband.get('householdIncome'); // 80000
14
15 // Someone gets raise.
16 wife.set('householdIncome', 90000);
17 husband.get('householdIncome'); // 90000
15. TEMPLATES
➤ Uses Handlebars + Ember helpers
➤ Ember adds partial, render, view and control helpers
➤ Each template is backed by a model
➤ They include helpers such as: other templates, if else, loops,
formatting helpers, expressions like {{model.firstName}},
outlets (which are placeholders), components
1 <div id="main">
2 <div class="container-fluid">
3
4 {{outlet}}
5
6 </div>
7 </div>
17. 1 import Ember from 'ember';
2
3 export default Ember.route.extend({
4
5 model: function() {
6 return [
7 {
8 title: “London Palace"
9 },
10 {
11 title: "Eiffel Tower"
12 }
13 ];
14 }
15 });
ROUTING(implementation of a base route)
The model usually is a
record. The result of a query
to ember-data store
1 import Ember from 'ember';
2
3 export default Ember.route.extend({
4
5 model(params) {
6 var url = 'https://api.github.com/repos/' + params.repo_id;
7 return Ember.$.getJSON(url);
8 }
9 });
The model can be a POJO
1 import Ember from 'ember';
2
3 export default Ember.route.extend({
4
5 model: function(params) {
6 return this.store.find('myModel', params);
7 },
8
9 setupController: function(controller, model) {
10 controller.set('model', model);
11 }
12 });
The result of an ajax call
19. ROUTES
➤ The route queries the model and makes it available in the controller and
templates
➤ When the template or models being shown to the user changes, Ember
automatically keeps the url in the browser’s address bar up-to-date
➤ This means that at any point, users are able to share the URL of your app
➤ Ember Add-on for Chrome and Firefox let you inspect routes / data / promises
➤ error and loading routes automatically created and used on model loading and
error states
21. MODELS
➤ In the first case the data comes from backend in the model itself (payload)
➤ In the second case the model only contains the ‘ID’ of the related model
and is loaded asynchrony with an specific call when required.
13 'roles': hasMany('roles', { embedded: 'always'}),
14 'office': belongsTo('office', { async: 'true' }),
(relationships)
You can also define custom attr options, for example (readOnly):
16 'entityId': attr('string', { readOnly: true }),
17 'entityType': attr('string', { readOnly: true }),
22. MODELS
➤ You can define custom types to fit any kind of data
➤ Makes it easy to maintain as you only need to change in one place
1 // app/transforms/utc.js
2 import DS from 'ember-data';
3 import moment from 'moment';
4
5 export default DS.Transform.extend({
6 serialize(value) {
7 let formatedDate = moment.utc(value).format('YYYY-MM-DDTHH:mm:ss', 'en');
8 return formatedDate;
9 },
10
11 deserialize(value) {
12 return value;
13 }
14 });
1 export default Model.extend({
2 'productId': attr('number'),
3 'eventTypeID': attr('number'),
4 'reasonSVID': attr('string'),
5 'notes': attr('string'),
6
7 'eventDate': attr('utc', { defaultValue: () => new Date() })
8 }
23. ADAPTER /
SERIALIZER➤ By default ember-data uses JSON-API format
➤ You can override default behavior either application level or for particular models
(fit any backend data even legacy one !!)
24. ➤ In Ember Data, the Adapter determines how data is persisted to a
backend data store, such as the URL format and headers for a
REST API.
➤ You can change default functionality (if your backend conventions
differ from ember-data assumptions) by extending it
ADAPTER(customization example)
1 import Ember from 'ember';
2 import DS from 'ember-data';
3
4 export default DS.JSONAPIAdapter.extend({
5 host: 'https://api.example.com',
6 namespace: 'api',
7
8 session: Ember.inject.service('session'),
9
10 headers: Ember.computed('session.authToken', function() {
11 return {
12 'API_KEY': this.get('session.authToken'),
13 'ANOTHER_HEADER': 'Some header value'
14 };
15 })
16 });
25. ➤ In Ember Data, serializers format the data sent to and
received from the backend store.
SERIALIZER(customization example)
27. COMPONEN
TS➤ A completed isolated view that has no access to the surrounding context
➤ A great way to build reusable components for your application
➤ routable-components will be the future
28. DATA DOWN!
ACTIONS UP!➤ DDAU - single data flow (hence unidirectional)
➤ Apps easier to reason about
➤ Improves performance
(GoodBye MVC)
Yes, this talk is about another Javascript framework but before you say Ohh I like Angular, React or other just remember
that each year trends changes so why not give Ember a try? If you see I intentionally left Ember out this slide image, maybe 2016 or 2017 may be the Ember year
Mention Yehudi Katz, Core Team Member at Ruby and JQuery. He brings best ideas of both world.
There is no corporation around this framework. Only a small community. I have found that very good developers
are part of this community. So maybe this talk is an invitation to join.
If a framework is opinionated, it lock or guides you into their way of doing things.
Convention over configuration (also known as coding by convention) is a software design paradigm used by software frameworks that attempt to decrease the number of decisions that a developer using the framework is required to make without necessarily losing flexibility. The concept was introduced by David Heinemeier Hansson to describe the philosophy of the Ruby on Rails web framework.
Not so sure about this affirmation, even small apps may get benefits from using a framework
Architecture means you make code that keep organized, you use stablished conventions and keep it testable and maintainable
Here, the dependent key todos.@each.isDone instructs Ember.js to update bindings and fire observers when any of the following events occurs:
The isDone property of any of the object of the array changes
An item is added/removed from the todos array
todos property changes to a different array
Sometimes you don't care if properties of individual array items change. In this case use the [] key instead of @each. Computed properties dependent on an array using the [] key will only update if items are added to or removed from the array, or if the array property is set to a different array.
Here, indexOfSelectedTodo depends on todos.[], so it will update if we add an item to todos, but won't update if the value of isDone on a todo changes
Because Ember supports promises, it can work with any persistence library that uses them as part of its public API. You can also use many of the conveniences built in to promises to make your code even nicer.
Multiple models can be returned through an Ember.RSVP.hash. The Ember.RSVP.hash takes parameters that return promises, and when all parameter promises resolve, then the Ember.RSVP.hash promise resolves
When accessing patients.edit route then Ember will search for:
patients.edit-loading
patients.loading or patients_loading
loading or application-loading
If the various beforeModel/model/afterModel hooks don't immediately resolve, a loading event will be fired on that route.
If the loading handler is not defined at the specific route, the event will continue to bubble above a transition's parent route, providing the application route the opportunity to manage it.
When using the loading handler, we can make use of the transition promise to know when the loading event is over:
Because everything in ember extends from Ember.Object we can have computed properties even in models
You can define your customs types like: UTC, defining the files inside transforms/utc.js
As far as I know, when async is false Ember will fetch the related entity at the same time as fetching the parent entity. When async is true, it will fetch the related entities when you actually request them.
but if the relationship was
stuff: DS.hasMany('stuff', {async: true})
then Ember Data is just expecting this:
{
user: { id: 1, stuff_id: [1, 2, 3] }
}
...and if you load the user and ask for user.get('stuff') then it will make a separate request for its related Stuff objects. So it may be more helpful to think of the async: value not as telling Ember Data how to fetch things, but as telling Ember Data how to expect things from the server.
You can define your customs types like: UTC, defining the files inside transforms/utc.js
DS.Adapter is the basic adapter with no functionality. It is generally a good starting point if you want to create an adapter that is radically different from the other Ember adapters.
DS.JSONAPIAdapter The JSONAPIAdapter is the default adapter and follows JSON API conventions to communicate with an HTTP server by transmitting JSON via XHR.
DS.RESTAdapter The RESTAdapter allows your store to communicate with an HTTP server by transmitting JSON via XHR. Before Ember Data 2.0 this adapter was the default.
By default the adapter will target the current domain. If you would like to specify a new domain you can do so by setting the host property on the adapter.
The namespace property can be used to prefix requests with a specific url namespace.
There are two ways to define a custom serializer. First, you can define a custom serializer for your entire application by defining an "application" serializer.
normalizeResponse(store, primaryModelClass, payload, id, requestType)
export default DS.JSONAPISerializer.extend({
primaryKey: '_id'
});
keyForAttribute: function(attr) {
return Ember.String.underscore(attr);
}
The JSONSerializer is a serializer that ships with Ember Data that can be used along side the RESTAdapter to serialize these simpler APIs.
It is no secret that many new Ember 2 conventions and changes have direct influence from React and Flux (a pattern for data flow in React apps). What Flux calls the “unidirectional data flow” is what we call “Data down, actions up” (DDAU) in Ember.
DDAU avoids the traditional client-side MVC pattern in favor of a single flow of data (hence unidirectional), which makes apps easier to reason about, and improves their performance. The fundamental problem with MVC is revealed as your application grows larger and more complex — cascading updates and implicit dependencies lead to a tangled mess and unpredictability. Updating one object leads to another changing, which in turn triggers more changes, and ultimately makes maintaining your application a frustrating experience.