codeschool-emberjs

159
Setting up Ember.js Level 1 - Warming Up

Upload: corina

Post on 12-Feb-2016

12 views

Category:

Documents


0 download

DESCRIPTION

material from CodeSchool

TRANSCRIPT

Page 1: CodeSchool-Emberjs

Setting up Ember.js

Level 1 - Warming Up

Page 2: CodeSchool-Emberjs

Ember.js“A framework for creating ambitious web applications”

Page 3: CodeSchool-Emberjs

Need To KnowJavaScriptHTML & CSS jQuery

Ruby & Rails Python & Django

Don’t need to know

Page 4: CodeSchool-Emberjs

Why Ember.js

Page 5: CodeSchool-Emberjs

Why Ember.js?

HTML

Web App

iOS Android

API

Database

Ember.js

Page 6: CodeSchool-Emberjs

Why Ember.jsYou want your application to feel responsive

Page 7: CodeSchool-Emberjs

When Not to Use

• Blog

• Newspaper

• Static Content

When your web app doesn’t have a lot of interactivity.

Page 8: CodeSchool-Emberjs

Getting Started With Ember.js

http://emberjs.com/

1. Grab the starter kit & Ember Data

https://github.com/emberjs/data

http://getbootstrap.com/

2. We’re using Twitter Bootstrap

Page 9: CodeSchool-Emberjs

Setting Up index.htmlindex.html<html> <head> <script src="jquery.js"></script> <script src="handlebars.js"></script> <script src="ember.js"></script> <script src="ember-data.js"></script> <script src="app.js"></script> ! <link href="bootstrap.css" media="screen" rel="stylesheet" /> </head> </html>

Page 10: CodeSchool-Emberjs

Test Drive

Page 11: CodeSchool-Emberjs

The Ember Application

app.js

var App = Ember.Application.create({ });

We’re namespacing our application App, but you could name this variable anything

We need a single object to contain all functionality of our app.

Page 12: CodeSchool-Emberjs

Ember Application

When you see this icon, it’s our application

The roots of our application Need to create an application only once Everything we do comes after

Page 13: CodeSchool-Emberjs

Ember Application Options

var App = Ember.Application.create({ LOG_TRANSITIONS: true });

Not required, but helpful for debugging!

Inside our application we can send in a JavaScript Object with options.

For example, if we wanted to log out a message to the browser every time a new page is accessed.

Page 14: CodeSchool-Emberjs

The HTML Contentapp.js

var App = Ember.Application.create({ LOG_TRANSITIONS: true });

<html> ... <body> <div class='navbar'>...</div> <div class='container'>...</div> <footer class='container'>...</footer> </body> </html>

index.html

Page 15: CodeSchool-Emberjs

Running Our First AppIf we open the app and view the source you’ll notice Ember added a body class and data-ember-extension to our code.

<!DOCTYPE html> <html> <head>...</head> <body class='ember-application' data-ember-extension='1'> <div class='navbar'>...</div> <div class='container'>...</div> <footer class='container'>...</footer> </body> </html>

So Ember will know the part of the page it will control.

Page 16: CodeSchool-Emberjs

We Need to Dynamically Update

We want to move everything inside <body> into a template

We’re going to want to dynamically update the content inside this body. Thus, we need a templating language.

body

div.navbar

div.container

footer.container

<div class='navbar'>...</div> <div class='container'>...</div> <footer class='container'>...</footer>

<body>

</body>

Page 17: CodeSchool-Emberjs

div.navbar

div.container

footer.container

bodytemplate

Handlebars Template

This is a Handlebars template

Handlebars templates look like regular HTML with handlebars expressions

<body><script type='text/x-handlebars'>

<div class='navbar'>...</div> <div class='container'>...</div> <footer class='container'>...</footer> </script></body>

Page 18: CodeSchool-Emberjs

Running With HandlebarsEmber by default will render the Handlebars template into a div.

<!DOCTYPE html> <html> <head>...</head> <body class='ember-application' data-ember-extension='1'> <div id='ember248' class='ember-view'> <div class='navbar'>...</div> <div class='container'>...</div> <footer class='container'>...</footer> </div> </body> </html>

So Ember can uniquely identify this div

Page 19: CodeSchool-Emberjs

Ember Template

Works just like HTML Ember uses the Handlebars.js for templates The part of our application people see

When you see this icon, it’s an Ember template

Page 20: CodeSchool-Emberjs

Named Templates

Level 1 - Warming Up

Page 21: CodeSchool-Emberjs

Reviewindex.html

We can put HTML inside a Handlebars template

What if we wanted to make the content in this template dynamic?

<body> <script type='text/x-handlebars'>

</script></body>

<div class='navbar'>...</div>

<footer class='container'>...</footer> <div class='container'> </div>...

Page 22: CodeSchool-Emberjs

Adding Data to Templatesindex.html<script type=‘text/handlebars’ data-template-name=‘application’> <div>...</div></script>

Called a “handlebars expression”

{{siteName}}’s value will need to be provided by our Ember app, so it can print The Flint & Flame!

Later on in this course we’ll be using templates to add dynamic content.

<script type='text/x-handlebars'> <div class='navbar'>...</div> <div class='container'>

