Last active
August 29, 2015 14:07
-
-
Save austinkeeley/c31be13c034056c54c82 to your computer and use it in GitHub Desktop.
Audio captions for "Building an Ember.js Application"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
0:00:01.029,0:00:05.030 | |
Hi, this is Tom and in this screencast we're | |
going to show you how to build a blog | |
0:00:05.003,0:00:06.962 | |
reading application using Ember.js. | |
0:00:07.259,0:00:10.610 | |
Ember is a JavaScript framework for | |
building ambitious | |
0:00:10.610,0:00:15.341 | |
URL driven web applications and the app | |
in particular that we're going to build | |
0:00:15.349,0:00:19.072 | |
you can see up here at the top has an About page, it | |
has a list of all the blog posts available | |
0:00:19.072,0:00:22.097 | |
and when you click on one of these post | |
you can see it displays it over here on the | |
0:00:22.097,0:00:25.417 | |
right hand side without removing it from | |
the left hand side | |
0:00:25.429,0:00:28.100 | |
--without removing this list, and if we want | |
to make an edit to one of these blog posts | |
0:00:28.100,0:00:33.063 | |
we can just click this edit button and you can see | |
that as we type, the blog post changes | |
0:00:33.063,0:00:35.620 | |
below automatically. | |
0:00:35.860,0:00:38.012 | |
So the first thing | |
we're going to do to get started | |
0:00:38.012,0:00:41.016 | |
is go to the Ember.js website and | |
download the starter kit. | |
0:00:41.016,0:00:44.038 | |
The starter kit is a zip file that | |
contains | |
0:00:44.038,0:00:48.026 | |
all the HTML and JavaScript that you'll need to | |
get started. We're also going to go to | |
0:00:48.026,0:00:50.057 | |
the Chrome Web Store in search for the | |
Ember Inspector. | |
0:00:50.057,0:00:54.022 | |
The Ember Inspector is an extension for | |
Chrome's Developer Tools that allows you | |
0:00:54.022,0:00:57.016 | |
to both understand and debug your Ember | |
applications. | |
0:00:57.016,0:01:04.016 | |
We'll show you in just a moment how it works. | |
0:01:04.040,0:01:12.720 | |
Now that we've downloaded and unzipped | |
the starter kit, let's open it up in our favorite editor. | |
0:01:13.100,0:01:18.000 | |
I'll be using Sublime Text but you can | |
use whatever you want. You can see that the starter kit | |
0:01:18.001,0:01:23.020 | |
automatically includes Ember.js and all | |
of its dependencies, | |
0:01:23.029,0:01:27.075 | |
namely Handlebars and jQuery. As we're | |
building our application | |
0:01:27.075,0:01:31.054 | |
all of our Handlebars templates are gonna | |
go into index.html | |
0:01:31.054,0:01:35.014 | |
and all the JavaScript is going to go | |
into app.js. | |
0:01:35.025,0:01:39.225 | |
I'm also going to include some | |
additional CSS and JavaScript libraries | |
0:01:39.229,0:01:40.729 | |
that I need to build the blog reader. | |
0:01:40.729,0:01:43.950 | |
In this case I'll include Twitter | |
Bootstrap CSS | |
0:01:43.950,0:01:48.040 | |
as well as moment.js, a date formatting | |
library, and Showdown, | |
0:01:48.040,0:01:54.000 | |
a library for converting Markdown into | |
HTML. Now that we've got all our dependencies installed | |
0:01:54.004,0:01:57.044 | |
I'm gonna switch over to the app.js | |
and I'm gonna delete | |
0:01:57.046,0:02:01.038 | |
all the stuff in here that the starter kit comes | |
with; we're not going be needing this. The only | |
0:02:01.038,0:02:02.998 | |
thing that we need here is the | |
0:02:03.008,0:02:06.055 | |
app, the Ember application instance and | |
this is the thing that kind of | |
0:02:06.055,0:02:11.015 | |
"boots up" the Ember application. The next | |
place I'm going to go is back to our | |
0:02:11.029,0:02:15.070 | |
index.html file and the starter kit also | |
comes with some Handlebars temples that | |
0:02:15.070,0:02:23.123 | |
we're not going to be needing so let me go ahead and delete all of these. | |
0:02:23.123,0:02:29.043 | |
And what I want to do is actually replace this with just some static HTML that's been marked up to | |
0:02:29.044,0:02:32.704 | |
look good in Twitter Bootstrap. | |
0:02:33.400,0:02:37.920 | |
So let me just paste this in here | |
0:02:38.056,0:02:42.316 | |
Now, if we open this up in the browser, we | |
should see | |
0:02:42.319,0:02:46.000 | |
that this temple has been rendered | |
automatically so just by creating an | |
0:02:46.000,0:02:47.599 | |
Ember application instance, | |
0:02:47.599,0:02:50.674 | |
that's us telling Ember that there's an | |
application template that we want to | |
0:02:50.680,0:02:56.000 | |
render automatically to the screen. Now looking up here we'll see there's two different | |
0:02:56.006,0:02:57.905 | |
areas: there's "Posts" and "About", | |
0:02:57.960,0:03:03.260 | |
so let's work on the About section first | |
what we'd like is if the user comes into /about | |
0:03:03.269,0:03:06.869 | |
it renders the "about" template on to the screen. | |
0:03:06.869,0:03:10.700 | |
Now in Ember, the way that you get a template on the | |
screen is to first think about what URL | |
0:03:10.700,0:03:15.139 | |
is associated with it, so let's switch | |
over to our editor | |
0:03:15.139,0:03:19.680 | |
and I'll go back to our app.js file | |
0:03:19.068,0:03:22.073 | |
and to define the URLs in our application | |
we'll say | |
0:03:22.073,0:03:25.087 | |
app.router.map() is a a method that | |
we can call | |
0:03:25.087,0:03:30.006 | |
and this method takes a function and | |
inside a this function we just want to | |
0:03:30.006,0:03:32.015 | |
define all the URLs in our application. | |
0:03:32.015,0:03:35.020 | |
So in this case at /about we want | |
to render the | |
0:03:35.020,0:03:41.000 | |
"about" template so we will say | |
this.resource('about'); | |
0:03:41.220,0:03:42.919 | |
So now that we've got our route defined, | |
0:03:42.919,0:03:46.039 | |
I want to create an "about" template, so to | |
do that | |
0:03:46.049,0:03:49.579 | |
I'm going to go into the index.html file | |
0:03:49.579,0:03:54.299 | |
and just like before we had the <script> tag with a type attribute as | |
0:03:54.299,0:03:57.599 | |
"text/x-handlebars", I'm gonna do the same | |
thing down here below, | |
0:03:57.609,0:04:00.870 | |
say type="text/x-handlebars" | |
0:04:00.870,0:04:06.200 | |
but I'm also gonna give it an ID and the ID | |
here is the name of the template, in this case, "about". | |
0:04:06.200,0:04:08.081 | |
And you can see this lines up with | |
0:04:08.081,0:04:14.031 | |
our URL so I'll just paste some | |
static content in here that I want to display | |
0:04:14.031,0:04:18.031 | |
on the screen. Now if I switch back to | |
my browser, | |
0:04:18.031,0:04:21.691 | |
I'll refresh the page and... nothing happens. | |
0:04:21.699,0:04:26.755 | |
Well, why not? Well, obviously we haven't | |
visited /about yet, so the browser | |
0:04:26.760,0:04:31.002 | |
doesn't know what to render, so let's | |
come up here to the URL bar | |
0:04:31.002,0:04:35.200 | |
I'll type #/about (because we're using hashbangs) | |
0:04:35.200,0:04:38.159 | |
and hit return... still nothing's | |
happening. | |
0:04:38.159,0:04:42.370 | |
Why not? Well, the reason for this is that | |
0:04:42.370,0:04:46.460 | |
we haven't put what's called an "outlet" | |
into our application template. | |
0:04:46.460,0:04:48.058 | |
So remember that the application template | |
0:04:48.058,0:04:51.778 | |
is the template that's rendered on screen -- it's | |
always visible | |
0:04:51.789,0:04:54.860 | |
to the user, but what we need to do | |
0:04:54.860,0:04:58.018 | |
is put in an "outlet" which is just a | |
placeholder that gets | |
0:04:58.018,0:05:01.111 | |
filled in with the template associated | |
with the URL. | |
0:05:01.120,0:05:06.076 | |
So we'll just use the outlet helper to tell Ember "this | |
is where temples get rendered when the | |
0:05:06.080,0:05:07.980 | |
URL changes." | |
0:05:07.980,0:05:11.000 | |
So let's save that and go back to our | |
browser and refresh | |
0:05:11.009,0:05:14.229 | |
and now you can see the static content | |
that we have is being rendered | |
0:05:14.229,0:05:18.620 | |
into the application template. Now let's | |
take a look at what this application | |
0:05:18.062,0:05:24.460 | |
looks like using the Ember Inspector. So if I open the developer tools, like Command-Option-J on my Mac, | |
0:05:24.460,0:05:26.340 | |
this will bring up the developer tools, | |
0:05:26.340,0:05:30.699 | |
if you click on Ember you can see this | |
says "No Ember Application Detected" | |
0:05:30.699,0:05:34.219 | |
but we've obviously got an Ember | |
application here and the reason is that | |
0:05:34.229,0:05:37.900 | |
Chrome has much stricter security | |
settings when you're loading files off the | |
0:05:37.900,0:05:41.019 | |
local hard disk so you can see | |
that we're using the file:// protocol here. | |
0:05:41.019,0:05:45.539 | |
So what I'm going to do is open my Chrome | |
preferences and go to Extensions | |
0:05:45.540,0:05:49.340 | |
and I'm just gonna come down here and click | |
on "Allow access to file URLs" | |
0:05:49.349,0:05:53.300 | |
for the Ember Inspector-- so let me | |
close this tab now, and if we refresh the page, | |
0:05:53.300,0:05:57.880 | |
you can see that it's now detecting our | |
Ember application | |
0:05:57.880,0:06:00.011 | |
and if you look, you can see that it's | |
actually showing us | |
0:06:00.011,0:06:03.080 | |
a hierarchy of all the templates that we | |
defined, so | |
0:06:03.080,0:06:07.080 | |
if I hover my mouse over "application" you | |
can see it's actually highlighting, | |
0:06:07.083,0:06:11.920 | |
in page, the region on screen powered by | |
the application template | |
0:06:11.920,0:06:14.979 | |
and if I hover over the "about" template | |
0:06:14.979,0:06:19.999 | |
-- the string "about" here, it's highlighting | |
that sub-template where the outlet was | |
0:06:20.000,0:06:23.159 | |
that the about temple was rendered into | |
and | |
0:06:23.159,0:06:27.379 | |
if we click on this "routes" tab here, you | |
can see that it's actually showing us | |
0:06:27.389,0:06:31.199 | |
all of the routes to find in our | |
application. In particular you can see | |
0:06:31.199,0:06:32.202 | |
that we've defined | |
0:06:32.229,0:06:35.539 | |
the "about route" --so the URL for that is | |
/about, | |
0:06:35.539,0:06:39.180 | |
and that showing the "about" template. You | |
can also see that there's that there's a kind of | |
0:06:39.180,0:06:44.560 | |
implicit route which matches just /. This is what we call the the index route. | |
0:06:44.560,0:06:48.440 | |
Now, it's probably not a tenable | |
long-term strategy to have our users be | |
0:06:48.449,0:06:51.508 | |
typing in the URL manually in the | |
address bar of their browsers | |
0:06:51.508,0:06:55.080 | |
It would be nice if when they clicked | |
on these links you could get back and | |
0:06:55.080,0:06:57.160 | |
forth but these are actually just stubs | |
0:06:57.169,0:07:00.860 | |
right now so the next step is just turn | |
these into real | |
0:07:00.860,0:07:04.006 | |
links so let me switch back to our code | |
editor. | |
0:07:04.006,0:07:08.077 | |
Just real quick I'm gonna make a | |
resource. I'm gonna make a URL for "Posts", | |
0:07:08.077,0:07:12.117 | |
this is just a placeholder; we'll fill this in with a template later. | |
0:07:12.129,0:07:15.370 | |
I'm going to switch over to index.html | |
0:07:15.370,0:07:19.637 | |
and what I'm gonna do is I'm going to | |
replace | |
0:07:19.639,0:07:25.059 | |
these stubbed out <a> tags with the | |
Handlebars helper #link-to | |
0:07:25.069,0:07:28.479 | |
and this is what we call a "block helper" so | |
this needs and opening | |
0:07:28.479,0:07:31.939 | |
helper and closing helper (a tag | |
may be one way to think about it), | |
0:07:31.949,0:07:36.200 | |
and I'll just do the same thing here for | |
the second one, so {{#link-to 'about'}} | |
0:07:36.200,0:07:41.060 | |
and then I'll also just close this | |
Handlebar's helper. | |
0:07:41.069,0:07:44.189 | |
So now if we switch back to our browser | |
and refresh the page | |
0:07:44.189,0:07:47.639 | |
you'll see that not only is there an | |
0:07:47.639,0:07:51.750 | |
"about" route there's also now a "posts" | |
route. So let me close this | |
0:07:51.750,0:07:54.935 | |
and if we click on About you can see | |
this now links to the "about" | |
0:07:54.949,0:07:58.400 | |
route and if I click on Posts this will also link to the | |
0:07:58.400,0:08:02.444 | |
"posts" template if we had one defined. So | |
there are a few things to note here: | |
0:08:02.449,0:08:05.530 | |
the first is that as we move between | |
these | |
0:08:05.530,0:08:08.689 | |
links, you can see that it's | |
actually keeping the URL | |
0:08:08.689,0:08:11.580 | |
up-to-date for us automatically; we're | |
not writing the code to do that, that just | |
0:08:11.580,0:08:13.000 | |
happens automatically | |
0:08:13.003,0:08:17.503 | |
and if we were to switch back to our | |
CSS | |
0:08:17.509,0:08:25.340 | |
and if we were to add a class of "active" | |
and just make this font-weight: bold | |
0:08:25.340,0:08:26.960 | |
you can see now if I refresh the | |
page | |
0:08:26.969,0:08:30.016 | |
every link using the "link-to" helper | |
gets this | |
0:08:30.020,0:08:34.380 | |
"active" class name applied to it so it's | |
very easy to add styling to indicate | |
0:08:34.380,0:08:36.018 | |
whatever the currently active | |
0:08:36.024,0:08:40.030 | |
route is. So far, everything we've done | |
has been pretty static. | |
0:08:40.030,0:08:43.890 | |
What we'd like to do now is if we click | |
on this Post link what would like to | |
0:08:43.890,0:08:46.039 | |
happen is to see a dynamically generated | |
list | |
0:08:46.039,0:08:51.999 | |
of all the posts available and we really | |
want that to be backed by some kind of JSON object | |
0:08:52.000,0:08:55.018 | |
so let's take a slight diversion here | |
and just | |
0:08:55.018,0:08:58.094 | |
understand that in Ember, a template is | |
always backed by | |
0:08:58.094,0:09:03.094 | |
a model. Now usually that model some | |
kind of JSON that comes in from a server | |
0:09:03.100,0:09:06.420 | |
but it can really be anything and the | |
cool thing about Ember is that when | |
0:09:06.420,0:09:09.839 | |
the underlying model changes, the temple | |
will actually automatically update itself; | |
0:09:09.839,0:09:12.990 | |
you don't need to write any code to say "go into the DOM make sure it matches | |
0:09:12.990,0:09:17.370 | |
the new reality," that sort of thing just | |
happens for you automatically. | |
0:09:17.370,0:09:21.440 | |
Now that we understand that templates are | |
backed by models, we know that we have two tasks: | |
0:09:21.440,0:09:26.240 | |
the first is to create a "post" template | |
and the second task is to specify which | |
0:09:26.240,0:09:27.560 | |
model should back that template. | |
0:09:27.820,0:09:31.051 | |
So let's go ahead and define the template | |
first. So I'm gonna come into our | |
0:09:31.051,0:09:33.071 | |
index.html file here, | |
0:09:33.080,0:09:36.860 | |
and I'm gonna just paste in a template | |
that I prepared already | |
0:09:36.860,0:09:40.339 | |
so a couple of things to call out: the first | |
is remember that its type attribute is | |
0:09:40.339,0:09:42.659 | |
"text/x-handlebars" | |
0:09:42.660,0:09:44.579 | |
and we've given it the | |
ID of "posts" | |
0:09:44.580,0:09:48.860 | |
that means that when you visit the | |
/posts URL, it's going to render this template. | |
0:09:48.860,0:09:53.279 | |
The next thing is that we are using some | |
Handlebars helpers inside of here to dynamically | |
0:09:53.279,0:09:57.600 | |
power this HTML so in particular we're | |
using the #each helper | |
0:09:57.600,0:10:00.066 | |
which basically says "take the content | |
inside of it | |
0:10:00.067,0:10:05.027 | |
and if your backing model is an array, | |
repeat this snippet once for each item | |
0:10:05.029,0:10:08.390 | |
inside of that array". Now that we've got the | |
template setup, | |
0:10:08.039,0:10:13.057 | |
let's switch back over to our app.js | |
and the first thing I'm gonna do is just | |
0:10:13.057,0:10:17.016 | |
bring over some fixture data that I've got | |
--we're not plugging this into a | |
0:10:17.016,0:10:20.236 | |
real server so I was going to use some | |
fake data for right now, | |
0:10:20.240,0:10:24.760 | |
I'll paste this in at the bottom the file so | |
you can see this is just a variable | |
0:10:24.769,0:10:30.560 | |
called "posts" and "posts" is just an array | |
of plain old JavaScript objects-- nothing | |
0:10:30.560,0:10:32.116 | |
fancy going on here. | |
0:10:32.120,0:10:35.140 | |
Well, now I've got my template and now got my | |
model... | |
0:10:35.149,0:10:38.940 | |
how do I combine these two things | |
together? Well, in Ember | |
0:10:38.940,0:10:42.020 | |
there is an object called the "route" and | |
the route is responsible | |
0:10:42.025,0:10:46.645 | |
for specifying which model a template | |
should be backed by, | |
0:10:46.649,0:10:50.929 | |
so this case because I want to specify | |
the model for the "posts" | |
0:10:50.929,0:10:54.020 | |
template I'm going to create a route | |
called "PostsRoute". | |
0:10:54.022,0:10:58.662 | |
So I'll do that by saying | |
app.PostsRoute = Ember.route.extend(...), | |
0:10:58.662,0:11:01.780 | |
This is just creating a subclass of | |
the route object. | |
0:11:01.780,0:11:05.700 | |
Now routes have a method called "model" | |
0:11:05.709,0:11:09.990 | |
and this is a hook that Ember calls into | |
when it needs to ask you "what is the | |
0:11:09.990,0:11:12.930 | |
model I should be using for this | |
template?" So I'll say | |
0:11:12.930,0:11:18.094 | |
model: function() and here I'll just return | |
this "posts" variable that we defined at | |
0:11:18.094,0:11:22.294 | |
the bottom of the page. Now if I were | |
switched over to my browser | |
0:11:22.300,0:11:27.699 | |
and refresh, you can see that we have a | |
dynamically populated list of all the | |
0:11:27.699,0:11:29.250 | |
blog post-- basically every | |
0:11:29.250,0:11:33.005 | |
blog post that we have in our | |
fixture array here, is now being rendered | |
0:11:33.005,0:11:34.545 | |
onto the screen. | |
0:11:35.001,0:11:37.057 | |
So now that we have this dynamically | |
populated list | |
0:11:37.057,0:11:41.086 | |
posts we'd like to happen is that if you | |
click on one, it'll show some more detail | |
0:11:41.086,0:11:43.033 | |
like for example the | |
0:11:43.033,0:11:46.086 | |
contents of it. Now we know that | |
0:11:46.086,0:11:50.011 | |
in Ember, if you want to show a template, | |
you need to think about the URL. | |
0:11:50.011,0:11:54.751 | |
So let's swhich back to our app.js file | |
and we'll create a new resource although | |
0:11:54.760,0:11:59.012 | |
we're gonna do something a little bit different this | |
time. So I'm gonna say "post" | |
0:11:59.012,0:12:03.992 | |
-- and this is the singular instead of | |
the plural, but unlike all of our previous | |
0:12:04.004,0:12:08.030 | |
templates, instead of there always being | |
the same model, the model backing | |
0:12:08.030,0:12:12.340 | |
this template actually changes depending | |
on which post your viewing. | |
0:12:12.340,0:12:16.057 | |
Now we need to somehow capture that in the URL. In the URL we need to specify which post | |
0:12:16.057,0:12:20.037 | |
we're looking at so I'm gonna say here, I'm | |
gonna pass an options hash | |
0:12:20.042,0:12:24.001 | |
and this takes a path and we're just gonna say | |
0:12:24.001,0:12:28.700 | |
this guy will be called post_id. | |
0:12:28.700,0:12:31.040 | |
So now that we've created this | |
0:12:31.042,0:12:36.022 | |
"post" resource and we specified how the | |
post's ID should go into the URL, | |
0:12:36.024,0:12:40.004 | |
let's go and actually create that | |
template. So let me switch back | |
0:12:40.007,0:12:43.013 | |
to index.html here and just like before | |
0:12:43.013,0:12:49.113 | |
I've prepared a Handlebars template | |
inside of a <script> tag | |
0:12:49.120,0:12:52.018 | |
and this is just the same as before | |
these are just pulling attributes from | |
0:12:52.018,0:12:55.538 | |
the particular model backing this | |
template so this is saying for example | |
0:12:55.540,0:12:59.580 | |
"get the title property, this is the | |
excerpt, here's the full body of the post," | |
0:12:59.580,0:13:03.013 | |
but now that we've got this list the post how do we tell the app | |
0:13:03.013,0:13:06.029 | |
"hey, transition into this post template | |
and, | |
0:13:06.029,0:13:09.860 | |
by the way, here's the specific post I | |
want you to show"? Well, just like before | |
0:13:09.860,0:13:12.520 | |
we're going to use the #link-to helper | |
0:13:12.520,0:13:16.020 | |
so let me say #link-to and the name of this | |
template is just "post" | |
0:13:16.020,0:13:19.048 | |
and I'll put a closing /link-to, | |
0:13:19.055,0:13:23.015 | |
but one thing that you need to know | |
about the #link-to helper is that it actually takes | |
0:13:23.022,0:13:26.024 | |
an optional argument which is the model that you want to display | |
0:13:26.024,0:13:29.058 | |
so in this case I'll say "this" which is the | |
model | |
0:13:29.058,0:13:32.072 | |
inside the #each so it's looping over a list | |
of all the posts and | |
0:13:32.072,0:13:36.012 | |
and "this" is going to be whichever model | |
--whichever post, that we click on. | |
0:13:36.029,0:13:41.023 | |
Now if we switch back to browser and I | |
refresh the page you can see that these have all become links now | |
0:13:41.023,0:13:46.012 | |
and when I click on it you can see it's | |
now showing us that post template that we had defined | |
0:13:46.012,0:13:49.073 | |
and perhaps most importantly --check this | |
out: | |
0:13:49.073,0:13:53.033 | |
the ID of that post is actually being put into | |
the URL for us automatically. | |
0:13:53.042,0:13:56.067 | |
Now if we hit the back button, we can go | |
see the list of the posts again if we | |
0:13:56.067,0:14:01.014 | |
click on a different post, now you'll see the ID "2" instead of "1" is in the URL. | |
0:14:01.014,0:14:05.067 | |
Again, we can come back here and we'll see the list again. So this is great, but | |
0:14:05.067,0:14:09.660 | |
what we said that we wanted initially | |
was that when you click on one of these posts, | |
0:14:09.660,0:14:13.540 | |
instead of replacing the entire template | |
--like switching between Posts and About, | |
0:14:13.540,0:14:16.060 | |
instead we'd like to keep the list on | |
the left hand side and instead of | |
0:14:16.062,0:14:22.142 | |
replacing it, we want to render this post | |
template next to this content over here. | |
0:14:22.142,0:14:26.016 | |
Well, the good news is that Ember has a | |
feature called "nested routes" | |
0:14:26.016,0:14:30.054 | |
and what this allows you to do is | |
instead of replacing the template above you | |
0:14:30.054,0:14:34.057 | |
you can render into it. So let me show | |
you what I mean. I'll switch back to our | |
0:14:34.060,0:14:39.035 | |
app.js file here and instead of "post" and | |
"posts" | |
0:14:39.035,0:14:43.048 | |
being siblings, I'm gonna pass a function | |
0:14:43.048,0:14:47.042 | |
to this resource -- and I was gonna move this inside of it so this is what we call a | |
0:14:47.042,0:14:51.075 | |
"child route", this is a child route nested inside of its parent. | |
0:14:51.075,0:14:54.099 | |
And now all I need to do is make sure that | |
my "posts" | |
0:14:54.099,0:14:59.079 | |
template includes an outlet inside of it --remember this outlet? This is where the child | |
0:14:59.080,0:15:02.020 | |
template gets rendered into. So this is telling it | |
"hey when you enter this child template, | |
0:15:02.028,0:15:05.016 | |
render it here inside this <div> with the | |
class of 'span9'" | |
0:15:05.016,0:15:09.476 | |
Now if I come back and refresh the page, | |
you can see when I click | |
0:15:09.480,0:15:13.000 | |
instead of replacing this list it's | |
actually going to render it into it | |
0:15:13.003,0:15:17.007 | |
and again if we bring up our inspector | |
you can see that now our | |
0:15:17.007,0:15:20.096 | |
hierarchy shows the "application" template, | |
the "post" template and then, | |
0:15:20.096,0:15:23.596 | |
inside of it, this "posts" template. | |
0:15:24.022,0:15:28.002 | |
Now that we can see a particular post | |
let's give the user the ability to make | |
0:15:28.006,0:15:31.092 | |
changes to it. I'm gonna switch over to | |
our HTML file | |
0:15:31.092,0:15:35.143 | |
and I'm going to add some editing UI | |
to our "posts" template. | |
0:15:35.143,0:15:41.037 | |
So I'll just add it up at the top here -- I'll delete this. So what this is saying is | |
0:15:41.048,0:15:46.540 | |
if we're in editing mode, show the | |
"post/edit" partial and | |
0:15:46.540,0:15:49.073 | |
you can see that there are some buttons | |
here and these buttons are using the | |
0:15:49.073,0:15:53.091 | |
action helper. Now the action helper just | |
sends events to either your | |
0:15:53.091,0:15:57.011 | |
application's controller or your route when | |
some kind of UI event happens, in this | |
0:15:57.027,0:15:59.147 | |
case, if the user clicks on the button. | |
0:15:59.149,0:16:02.450 | |
So what we're saying is that when the user | |
clicks the edit button, we want to send | |
0:16:02.450,0:16:06.035 | |
the 'edit' action and if they click the | |
Done button we want to send the | |
0:16:06.035,0:16:10.995 | |
'doneEditing' action. Now I'm gonna switch | |
over to our app.js file and | |
0:16:11.005,0:16:14.585 | |
I'm gonna make a new object called | |
a "controller". | |
0:16:14.589,0:16:19.041 | |
Now, in Ember, a controller is an object that | |
stores application state | |
0:16:19.041,0:16:22.052 | |
and it responds to events from your | |
templates. | |
0:16:22.052,0:16:26.012 | |
So in this case you can see I've got a | |
property called isEditing: false | |
0:16:26.026,0:16:31.025 | |
So this isn't persistent state we | |
don't say this on the model because if the user were to | |
0:16:31.025,0:16:34.065 | |
refresh the page, they wouldn't | |
necessarily expect that they would still | |
0:16:34.066,0:16:37.008 | |
be in editing mode. Similarly, you can see that we have this | |
0:16:37.008,0:16:41.006 | |
'actions' hash that contains methods | |
0:16:41.006,0:16:44.026 | |
for each of the actions that we're | |
sending from our template, right? So if you remember, | |
0:16:44.055,0:16:48.014 | |
here, we're saying "send the 'doneEditing | |
action'" when you're done editing or if | |
0:16:48.014,0:16:49.374 | |
you want to go into editing mode, | |
0:16:49.380,0:16:52.060 | |
"send 'edit' action." We're handling those | |
here | |
0:16:52.065,0:16:57.400 | |
inside in this "actions" hash and if you | |
look at the implementation you can see | |
0:16:57.400,0:17:02.000 | |
inside of the "edit" action, we're just | |
flipping this boolean flag from false to true | |
0:17:02.004,0:17:05.184 | |
and then in 'doneEditing' we go back. | |
0:17:05.184,0:17:10.003 | |
So now all we need to do is go back to our | |
template you can see that we are | |
0:17:10.003,0:17:15.820 | |
including another template by using the | |
partial helper so this partial is called /post/edit | |
0:17:15.820,0:17:20.000 | |
so this is just a mechanism for | |
breaking up your templates if they start to get a little bit big. | |
0:17:20.000,0:17:23.049 | |
So I'm gonna paste that template in here | |
now you can see that this is just a | |
0:17:23.049,0:17:28.280 | |
little bit of an editing UI, so this is creating a bound | |
input tag | |
0:17:28.280,0:17:32.040 | |
it's binding to the model's title property | |
there's another one similar for excerpt | |
0:17:32.041,0:17:36.560 | |
and then finally there's a textarea for | |
editing the body. | |
0:17:36.560,0:17:39.720 | |
Now if I save this, I'll switch over to my | |
browser | |
0:17:39.720,0:17:42.840 | |
and now if I click on one of these posts | |
and I click on Edit | |
0:17:42.840,0:17:48.011 | |
you can see it's now putting up this | |
editing UI for me and this is actually live bound so | |
0:17:48.011,0:17:51.096 | |
as I make changes here --you can see as I | |
type, it's not only changing down below | |
0:17:51.096,0:17:53.034 | |
here where the titles being used | |
0:17:53.034,0:17:57.089 | |
but also here in the list on the left | |
hand side. So now we can click the Done | |
0:17:57.089,0:17:59.760 | |
button; this will end editing. You can see | |
0:17:59.760,0:18:06.040 | |
we can click around, we can edit another post... but I'm noticing a problem with the application | |
0:18:06.040,0:18:12.020 | |
and the problem is that if I click the | |
reload button, you can see the application breaks | |
0:18:12.020,0:18:14.820 | |
and the reason that it breaks is that | |
when you refresh the page, | |
0:18:14.820,0:18:19.000 | |
it throws away everything, including those | |
JavaScript objects that were | |
0:18:19.001,0:18:22.058 | |
backing the template. So, of course, what the application needs to do is | |
0:18:22.058,0:18:25.141 | |
reconstruct those models from the URL. | |
0:18:25.141,0:18:30.179 | |
In this case, our URL is /post/2 | |
this is the ID | |
0:18:30.179,0:18:33.089 | |
of the post that we want to display but | |
unfortunately so far our | |
0:18:33.089,0:18:38.002 | |
application doesn't know how to turn | |
this URL /post/2 | |
0:18:38.002,0:18:41.023 | |
into the JavaScript object that has the | |
ID of 2. | |
0:18:41.023,0:18:44.035 | |
Well, remember in Ember, | |
0:18:44.035,0:18:47.795 | |
the object who's responsible for | |
translating the URL | |
0:18:47.809,0:18:51.082 | |
into a model for a template is the route, | |
so before we had a | |
0:18:51.082,0:18:55.022 | |
PostsRoute but now I'm going to add a new | |
route | |
0:18:55.022,0:18:59.078 | |
called PostRoute --singular, and remember | |
this is the object responsible for | |
0:18:59.080,0:19:04.096 | |
giving the post template its model. Now this is a little bit different from before because | |
0:19:04.096,0:19:09.052 | |
our resources are little bit different because it has what we call | |
0:19:09.052,0:19:14.011 | |
a dynamic segment, right? Part of its URL | |
is not static; it's dynamic based on the | |
0:19:14.011,0:19:15.034 | |
ID of the model. | |
0:19:15.040,0:19:22.020 | |
So this post_id gets passed in as part of this params hash. You can see that we're | |
0:19:22.020,0:19:29.035 | |
are looking through the posts array looking for the first post whose ID property | |
0:19:29.035,0:19:33.063 | |
matches the ID in the URL and we're just returning | |
that for the model hook. | |
0:19:33.063,0:19:38.423 | |
So this is just telling Ember "hey we've | |
come in the /post/2," | |
0:19:38.429,0:19:41.059 | |
for example, "go find the first post | |
0:19:41.059,0:19:46.039 | |
whose ID is 2 in our fixture data | |
and make that the model of the template. | |
0:19:46.048,0:19:50.007 | |
So now we made this change and we | |
refresh the page | |
0:19:50.007,0:19:54.547 | |
--should probably save first, refresh the page, now you | |
can see that the application works. | |
0:19:55.005,0:19:59.624 | |
Okay, so I'm noticing a few more problems | |
the first one is that this date was | |
0:19:59.669,0:20:02.046 | |
obviously intended to be consumed by | |
computers, | |
0:20:02.046,0:20:06.015 | |
not humans, so what I'd like to do in | |
this case is create what's called | |
0:20:06.020,0:20:10.040 | |
a "Handlebars helper" which we can use in our Handlebars helpers to help format | |
0:20:10.049,0:20:14.086 | |
specific values. So I'm gonna make a new | |
helper | |
0:20:14.086,0:20:17.088 | |
called format-date --I'll add this right here, | |
0:20:17.088,0:20:21.094 | |
this is going to rely on the moment.js | |
formatting library, so this just takes | |
0:20:21.094,0:20:26.914 | |
a date as an input value; it uses the | |
moment API to convert it into a string | |
0:20:26.919,0:20:28.159 | |
that contains the date | |
0:20:28.159,0:20:31.160 | |
but relativized from the current time | |
for human to read. | |
0:20:31.169,0:20:35.027 | |
So now that I've created this format-date | |
Handlebars helper I can just go back to my | |
0:20:35.027,0:20:38.078 | |
Handlebars templates and wherever I have | |
a value | |
0:20:38.078,0:20:42.998 | |
I can just put the name of the helper right | |
before that. So, in this case I have the format | |
0:20:43.003,0:20:46.082 | |
date helper --I'm just going to insert that right before | |
the dtate property. | |
0:20:46.082,0:20:51.002 | |
And now if I go back and I refresh the page, you | |
can see now instead of | |
0:20:51.008,0:20:56.428 | |
that ugly computer date, I'm getting this | |
nice relative date, "8 months ago". | |
0:20:56.428,0:21:01.000 | |
Another thing I'm noticing is that down below, this isn't actually HTML, this | |
0:21:01.009,0:21:05.057 | |
looks like it's Markdown, so in addition | |
to the format-date helper, | |
0:21:05.057,0:21:11.135 | |
I am also going to create a new helper | |
called format-markdown | |
0:21:12.035,0:21:15.071 | |
and this is just going to use the | |
Showdown JavaScript library --you see I'm creating | |
0:21:15.071,0:21:17.073 | |
a new helper here called format-markdown, | |
0:21:17.073,0:21:21.098 | |
this is going to take some Markdown as | |
input and it's going to | |
0:21:21.098,0:21:27.438 | |
return that Markdown turned into HTML. | |
0:21:27.440,0:21:30.040 | |
Now, it's important that your | |
Handlebars helpers, by default | |
0:21:30.048,0:21:34.480 | |
escape any HTML that you might | |
return because this could make you | |
0:21:34.480,0:21:38.055 | |
vulnerable to an XSS attack, so if you want to opt out | |
0:21:38.055,0:21:43.015 | |
of that XSS protection, what you want to do | |
is return a new Handlebars.SafeString | |
0:21:43.027,0:21:47.015 | |
This is telling Ember and Handlebars "hey | |
I've taken responsibility for making sure | |
0:21:47.015,0:21:50.031 | |
that there aren't any potential XSS | |
to attacks in this | |
0:21:50.031,0:21:54.151 | |
HTML that I'm gonna render to the | |
screen." So now that we've | |
0:21:54.169,0:21:59.019 | |
got this format-markdown helper, just | |
like before, I'm gonna go back to my Handlebars templates | |
0:21:59.019,0:22:02.050 | |
and and everywhere that I was previously | |
outputting | |
0:22:02.005,0:22:06.010 | |
this raw Markdown, I'm going to replace | |
that with our | |
0:22:06.055,0:22:09.096 | |
format-markdown helper | |
0:22:09.096,0:22:13.180 | |
and we'll do the same thing down here for | |
the body. | |
0:22:14.008,0:22:17.887 | |
--Looks like I accidently took off one of these | |
opening curlies. | |
0:22:18.679,0:22:22.011 | |
Now if I refresh the page, you can see | |
that instead | |
0:22:22.011,0:22:25.015 | |
that ugly Markdown, we've actually got | |
this really nice | |
0:22:25.015,0:22:28.995 | |
formated HTML, so it's taking the | |
Markdown and turning into HTML | |
0:22:29.008,0:22:32.027 | |
and what's really cool, I think, is that | |
this is all "live bound" --these helpers | |
0:22:32.027,0:22:33.659 | |
update automatically | |
0:22:33.659,0:22:38.260 | |
so if I were to expand this and add a | |
new header | |
0:22:38.026,0:22:42.089 | |
you can see that as I type it's actually... | |
0:22:42.089,0:22:46.106 | |
formatting this text. | |
0:22:47.006,0:22:52.033 | |
So I think that's pretty cool and that's the | |
kinda stuff that you can get with server rendered web applications. | |
0:22:52.033,0:22:58.053 | |
So let's take stock of what we've built | |
here. If we come in to /post you can see it will give us a list | |
0:22:58.056,0:23:03.031 | |
all the posts in our application. If we | |
click on it, instead of replacing the whole template | |
0:23:03.031,0:23:07.083 | |
you can see it will show it over here. | |
We've got this edit UI that we've | |
0:23:07.083,0:23:10.422 | |
broken apart into maintainable different | |
templates. | |
0:23:10.422,0:23:15.020 | |
We can refresh the page if we command | |
click when these links you can see that | |
0:23:15.025,0:23:16.694 | |
we haven't broke command clicking. | |
0:23:16.700,0:23:20.960 | |
We can click the forward and back button, | |
URL support works really, really great. | |
0:23:21.429,0:23:26.039 | |
So this is a a pretty cool app and I | |
think if we switch back to our editor, | |
0:23:26.039,0:23:30.019 | |
you'll see we've only written... --this is 44 lines of | |
code | |
0:23:30.019,0:23:33.023 | |
including white space, which is not bad | |
--I'm not counting the fixtures here but | |
0:23:33.023,0:23:39.120 | |
application code is under fifty lines of code | |
and I think for what we built that's quite impressive. | |
0:23:39.120,0:23:42.000 | |
There is one last thing that I want to | |
show you that I think is | |
0:23:42.002,0:23:46.045 | |
is pretty cool. So far we've been | |
using fixture data. | |
0:23:46.045,0:23:50.019 | |
I actually have a JSON API that I use | |
from my blog | |
0:23:50.019,0:23:53.063 | |
so what I wanna do was actually just | |
remove this fixture data | |
0:23:53.063,0:23:57.003 | |
and replace it with a live JSON API | |
actually pulling live | |
0:23:57.004,0:24:00.820 | |
over the network. So the first thing I'm | |
going to do is to scroll to the bottom | |
0:24:00.820,0:24:02.640 | |
of this file and I'm gonna delete | |
0:24:02.649,0:24:05.960 | |
all this fixture data that we had | |
before --save. | |
0:24:05.960,0:24:09.096 | |
So that's gone now. So one thing that's | |
really cool about the | |
0:24:09.096,0:24:13.012 | |
routes model hook is that it can handle | |
either synchronous or | |
0:24:13.012,0:24:16.012 | |
asynchronous data and this looks the same, | |
0:24:16.012,0:24:20.051 | |
so for example right now we are | |
synchronously returning this posts array | |
0:24:20.051,0:24:24.991 | |
but I can take this and I can replace it | |
with this call to jQuery | |
0:24:25.001,0:24:28.070 | |
so jQuery's $.getJSON method returns | |
a promise | |
0:24:28.079,0:24:31.899 | |
and the model hook supports promises out | |
of the box. | |
0:24:31.899,0:24:35.004 | |
So anything that I return from here | |
that's a promise, it'll actually wait to | |
0:24:35.004,0:24:36.037 | |
render that template | |
0:24:36.037,0:24:40.377 | |
until that promise has been fulfilled. In | |
this case, until this JSON request | |
0:24:40.389,0:24:42.005 | |
--you can see I'm using a JSONP API, | |
0:24:42.005,0:24:45.006 | |
has finished and you'll note what I'm doing | |
here | |
0:24:45.006,0:24:50.004 | |
is that, like with any promise, we | |
can add a .then() on the end of it | |
0:24:50.004,0:24:53.021 | |
and this actually gives us an | |
opportunity to change that data a little bit. | |
0:24:53.021,0:24:56.007 | |
So in my case, my JSON API, the post | |
has a | |
0:24:56.007,0:24:59.015 | |
property called content, but if you recall from | |
earlier, | |
0:24:59.078,0:25:04.638 | |
our templates are assuming a property called body so I'm just writing a little function here | |
0:25:04.639,0:25:08.025 | |
that will translate the JSON data coming | |
from my server | |
0:25:08.025,0:25:12.925 | |
into the format that my template expects. | |
And I can do a similar thing here | |
0:25:12.929,0:25:16.082 | |
for the post route so this is how to get | |
a list of all the posts | |
0:25:16.082,0:25:20.902 | |
and then if we come in it, for example, | |
/post/2 | |
0:25:20.909,0:25:25.669 | |
--I'll paste this in, this is just a similar | |
thing that's making a JSON request | |
0:25:25.669,0:25:29.049 | |
to my JSON API on my blog and you can see that | |
we're actually | |
0:25:29.049,0:25:34.075 | |
interpolating the ID of the post that we | |
want to look up, and again, doing that same | |
0:25:34.075,0:25:38.035 | |
aliasing the content property to body. | |
0:25:38.036,0:25:43.099 | |
So I'll save this and I'll switch over to | |
my browser. | |
0:25:43.099,0:25:47.039 | |
And if we refresh the page, you can see | |
that | |
0:25:47.047,0:25:51.051 | |
it's actually loading all of the blog posts | |
for my blog using this | |
0:25:51.051,0:25:55.411 | |
JSON API --now I can see that there's | |
some HTML formatting here so I'll | |
0:25:55.419,0:25:57.120 | |
switch over to my template | |
0:25:57.120,0:26:01.152 | |
and look for where the title is being | |
used in this list. I can tell | |
0:26:01.169,0:26:07.449 | |
Handlebars not to escape a property by | |
putting it in triple curlies instead of just double. | |
0:26:08.075,0:26:12.136 | |
So I'll save and refresh... | |
0:26:13.036,0:26:16.061 | |
and now you can see these are being | |
formatted exactly as before. | |
0:26:16.061,0:26:20.052 | |
I'll click on this you can see it's | |
actually displaying the post inline here. | |
0:26:20.052,0:26:25.012 | |
I can edit it as before, so I can like chop off | |
this bit, | |
0:26:25.022,0:26:29.038 | |
I can say "done", and you can see that its | |
formatting these dates correctly, | |
0:26:29.038,0:26:32.084 | |
you can see I don't blog very often so three | |
months ago, | |
0:26:32.084,0:26:36.899 | |
seven months ago... and you can see it's | |
actually putting the ID | |
0:26:36.899,0:26:43.000 | |
of the post in the URL automatically so | |
if I were to refresh the page... | |
0:26:43.000,0:26:45.900 | |
you can see that exact same data | |
comes up. | |
0:26:48.012,0:26:51.046 | |
I hope you enjoyed learning about how to | |
build apps using Ember.js | |
0:26:51.046,0:26:55.084 | |
I think you'll be surprised to find | |
out that as you build out these applications, | |
0:26:55.084,0:26:59.086 | |
it doesn't actually require dramatically | |
more code --every new feature is only just | |
0:26:59.086,0:27:02.360 | |
a little bit on top of what you had | |
before | |
0:27:02.360,0:27:05.120 | |
and its scales really nicely across | |
teams too. | |
0:27:05.120,0:27:09.000 | |
So if you're interested, I would recommend that you check out the guides on the Ember website | |
0:27:09.000,0:27:12.720 | |
we've got really in-depth guides that will | |
help you become an Ember expert | |
0:27:12.720,0:27:16.016 | |
and I'd also recommend that you check out | |
the community section; this is a great | |
0:27:16.016,0:27:17.936 | |
way to get in touch with the community, | |
0:27:17.940,0:27:21.980 | |
find out how to join a meet up, get help on | |
Stack Overflow... | |
0:27:21.980,0:27:25.015 | |
If you need anything, please join us in | |
the IRC channel; we're happy to answer | |
0:27:25.015,0:27:26.915 | |
any questions that you might have. | |
0:27:26.920,0:27:28.160 | |
Thanks for watching. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment