codeschool-emberjs
DESCRIPTION
material from CodeSchoolTRANSCRIPT
Setting up Ember.js
Level 1 - Warming Up
Ember.js“A framework for creating ambitious web applications”
Need To KnowJavaScriptHTML & CSS jQuery
Ruby & Rails Python & Django
Don’t need to know
Why Ember.js
Why Ember.js?
HTML
Web App
iOS Android
API
Database
Ember.js
Why Ember.jsYou want your application to feel responsive
When Not to Use
• Blog
• Newspaper
• Static Content
When your web app doesn’t have a lot of interactivity.
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
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>
Test Drive
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.
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
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.
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
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.
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>
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>
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
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
Named Templates
Level 1 - Warming Up
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>...
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>
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
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
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
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'>
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>
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>
Only our application template is showing!
No welcome message!
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>
...
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>
Our index template is rendered within our application template
The Router
Level 1 - Warming Up
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 ?
The Ember Router
Translates a path into a route. Like a tree trunk — all requests start here.
This is our Router icon
Routing Our Way In
Browser Request
Figures out the route name for each request
Ember Router
?
??
??
?
Handlebars Template
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() {
});…
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
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>
Template Naming
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
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!
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
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
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>
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
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
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
Handlebars
Level 2 - Rendering the Flame
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?
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>
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}}
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'
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.
TitleBody Level One
Demo showing li links still working
Controller Basics
Level 2 - Rendering the Flame
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.
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?
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
Routing Our Way InBrowser Request
Router
?
??
?? Handlebars Template
ControllerProvides properties for the template
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
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.
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>
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' />
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>
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' />
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.
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
}
}});
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()
TitleScreenshot with the time showing
Resource Routes
Level 3 - A Route Through the Woods
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>
We’ll show all of our products on a new products page
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');});
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');
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' );
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');
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.
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
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
Ember RouteProvides data for the controller
Ember RouterTranslates a path into a route
Don’t get these confused
Ember RouteBrowser Request
Router
??
?? Handlebars Template
Controller
Route
Provides data for the controller
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({ });
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; }
});
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>
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>
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}}
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}}
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>
Related and Dynamic Routes
Level 3 - A Route Through the Woods
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');
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');
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' );
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' );
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' }
{ 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) { ! } });
{ 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
Flint in the path is a dynamic segment
Our product template is rendered
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>
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….
Nested Routes
Level 3 - A Route Through the Woods
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
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
product template also rendering within {{outlet}} in the application template
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?
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
Template in a Template
application template
div.container{{outlet}}
products template
product template
How do we nest the product template inside the products template?
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
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' }); });});
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.
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>
TitleBody Level One
/products page works
Clicking on a product works!
https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi
Ember Inspector For Chrome
The products/index template will be shown within the products template with no product.
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
Level 4 - Acorn Models and Pinecone Data
Models and Ember Data
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); } });
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.
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.
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({ });
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'
{
}
,
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'
{
}
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
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
Ember DataBrowser Request
Router
?
?Handlebars Template
Controller
Route
Model
Ember Data
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
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' } ];
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,
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
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.
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'); } });
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); } });
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!
Show new products page linking to product page
Level 4 - Acorn Models and Pinecone Data
Related models
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')});
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')});
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')
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,
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,
} ];
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] } ];
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
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>
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>
Demo works! Show all reviews
Switching to REST
Let’s switch our application adapter back to REST
App.ApplicationAdapter = DS.RESTAdapter.extend();
Ember Data hit the /products URL to get JSON data for our products route
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