Date post: | 08-May-2015 |
Category: |
Software |
Upload: | coldfusionconference |
View: | 386 times |
Download: | 0 times |
Building Ambitious Web Applications
Ryan Anklam
About Me
• Senior UI Engineer at Netflix • Friend of Open Source • @bittersweetryan
Why Ember?
Why Ember
Why Ember
Strong conventions
Why Ember
URL’s are first class citizens
Why Ember
Handlebars is the right abstraction
Why Ember
Ember data
Why Ember
Core Concepts
Core Concepts
Templates
Core Concepts
Routes
Core Concepts
Controllers
Core Concepts
Components
Core Concepts
Models
Let’s Build Something
Application & App.Router
Application & App.Router
App = Ember.Application.create();
Everything will be attached to this object.
Application & App.Router
App.Router.map(function() { ! this.resource( 'sessions', { path : 'sessions' }, function(){ this.route( 'add' ); } ); this.route( 'session', { path : 'session/:id'} ); ! this.resource( 'speakers', { path : 'speakers' }, function(){ this.route( 'speaker', { path : '/:name' } ); }); });
A resource can have nested routeshttp://<server>/#/sessionshttp://<server>/#/sessions/add
http://<server>/#/session/1
http://<server>/#/speakershttp://<server>/#/speakers/Ryan%20Anklam
Templates
Core Concepts
Handlebars + Ember ===
Powerful Templating
Templates
<script type="text/x-handlebars"> <header> <h1> Welcome to cf.Objective (2014)</h1> </header> <nav> <ul> <li class="nav-item">{{#link-to 'index'}}Home{{/link-to}}</li> <li class="nav-item">{{#link-to 'sessions'}}Sessions{{/link-to}}</li> <li class="nav-item">{{#link-to 'speakers'}}Speakers{{/link-to}}</li> </ul> </nav> <div class="content"> {{outlet}} </div> </script>
http://<server>/http://<server>/#/sessionshttp://<server>/#/speakers
Routes will output here
Templates
<script type="text/x-handlebars" data-template-name="index"> <div class="welcome>" <p class="welcome-message"> This is an app to show off some of what is cool in Ember.js </p> </div> </script>
Loads in {{outlet}} when index route is active
Templates
<script type="text/x-handlebars" data-template-name="loading"> <div class="loading">Loading...</div> </script>
Will load into {{outlet}} when a data promise is in flight
Templates
<script type="text/x-handlebars" data-template-name="error"> <div class="error"> There was an error loading this page. </div> </script>
Will load into {{outlet}} when a data promise is rejected
Templates
<script type="text/x-handlebars" data-template-name="session"> <h2 {{bind-attr class=goingClass}}> {{name}}{{#if going}} (Going){{/if}} </h2> <p>{{description}}</p> <button {{action 'going' this}} class=“button"> I'm {{#if going}}Not{{/if}} Going </button> </script>
Loads in {{outlet}} when session route is active
Routes
Routes
App.SessionRoute = Ember.Route.extend({ model : function( params ){ return this.store.find( 'session', params.session_id ); } } );
this.route( 'session', { path : 'session/:id'} );
Resources
App.SessionsIndexRoute = Ember.Route.extend({ model: function() { return this.store.find( 'session' ); } });
this.resource( 'sessions', { path : 'sessions' }, function(){ this.route( 'add' ); } );
Resources
App.SessionsAddRoute = Ember.Route.extend({ model : function(){ return { speakers : this.store.find( 'speaker' ), tracks : ['JS','Architecture'] }; } });
this.resource( 'sessions', { path : 'sessions' }, function(){ this.route( 'add' ); } );
Controllers
Session Template
GoingName
Session Model
Controllers
Session Controller
Controllers
App.SessionController = Ember.ObjectController.extend({ actions : { going : function( session ){ session.toggleProperty( 'going' ); ! } }, goingClass : function(){ return (this.get( 'going' ))? 'attending' : ''; }.property( 'going' ) });
this.route( 'session', { path : 'session/:id'} );<button {{action 'going' this}} class=“button">
<h2 {{bind-attr class=goingClass}}>
Controllers
App.SessionsIndexController = Ember.ArrayController.extend({ attending : function(){ return this.reduce( function( prev, curr ){ if( curr.get( 'going' ) ){ return prev + 1; } else{ return prev; } }, 0); }.property( '@each.going'), });
Tells the controller to update when any going property changes
this is the collection of objects returned by the route
Controllers
App.SessionsAddController = Ember.ObjectController.extend({ actions : { addSession : function(){ var session = this.store.createRecord( 'session', { track : this.get( 'track' ), name : this.get( 'name' ), description : this.get( 'description' ), speaker : this.get( 'speaker' ) } ); ! this.transitionToRoute('session',session); } } });
Redirect to the session route
Ember Data
Ember Data
• Swappable Adapters • Relationships • Fully embraces promises • Data caching • Fixtures speed up development
Store
App.ApplicationAdapter = DS.FixtureAdapter;App.ApplicationAdapter = DS.LocalStorageAdapter;App.ApplicationAdapter = DS.ParseAdapter;App.ApplicationAdapter = DS.FirebaseAdapter;
Store
App = Ember.Application.create(); !App.ApplicationAdapter = DS.FixtureAdapter.extend();
Store
App.Session = DS.Model.extend({ track : DS.attr( 'string' ), name : DS.attr( 'string' ), description: DS.attr( 'string' ), going: DS.attr( 'boolean', {defaultValue : false} ), speaker : DS.belongsTo('speaker') });
this.route( 'session', { path : 'session/:id'} );
Store
App.Speaker = DS.Model.extend({ firstName : DS.attr( 'string' ), lastName : DS.attr('string'), fullName : function(){ return this.get( 'firstName' ) + ' ' + this.get( 'lastName' ); }.property( 'firstName', 'lastName' ), bio : DS.attr( 'string' ), sessions: DS.hasMany('session', { async : true } ) });
Components
No, Seriously. Components.
Built with components
Component
<script type="text/x-handlebars" data-template-name="components/speaker-bio"> ! <article class="speaker-bio"> <h2 class="speaker-name">{{name}}</h2> <section class="speaker-description"> {{bio}} </section> {{#if sessions}} <h3>Sessions</h3> <ul> {{#each session in sessions}} <li>{{session.name}}</li> {{/each}} </ul> {{/if}} </article> </script>
Becomes component name
Properties are encapsulated
Component
<script type="text/x-handlebars" data-template-name="speakers"> <h1>Speakers</h1> {{#each item in model}} {{#with item}} {{speaker-bio name=fullName bio=bio sessions=sessions}} {{/with}} {{/each}} </script>
Add the component to a templatePass properties into component
Component
App.SpeakerBioComponent = Ember.Component.extend({ actions: { toggleBody: function() { this.toggleProperty('isShowingBody'); } } });
Getting Help
Why Ember
Ember Inspector
Why Ember
http://discuss.emberjs.com/ IRC: freenode.net #emberjs
Looking Ahead
Looking Ahead
Ember CLI(currently in beta)
Why Ember
HTMLBars(currently in alpha)
Why Ember
Ember-Data 1.0(currently in beta)