<footer class='container'>...</footer>

<h1>Welcome to {{siteName}}!</h1> </div>

</script>

Page 23: CodeSchool-Emberjs

Our Current Webpage Layout

div.navbar

div.container

footer.container

bodytemplate

We are going to have multiple pages on our website.

• Homepage

• About Page

• Product List

Page 24: CodeSchool-Emberjs

div.navbar

div.container

footer.container

bodytemplate

<h1>About The Fire Spirits</h1>

<h1>Welcome to The Flint & Flame!</h1>

The Different Pages

About Page

Home Page

We want this to be the default

Page 25: CodeSchool-Emberjs

div.navbar

div.container

footer.container

body

<h1>About The Fire Spirits</h1>

<h1>Welcome to The Flint & Flame!</h1>

We Need to Name our Templates

We want this to be the default

‘application’ template‘about’ template

‘index’ template

Page 26: CodeSchool-Emberjs

Adding the Template Name

index.html

<script type=‘text/handlebars’ data-template-name=‘application’> <div>...</div></script>

The application template is shown for every page by default.

Each template needs a unique name.

<script type='text/x-handlebars' <div class='navbar'>...</div> <div class='container'>...</div> <footer class='container'>...</footer></script>

data-template-name='application'>

Page 27: CodeSchool-Emberjs

Adding the Homepage Templateindex.html

‘index’ is what we’ll call the homepage template name

<script type='text/x-handlebars' <div class='navbar'>...</div> <div class='container'>...</div> <footer class='container'>...</footer></script>

data-template-name='application'>

<script type='text/x-handlebars' data-template-name='index'> <h1>Welcome to The Flint & Flame!</h1> </script>

Page 28: CodeSchool-Emberjs

Adding our About Templateindex.html

<script type='text/x-handlebars' <div class='navbar'>...</div> <div class='container'>...</div> <footer class='container'>...</footer></script>

data-template-name='application'>

<script type='text/x-handlebars' data-template-name='index'> <h1>Welcome to The Flint & Flame!</h1> </script>

<script type='text/x-handlebars' data-template-name='about'> <h1>About The Fire Spirits</h1> </script>

Page 29: CodeSchool-Emberjs

Only our application template is showing!

No welcome message!

Page 30: CodeSchool-Emberjs

But where do they go?We need a way of telling our templates where on the page to render.

?<script type='text/x-handlebars' data-template-name='application'> <div class='navbar'>...</div><div class='container'> </div>

<footer class='container'>...</footer> </script> !<script type='text/x-handlebars' data-template-name='index'> <h1>Welcome to The Flint & Flame!</h1> </script> !<script type='text/x-handlebars' data-template-name='about'> <h1>About The Fire Spirits</h1> </script>

...

Page 31: CodeSchool-Emberjs

OutletUsing the handlebars expression “outlet” we’re giving our code a hint as to where templates should be inserted.

If our Ember code reaches an {{outlet}}, by default it will look to find a template named ‘index’ and render that in place of the outlet.

<script type='text/x-handlebars' data-template-name='application'> <div class='navbar'>...</div><div class='container'>{{outlet}}</div>

<footer class='container'>...</footer> </script> !<script type='text/x-handlebars' data-template-name='index'> <h1>Welcome to The Flint & Flame!</h1> </script> !<script type='text/x-handlebars' data-template-name='about'> <h1>About The Fire Spirits</h1> </script>

Page 32: CodeSchool-Emberjs

Our index template is rendered within our application template

Page 33: CodeSchool-Emberjs

The Router

Level 1 - Warming Up

Page 34: CodeSchool-Emberjs

Routing Our Way Inindex.html<script type='text/x-handlebars' data-template-name='application'> <div class='navbar'>...</div> <div class='container'> {{outlet}} </div> <footer class='container'>...</footer> </script> !<script type='text/x-handlebars' data-template-name='about'> <h1>About The Fire Spirits</h1> </script>

How can we ever render the ‘about’ template?and map it to a URL, like http://example.com/about ?

Page 35: CodeSchool-Emberjs

The Ember Router

Translates a path into a route. Like a tree trunk — all requests start here.

This is our Router icon

Page 36: CodeSchool-Emberjs

Routing Our Way In

Browser Request

Figures out the route name for each request

Ember Router

?

??

??

?

Handlebars Template

Page 37: CodeSchool-Emberjs

Introducing the Routerapp.js

Every page for our website will be defined by the router.

var App = Ember.Application.create({ LOG_TRANSITIONS: true });

App.Router.map(function() {

});…

Page 38: CodeSchool-Emberjs

How can we render the ‘about’ template?

App.Router.map(function() {

});…

app.js

<script type='text/x-handlebars' data-template-name='application'> … {{outlet}} … </script> !<script type='text/x-handlebars' data-template-name='about'> <h1>About The Fire Spirits</h1> </script>

index.html

Page 39: CodeSchool-Emberjs

The about Route

App.Router.map(function() {

});

app.js

this.route('about');

http://example.com#/about

/about

When an outlet is found, ‘about’ template is loaded into it.

about

URL

Path

Route

<script type='text/x-handlebars' data-template-name='application'> … {{outlet}} … </script> !<script type='text/x-handlebars' data-template-name='about'> <h1>About The Fire Spirits</h1> </script>

