Emberjs building-ambitious-web-applications

54
Building Ambitious Web Applications Ryan Anklam

Transcript of Emberjs building-ambitious-web-applications

Page 1: Emberjs building-ambitious-web-applications

Building Ambitious Web Applications

Ryan Anklam

Page 2: Emberjs building-ambitious-web-applications

About Me

• Senior UI Engineer at Netflix • Friend of Open Source • @bittersweetryan

Page 3: Emberjs building-ambitious-web-applications

Why Ember?

Page 4: Emberjs building-ambitious-web-applications

Why Ember

Page 5: Emberjs building-ambitious-web-applications

Why Ember

Strong conventions

Page 6: Emberjs building-ambitious-web-applications

Why Ember

URL’s are first class citizens

Page 7: Emberjs building-ambitious-web-applications

Why Ember

Handlebars is the right abstraction

Page 8: Emberjs building-ambitious-web-applications

Why Ember

Ember data

Page 9: Emberjs building-ambitious-web-applications

Why Ember

Page 10: Emberjs building-ambitious-web-applications

Core Concepts

Page 11: Emberjs building-ambitious-web-applications

Core Concepts

Templates

Page 12: Emberjs building-ambitious-web-applications

Core Concepts

Routes

Page 13: Emberjs building-ambitious-web-applications

Core Concepts

Controllers

Page 14: Emberjs building-ambitious-web-applications

Core Concepts

Components

Page 15: Emberjs building-ambitious-web-applications

Core Concepts

Models

Page 16: Emberjs building-ambitious-web-applications

Let’s Build Something

Page 17: Emberjs building-ambitious-web-applications

Application & App.Router

Page 18: Emberjs building-ambitious-web-applications

Application & App.Router

App = Ember.Application.create();

Everything will be attached to this object.

Page 19: Emberjs building-ambitious-web-applications

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

Page 20: Emberjs building-ambitious-web-applications

Templates

Page 21: Emberjs building-ambitious-web-applications

Core Concepts

Handlebars + Ember ===

Powerful Templating

Page 22: Emberjs building-ambitious-web-applications

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

Page 23: Emberjs building-ambitious-web-applications

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

Page 24: Emberjs building-ambitious-web-applications

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

Page 25: Emberjs building-ambitious-web-applications

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

Page 26: Emberjs building-ambitious-web-applications

Templates

<script type="text/x-handlebars" data-template-name="session"> <h2 {{bind-attr class=goingClass}}> {{name}}{{#if going}}&nbsp;(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

Page 27: Emberjs building-ambitious-web-applications

Routes

Page 28: Emberjs building-ambitious-web-applications

Routes

App.SessionRoute = Ember.Route.extend({ model : function( params ){ return this.store.find( 'session', params.session_id ); } } );

this.route( 'session', { path : 'session/:id'} );

Page 29: Emberjs building-ambitious-web-applications

Resources

App.SessionsIndexRoute = Ember.Route.extend({ model: function() { return this.store.find( 'session' ); } });

this.resource( 'sessions', { path : 'sessions' }, function(){ this.route( 'add' ); } );

Page 30: Emberjs building-ambitious-web-applications

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' ); } );

Page 31: Emberjs building-ambitious-web-applications

Controllers

Page 32: Emberjs building-ambitious-web-applications

Session Template

GoingName

Session Model

Controllers

Session Controller

Page 33: Emberjs building-ambitious-web-applications

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}}>

Page 34: Emberjs building-ambitious-web-applications

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

Page 35: Emberjs building-ambitious-web-applications

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

Page 36: Emberjs building-ambitious-web-applications

Ember Data

Page 37: Emberjs building-ambitious-web-applications

Ember Data

• Swappable Adapters • Relationships • Fully embraces promises • Data caching • Fixtures speed up development

Page 38: Emberjs building-ambitious-web-applications

Store

App.ApplicationAdapter = DS.FixtureAdapter;App.ApplicationAdapter = DS.LocalStorageAdapter;App.ApplicationAdapter = DS.ParseAdapter;App.ApplicationAdapter = DS.FirebaseAdapter;

Page 39: Emberjs building-ambitious-web-applications

Store

App = Ember.Application.create(); !App.ApplicationAdapter = DS.FixtureAdapter.extend();

Page 40: Emberjs building-ambitious-web-applications

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'} );

Page 41: Emberjs building-ambitious-web-applications

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 } ) });

Page 42: Emberjs building-ambitious-web-applications

Components

Page 43: Emberjs building-ambitious-web-applications

No, Seriously. Components.

Page 44: Emberjs building-ambitious-web-applications

Built with components

Page 45: Emberjs building-ambitious-web-applications

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

Page 46: Emberjs building-ambitious-web-applications

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

Page 47: Emberjs building-ambitious-web-applications

Component

App.SpeakerBioComponent = Ember.Component.extend({ actions: { toggleBody: function() { this.toggleProperty('isShowingBody'); } } });

Page 48: Emberjs building-ambitious-web-applications

Getting Help

Page 49: Emberjs building-ambitious-web-applications

Why Ember

Ember Inspector

Page 50: Emberjs building-ambitious-web-applications

Why Ember

http://discuss.emberjs.com/ IRC: freenode.net #emberjs

Page 51: Emberjs building-ambitious-web-applications

Looking Ahead

Page 52: Emberjs building-ambitious-web-applications

Looking Ahead

Ember CLI(currently in beta)

Page 53: Emberjs building-ambitious-web-applications

Why Ember

HTMLBars(currently in alpha)

Page 54: Emberjs building-ambitious-web-applications

Why Ember

Ember-Data 1.0(currently in beta)