Page 40: CodeSchool-Emberjs

Template Naming

Page 41: CodeSchool-Emberjs

What’s with the Hash Sign?

http://example.com#/about http://example.com/about

There is no ‘about/index.html’ or ‘about’ file path our application knows about

Page 42: CodeSchool-Emberjs

Where our Application ExistsIn Ember.js, our entire application is loaded through one file.

• Homepage • About Page • Product List

In our case index.html which is loaded from http://example.com

No matter what page we load…

…we will have first called up index.html

This loads up all our templates!

Page 43: CodeSchool-Emberjs

All Templates Sent!In Ember.js, all our templates are sent over when the application loads.

Everything Sent

application

index

about

product list

Server Client Browser

application

index

about

product list

Initial Requestapplication

about

Page 44: CodeSchool-Emberjs

What does the hash say?http://example.com#/about

When a browser see’s this it knows the file path information is over

Loads ‘’ or root file path

http://example.com/about Loads ‘about’ file path

http://example.com/store#/shopping_cart Loads ‘store’ file path

Page 45: CodeSchool-Emberjs

Back to Our About Route

App.Router.map(function() {

});

app.js http://example.com#/about

/about

about

URL

Ember Path

Route

What if we want the ‘/aboutus’ path to use the about route?

this.route('about'); File Path <none>

Page 46: CodeSchool-Emberjs

Using ‘/aboutus’ custom path

App.Router.map(function() {

});

app.js

http://example.com#/aboutus

/aboutus

about

this.route('about' );, { path: '/aboutus' }

URL

Ember Path

Route

Page 47: CodeSchool-Emberjs

What about the Index Route?

App.Router.map(function() {

});

app.js

http://example.com

index

this.route('about' );, { path: '/aboutus' }

<none>

this.route('index', { path: '' });

?

?

URL

Ember Path

Route

Page 48: CodeSchool-Emberjs

No! This is a Default

App.Router.map(function() {

});

app.js

this.route('about' );, { path: '/aboutus' }

this.route('index', { path: '' });

Default Behavior!

http://example.com

index

<none>

URL

Ember Path

Route

Page 49: CodeSchool-Emberjs
Page 50: CodeSchool-Emberjs

Handlebars

Level 2 - Rendering the Flame

Page 51: CodeSchool-Emberjs

Review

App.Router.map(function() { this.route('about'); });

app.js

Router is solely responsible for all URLs.

How do we link to the about and index route?

Page 52: CodeSchool-Emberjs

Linking to Routes

index.html

App.Router.map(function() { this.route('about'); });

<a href="#/about">About</a>

app.js

Router is solely responsible for all URLs.

It’s considered a bad practice to do this. We don’t hard code URL paths. Instead we ask the routes for their path.

<a href="#/">Home</a>

Page 53: CodeSchool-Emberjs

The Handlebars link-to helperUse the route name to look up the path.

{{#link-to 'about'}}About{{/link-to}}

<a class="ember-view" href="#/" id='ember2'>Homepage</a><a class="ember-view" href="#/about" id='ember3'>About</a>

link-to will determine the path based on the route name.

{{#link-to 'index'}}Homepage{{/link-to}}

Page 54: CodeSchool-Emberjs

Extra AttributesHow to add a custom class to our link.

<a class="ember-view navbar-brand" href="#/" id='ember2'>Homepage</a>

Rendered from template into

{{#link-to 'index' }}Homepage{{/link-to}} class='navbar-brand'

Page 55: CodeSchool-Emberjs
Page 56: CodeSchool-Emberjs
Page 57: CodeSchool-Emberjs

tagName option

index.html{{#link-to 'index' tagName='li'}}Home{{/link-to}} {{#link-to 'about' tagName='li'}}About{{/link-to}}

Notice the href disappeared!

<li class='ember-view' id='ember1'>Home</li> <li class='ember-view' id='ember2'>About</li>

How to change the HTML element.

It will still work, because Ember adds click handlers.

Page 58: CodeSchool-Emberjs

TitleBody Level One

Demo showing li links still working

Page 59: CodeSchool-Emberjs

Controller Basics

Level 2 - Rendering the Flame

Page 60: CodeSchool-Emberjs

Review

index.html

{{#link-to 'about' tagName='li'}}About{{/link-to}}

App.Router.map(function() { this.route('about'); });

app.js

Mapped paths to route names.

Link to other pages using route names, not paths.

Page 61: CodeSchool-Emberjs

Template Variables

index.htmlYou can use properties in your template

<script type='text/x-handlebars' data-template-name='index'> <p>There are {{productsCount}} products</p> </script>

How do you set a property for use in a template?

Page 62: CodeSchool-Emberjs

Ember Controller

Where a template looks to find a property value

Decorate your applications data for the template

Contains information that is not saved to a server

Each Ember Controller will use this icon

Page 63: CodeSchool-Emberjs

Routing Our Way InBrowser Request

Router

?

??

?? Handlebars Template

ControllerProvides properties for the template

Page 64: CodeSchool-Emberjs

Asking the Controller

app.jslooks for a controller named “IndexController”, and the property inside it.

index.html<script type='text/x-handlebars' data-template-name='index'> <p>There are {{productsCount}} products</p> </script>

There are 6 products

App.IndexController = Ember.Controller.extend({

});productsCount: 6

Page 65: CodeSchool-Emberjs

Every Route has a Default Controller

If we want to declare properties that get rendered inside a template, then we can declare this Controller.

App.Router.map(function() { this.route('about'); });

App.AboutController = Ember.Controller.extend({ });

Behind the scenes, Ember creates this About controller.

Page 66: CodeSchool-Emberjs

Binding with Metamorph!index.html

<p>There are <script id="metamorph-2-start" type="text/x-placeholder"></script> 6 <script id="metamorph-2-end" type="text/x-placeholder"></script> products </p>

Ember needs to wrap properties so it can potentially update it later. (AKA. Binding)

<script type='text/x-handlebars' data-template-name='index'> <p>There are {{productsCount}} products</p></script>

Page 67: CodeSchool-Emberjs

Binding Attributes?

index.html

app.js

App.IndexController = Ember.Controller.extend({ productsCount: 6,logo: '/images/logo.png'

});

<script type='text/x-handlebars' data-template-name='index'> <p>There are {{productsCount}} products</p>

</script> <img src='{{logo}}' alt='Logo' />

Page 68: CodeSchool-Emberjs
Page 69: CodeSchool-Emberjs

Property Bindingindex.html

<img src=" <script id="metamorph-2-start" type="text/x-placeholder"></script> /images/logo.png <script id="metamorph-2-end" type="text/x-placeholder"></script> >

We need to bind attributes differently.

<script type='text/x-handlebars' data-template-name='index'> <p>There are {{productsCount}} products</p> <img src='{{logo}}' alt='Logo' /></script>

Page 70: CodeSchool-Emberjs

How to Bind Attributesindex.html

<img src="/images/logo.png" data-bindattr-1="1">

Ember meta data is still added to enable binding

<script type='text/x-handlebars' data-template-name='index'> <p>There are {{productsCount}} products</p>

</script> <img {{bind-attr src='logo'}} alt='Logo' />

Page 71: CodeSchool-Emberjs
Page 72: CodeSchool-Emberjs

Dynamic Contentindex.html<script type='text/x-handlebars' data-template-name='index'> <p>Rendered on {{time}}</p> </script>

How do we fetch the current time?

(new Date()).toDateString()

We need to create a property that calls this function:

This is more then just a value, we need to execute some code to get the value.

Page 73: CodeSchool-Emberjs

Controller Functions

app.jsindex.html<script type='text/x-handlebars' data-template-name='index'> <p>Rendered on {{time}}</p> </script>

app.js

We need to tell Ember this is a property and to call it

(new Date()).toDateString()

App.IndexController = Ember.Controller.extend({ productsCount: 6, logo: '/images/logo.png', time: function() {

return

}

}});

Page 74: CodeSchool-Emberjs

Controller Functions as Properties

app.js<script type='text/x-handlebars' data-template-name='index'> <p>Rendered on {{time}}</p> </script>

time will be called, and that value used as a property

index.html

app.js

(new Date()).toDateString()

App.IndexController = Ember.Controller.extend({ productsCount: 6, logo: '/images/logo.png', time: function() {

return}

});.property()

Page 75: CodeSchool-Emberjs

TitleScreenshot with the time showing

Page 76: CodeSchool-Emberjs

Resource Routes

Level 3 - A Route Through the Woods

Page 77: CodeSchool-Emberjs

Reviewapp.js

The name of the controller matches the route and template names

index.html

Templates can use properties from a controller

App.IndexController = Ember.Controller.extend({ productsCount: 6, logo: '/images/test.png', time: function() { return (new Date()).toDateString(); }.property() });

<script type='text/handlebars' data-template-name='index'> <p>There are {{productsCount}} products</p> <img {{bind-attr src='logo'}} alt='Logo' /> <p>Current Time: {{time}}</p> </script>

Page 78: CodeSchool-Emberjs

We’ll show all of our products on a new products page

Page 79: CodeSchool-Emberjs

Use route for adjectives, verbs, adverbsapp.js

this.route('signup'); this.route('ignite'); this.route('help'); this.route('rate');

this.route('onsale'); this.route('expiring'); this.route('new'); this.route('internal');

Adjective Routes Verb Routes

App.Router.map(function() { this.route('about');});

Page 80: CodeSchool-Emberjs

Use ‘Resource’ Routes for Nounsapp.js

this.resource('tree'); this.resource('review'); this.resource('books'); this.resource('contacts');

Noun Resources

Can be singular or plural

The resource keyword has some additional functionality.That difference is what this level is all about!

App.Router.map(function() { this.route('about');

}); this.resource('products');

Page 81: CodeSchool-Emberjs

Resource Route Options

app.js

Like with route, we can pass in an optional path

App.Router.map(function() { this.route('about');

});, { path: '/items' } this.resource('products' );

Page 82: CodeSchool-Emberjs

Resource Viewsapp.js

index.htmlMatches the route name

<script type='text/x-handlebars' data-template-name='products'> <h1>Products</h1> </script>

App.Router.map(function() { this.route('about');

}); this.resource('products');

Page 83: CodeSchool-Emberjs

Linking to Resources

index.html

<script type='text/x-handlebars' data-template-name='application'> ... {{#link-to 'products' tagName='li'}}Products{{/link-to}} ... </script>

We can link to this route with its name like any other.

Page 84: CodeSchool-Emberjs

How can we add some data to this page?

The link has a class of active if we’re within the products routeRendering the products template

http://example.com#/products

Page 85: CodeSchool-Emberjs

Ember Route

Fetches data and passes it to the Ember Controller

Decides on what model to use for the current route

Decides which template to render to the screen

Each Ember Route will use this iconA model could be a JavaScript Object or an Array of objects

Page 86: CodeSchool-Emberjs

Ember RouteProvides data for the controller

Ember RouterTranslates a path into a route

Don’t get these confused

Page 87: CodeSchool-Emberjs

Ember RouteBrowser Request

Router

??

?? Handlebars Template

Controller

Route

Provides data for the controller

Page 88: CodeSchool-Emberjs

Router? Route? Controller?

created by Ember if not defined

Decorates the model, provides property values

App.ProductsController = Ember.Controller.extend({ });

this.resource('products');

Used to define routes our application accepts.

Responsible for getting data from external resources

created by Ember if not defined

Router

Route

Controller

App.ProductsRoute = Ember.Route.extend({ });

Page 89: CodeSchool-Emberjs

A Very Basic Modelapp.js

App.PRODUCTS = [ { title: 'Flint', price: 99, description: 'Flint is…', isOnSale: true, image: 'flint.png' }, { title: 'Kindling', price: 249, description: 'Easily…', isOnSale: false, image: 'kindling.png' } ];

app.js

This could be pulled from an API

The model property should return an an object or an array.

App.ProductsRoute = Ember.Route.extend({model: function() { return App.PRODUCTS; }

});

Page 90: CodeSchool-Emberjs

Putting it All Together

Model

The Route is responsible for fetching the model. Ember will set this model on the Controller, and that’s what’ll be used in your Handlebars Template.

index.html

Router

Route

Controller

Template

<script type='text/x-handlebars' data-template-name='products'> <h2>Products</h2> {{model}}</script>

Page 91: CodeSchool-Emberjs

Rendering the Modelindex.html

<h2>Products</h2> [object Object],[object Object]

Model is an array, so we’ll need to loop over it!

<script type='text/x-handlebars' data-template-name='products'> <h2>Products</h2> {{model}}</script>

Page 92: CodeSchool-Emberjs

Looping Over the Array of Objectsindex.html

<h1>Products</h1> <h2>Flint</h2> <h2>Kindling</h2>

<script type='text/x-handlebars' data-template-name='products'>

</script>

<h2>Products</h2>{{#each product in model}}

<h2>{{product.title}}</h2>{{/each}}

Page 93: CodeSchool-Emberjs

A Simpler #each Loop

index.html

Within the #each loop, you can access properties on that product

#each without extra options will look for a model property.

<script type='text/x-handlebars' data-template-name='products'> ...

</script>

{{#each}}<h2>{{title}}</h2>

{{/each}}

Page 94: CodeSchool-Emberjs

A Dash of Styleindex.html

Output a few more properties and add some styling

<script type='text/x-handlebars' data-template-name='products'><h1>Products</h1><ul class='list-unstyled col-md-8'> {{#each}} <li class='row'> <img {{bind-attr src='image'}} class='img-thumbnail col-md-5'/> <div class='col-md-7'> <h2>{{title}}</h2> <p class='product-description'>{{description}}</p> <p><button class='btn btn-success'>Buy for ${{price}}</button></p> </div> </li> {{/each}} </ul></script>

Page 95: CodeSchool-Emberjs
Page 96: CodeSchool-Emberjs

Related and Dynamic Routes

Level 3 - A Route Through the Woods

Page 97: CodeSchool-Emberjs

Review

app.jsAdded a resource route

Create a Route to provide the model

How would we create a page for a specific product? Based on title?

{ title: 'Flint', price: 99, description: 'Flint is…', isOnSale: true, image: 'flint.png' }

App.ProductsRoute = Ember.Route.extend({ model: function() { return App.PRODUCTS; } });

});

App.Router.map(function() { this.route('about'); this.resource('products');

Page 98: CodeSchool-Emberjs

Adding a Singular Routeapp.js

Nothing unique to a specific product in path

We need a path with a parameter. Maybe #/products/<product_title>

App.Router.map(function() { this.route('about'); this.resource('products');

});this.resource('product');

Page 99: CodeSchool-Emberjs

Dynamic Segment Routesapp.js

Our route object can use the title

{ title: 'Flint' }

{ title: '4' }

http://example.org#/products/Flint

http://example.org#/products/4

App.Router.map(function() { this.route('about'); this.resource('products');

});, { path: '/products/:title' }this.resource('product' );

Page 100: CodeSchool-Emberjs

Logging out Params from the Routeapp.js

{ title: 'Flint' }http://example.org#/products/Flint

App.Router.map(function() { this.route('about'); this.resource('products');

});

App.ProductRoute = Ember.Route.extend({ model: function(params) { ! } });

console.log(params);

, { path: '/products/:title' }this.resource('product' );

Page 101: CodeSchool-Emberjs

Returning a Single Object

app.js

We need to lookup a PRODUCT with title of params.title

App.PRODUCTS = [

, { title: 'Kindling', price: 249, description: 'Easily…', isOnSale: false, image: 'kindling.png' } ];

App.ProductRoute = Ember.Route.extend({ model: function(params) { ! } });

{ title: 'Flint', price: 99, description: 'Flint is…', isOnSale: true, image: 'flint.png' }

Page 102: CodeSchool-Emberjs

{ title: 'Flint', price: 99, description: 'Flint is…', isOnSale: true, image: 'flint.png' }

findBy Array HelperEmber adds helpers to JavaScript base objects, in this case to Array.

http://example.org#/products/Flint

return App.PRODUCTS.findBy('title', params.title);

App.ProductRoute = Ember.Route.extend({ model: function(params) { ! } });

Page 103: CodeSchool-Emberjs

{ title: 'Flint', price: 99, description: 'Flint is…', isOnSale: true, image: 'flint.png' }

Our Product Templateindex.html<script type='text/x-handlebars' data-template-name='product'> <h2>{{title}}</h2> <p>{{description}}</p> <p>Buy for ${{price}}</p> </script>

First looks in the controller for properties

Second looks in the model for properties

Model

Controller

Page 104: CodeSchool-Emberjs

Flint in the path is a dynamic segment

Our product template is rendered

Page 105: CodeSchool-Emberjs

Styling Our Productindex.html

Our more detailed view of a product

<script type='text/x-handlebars' data-template-name='product'> <div class='row'> <div class='col-md-7'> <h2>{{title}}</h2> <h3 class='text-success'>${{price}}</h3> <p class='text-muted'>{{description}}</p> </div>

<div class='col-md-5'> <img {{bind-attr src='image'}} class='img-thumbnail img-rounded'/> </div>

</div> </script>

Page 106: CodeSchool-Emberjs

Linking To a Productindex.html<script type='text/x-handlebars' data-template-name='products'> {{each}} {{#link-to 'product' this classNames='list-group-item'}} {{title}} {{/link-to}} {{/each}} </script> Pass in the product we want to link to

/products/Flint{{#link-to 'product' this}}

The link-to helper knows to use the this.title value in the URL since we defined our route with :title in the Router.

this.resource(‘product', { path: '/products/:title' });

Thus….

Page 107: CodeSchool-Emberjs
Page 108: CodeSchool-Emberjs

Nested Routes

Level 3 - A Route Through the Woods

Page 109: CodeSchool-Emberjs

Reviewapp.js

App.Router.map(function() { this.route('about'); this.resource('products'); this.resource('product', { path: '/products/:title' }); }); !App.ProductRoute = Ember.Route.extend({ model: function(params) { return App.PRODUCTS.findBy('title', params.title); } });

Resource route with a dynamic segment

Provide the singular product for the product route

Page 110: CodeSchool-Emberjs

Show screenshot or products page

Show screenshot or product page

We replaced the entire {{outlet}} !in our application with this content

products template rendering within {{outlet}} in the application template

Page 111: CodeSchool-Emberjs

product template also rendering within {{outlet}} in the application template

Page 112: CodeSchool-Emberjs

Our Existing Templates

products template

product template

A list of products

Information on a specific product

application template

div.container{{outlet}}

They both are rendered in the same {{outlet}}, but what if we wanted to do something different?

Page 113: CodeSchool-Emberjs

Body Level One

Since we don’t have many products, it !makes since to user a master-detail view

Screencast of finished product with products staying on the side

Page 114: CodeSchool-Emberjs

Template in a Template

application template

div.container{{outlet}}

products template

product template

How do we nest the product template inside the products template?

Page 115: CodeSchool-Emberjs

App.Router.map(function() { this.route('about'); this.resource('products', function() { this.resource('product', { path: '/:title' }); });});

If your Views are nested your Routes should be nestedapp.js

Notice we don’t need /products anymore.

App.Router.map(function() { this.route('about'); this.resource('products'); this.resource('product', { path: '/products/:title' }); });

Nest the resources

Old Router

Page 116: CodeSchool-Emberjs

http://example.com#/products/Flint

Following the Path

products/:titleEmber Path

Route product

URL

App.Router.map(function() { this.route('about'); this.resource('products', function() { this.resource('product', { path: '/:title' }); });});

Page 117: CodeSchool-Emberjs

Adding an Outlet

application template

div.container{{outlet}}

products template

product template

{{outlet}}

We need an outlet to show where the product template will be rendered inside the products template.

Page 118: CodeSchool-Emberjs

Adding the Outlet to our Templateindex.html

<script type='text/x-handlebars' data-template-name='products'> <div class='row'> <div class='col-sm-3'> <div class='list-group'> {{#each}} {{#link-to 'product' this classNames='list-group-item'}} {{title}} {{/link-to}} {{/each}} </div> </div><div class='col-sm-9'> {{outlet}} </div>

</div> </script>

Page 119: CodeSchool-Emberjs

TitleBody Level One

/products page works

Clicking on a product works!

Page 120: CodeSchool-Emberjs

https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi

Ember Inspector For Chrome

Page 121: CodeSchool-Emberjs
Page 122: CodeSchool-Emberjs

The products/index template will be shown within the products template with no product.

Page 123: CodeSchool-Emberjs

Template for products/index Route

index.html<script type='text/x-handlebars' data-template-name='products/index'> <p class='text-muted'>Choose a product from those on the left!</p> </script>

Will be filled into the {{outlet}} of the products template

Page 124: CodeSchool-Emberjs
Page 125: CodeSchool-Emberjs

Level 4 - Acorn Models and Pinecone Data

Models and Ember Data

Page 126: CodeSchool-Emberjs

app.js

ReviewWhat if we wanted to fetch products from a server?

App.PRODUCTS = [...]; !App.ProductsRoute = Ember.Route.extend({ model: function() { return App.PRODUCTS; } }); !App.ProductRouter = Ember.Route.extend({ model: function(params) { return App.PRODUCTS.findBy('title', params.title); } });

Page 127: CodeSchool-Emberjs

Ember ModelA class that defines the properties and behavior of the data that you present to the user.

Previously our route set the model to an Array.

This is our Model icon

Every Route can have a Model.

Page 128: CodeSchool-Emberjs

Ember ModelBrowser Request

Router

?

?? Handlebars Template

Controller

Route

Model

A class that defines the properties and behavior of the data that you present to the user.

Page 129: CodeSchool-Emberjs

Creating an Ember Model

app.js

Next, our Ember Model needs to know what attributes it contains.

We are working with products, so lets define a product class.Models are typically nouns.

App.Product = DS.Model.extend({ });

Page 130: CodeSchool-Emberjs

Ember Model Attributesapp.js

string

number

boolean

date

Remember our data?

We need to know what data types our Model should expect.

App.Product = DS.Model.extend({ });title: 'Flint',

price: 99, description: 'Flint is…'

,

isOnSale: true, image: 'flint.png'

{

}

,

Page 131: CodeSchool-Emberjs

Building the Product Model

app.js

We’ll define all the different properties in our model class.

App.Product = DS.Model.extend({ title: DS.attr('string'), price: DS.attr('number'), description: DS.attr('string'), isOnSale: DS.attr('boolean'), image: DS.attr('string') });

title: 'Flint', price: 99, description: 'Flint is…', isOnSale: true, image: 'flint.png'

{

}

Page 132: CodeSchool-Emberjs

Implied Data Types

app.js

If property types aren’t supplied, they will be implied.

App.Product = DS.Model.extend({

});

title: DS.attr(), price: DS.attr(), description: DS.attr(), isOnSale: DS.attr(), image: DS.attr()

title: , price: 99, description: , isOnSale: true,

{

}

'Acorn'

'These...'

isSeasonal: true

Page 133: CodeSchool-Emberjs

Ember DataEmber Data makes it easy to use a Model to retrieve records from a server, cache them for performance, save updates back to the server, and create new records on the client.

This is our Ember Data icon

Page 134: CodeSchool-Emberjs

Ember DataBrowser Request

Router

?

?Handlebars Template

Controller

Route

Model

Ember Data

Page 135: CodeSchool-Emberjs

Ember Data Adapters

App.ApplicationAdapter = DS.RESTAdapter.extend();

This is the default adapter.

Allows us to hardcode our data in fixtures for getting started.

App.ApplicationAdapter = DS.FixtureAdapter.extend();

To communicate with an HTTP server using JSON

To load records from memory

Page 136: CodeSchool-Emberjs

Migrating to Fixturesapp.js

We’ll need to convert this to use Ember Data FixtureAdapter.

App.PRODUCTS = [{

title: 'Flint', price: 99, description: 'Flint is…', isOnSale: true, image: 'flint.png'

},{

title: 'Kindling', price: 249, description: 'Easily…', isOnSale: false, image: 'kindling.png' } ];

Page 137: CodeSchool-Emberjs

Ember Data Fixtures for Productapp.js

Needs to use the FIXTURES constant within the model

Need to give each product a unique ID

App. = [{

title: 'Flint', price: 99, description: 'Flint is…', isOnSale: true, image: 'flint.png'

},{

title: 'Kindling', price: 249, description: 'Easily…', isOnSale: false, image: 'kindling.png' } ];

Product.FIXTURESid: 1,

id: 2,

Page 138: CodeSchool-Emberjs

Ember Data Has A StoreCentral repository for records in your application, available in routes and controllers.

Think of it as a cache storage of all your records.

Store

We’ll use the store to retrieve records and create new ones.

App.Product.FIXTURES

Page 139: CodeSchool-Emberjs

Changing :title to :product_idapp.js

this.resource('products', function() { this.resource('product', { path: '/:product_id' }); });

Switch to using product_id as the dynamic segment

this.resource('products', function() { this.resource('product', { path: '/:title' }); });

Ember Data (by default) must use a unique identifier. We’ll use :product_id.

Page 140: CodeSchool-Emberjs

Need to Update Our Routesapp.js

In order to get our fixture data out of the store

Finds all products from the fixture adapter

App.ProductsRoute = Ember.Route.extend({model: function() {

return App.PRODUCTS; } });

App.ProductsRoute = Ember.Route.extend({model: function() {

return this.store.findAll('product'); } });

Page 141: CodeSchool-Emberjs

App.ProductRoute = Ember.Route.extend({ model: function(params) { return this.store.find('product', params.product_id); } });

Updating the Product Routeapp.js

Finds only the product with the matching ID

App.ProductRoute = Ember.Route.extend({ model: function(params) { return App.PRODUCTS.findBy(‘title', params.title); } });

Page 142: CodeSchool-Emberjs

App.ProductRoute = Ember.Route.extend({ model: function(params) { return this.store.find('product', params.product_id); } });

Using the Default Product Route

We can delete the ProductRoute and use the default!

Page 143: CodeSchool-Emberjs

Show new products page linking to product page

Page 144: CodeSchool-Emberjs

Level 4 - Acorn Models and Pinecone Data

Related models

Page 145: CodeSchool-Emberjs

Reviewapp.js

What if our products had reviews?

App.ProductsRoute = Ember.Route.extend({ model: function() { return this.store.findAll('product'); } });

App.Product = DS.Model.extend({ title: DS.attr('string'), price: DS.attr('number'), description: DS.attr('string'), isOnSale: DS.attr('boolean'), image: DS.attr('string')});

Page 146: CodeSchool-Emberjs

The Review Model

app.js

First lets create a new model to represent a Review.

App.Product = DS.Model.extend({ title: DS.attr('string'), price: DS.attr('number'), description: DS.attr('string'), isOnSale: DS.attr('boolean'), image: DS.attr('string')});

App.Review = DS.Model.extend({ text: DS.attr('string'), reviewedAt: DS.attr('date')});

Page 147: CodeSchool-Emberjs

Create Associations app.js

});

App.Review = DS.Model.extend({ text: DS.attr('string'),

});

reviews: DS.hasMany('review'

product: DS.belongsTo('product')

, {async: true})

Allows reviews to be lazy loaded

reviewedAt: DS.attr('date')

App.Product = DS.Model.extend({ title: DS.attr('string'), price: DS.attr('number'), description: DS.attr('string'), isOnSale: DS.attr('boolean'), image: DS.attr('string')

Page 148: CodeSchool-Emberjs

Creating our Review Fixturesapp.js

We’ll use higher IDs so we can differentiate them

text: "Started a fire in no time!" }, { id: 101,

} ];

text: "Not the brightest flame, but warm!"

App.Review.FIXTURES = [ { id: 100,

Page 149: CodeSchool-Emberjs

Mapping Reviews to a Productapp.js

text: "Started a fire in no time!" }, { id: 101,

text: "Not the brightest flame, but warm!"

product: 1,

product: 1,

App.Review.FIXTURES = [ { id: 100,

} ];

Page 150: CodeSchool-Emberjs

Mapping a Product to Reviewsapp.js

Need to use an array to list out all reviews

App.Product.FIXTURES = [ { id: 1, title: 'Flint', price: 99, description: 'Flint is…', isOnSale: true, image: 'flint.png', reviews: [100,101] } ];

Page 151: CodeSchool-Emberjs

Loop Over All Reviewsindex.html

Loop over all reviews and display them

<script type="text/x-handlebars" data-template-name='product'> <h1>{{title}}</h1> <p>{{description}}</p> <p>Buy for ${{price}}</p>

<h3>Reviews</h3> <ul>

{{ }}<li>{{review.text}}</li>

{{/each}} </ul>

</script>

#each review in reviews

Page 152: CodeSchool-Emberjs

Easier Loop Variablesindex.html

Within the loop, you can reference the current review

<script type="text/x-handlebars" data-template-name='product'> <h1>{{title}}</h1> <p>{{description}}</p> <p>Buy for ${{price}}</p>

<h3>Reviews</h3> <ul>

{{ }}#each reviews<li>{{text}}</li>

{{/each}} </ul>

</script>

Page 153: CodeSchool-Emberjs

Loops With {{else}}index.html

Add a default for when there are no reviews

<script type="text/x-handlebars" data-template-name='product'> <h1>{{title}}</h1> <p>{{description}}</p> <p>Buy for ${{price}}</p>

<h3>Reviews</h3> <ul>

{{ }}#each reviews<li>{{text}}</li>

{{else}} <li><p class='text-muted'> <em>No reviews yet. Be the first to write one!</em> </p></li>

{{/each}} </ul>

</script>

Page 154: CodeSchool-Emberjs

Demo works! Show all reviews

Page 155: CodeSchool-Emberjs
Page 156: CodeSchool-Emberjs

Switching to REST

Let’s switch our application adapter back to REST

App.ApplicationAdapter = DS.RESTAdapter.extend();

Page 157: CodeSchool-Emberjs

Ember Data hit the /products URL to get JSON data for our products route

Page 158: CodeSchool-Emberjs

Asynchronous JSONhttp://example.com/products

{ "products": [ { "id": 1, "title": "Flint", "price": 99, "description": "Flint is…", "isOnSale": true, "image": "/assets/products/flint.png", "reviews": [100,101] }, { … } ], "reviews": [ { "id": 100, "product": 1, "text": "Started a fire in no time!" }, … ] }

This JSON file could be generated or hardcoded

Page 159: CodeSchool-Emberjs