Skip to content

Instantly share code, notes, and snippets.

@austinkeeley
Last active August 29, 2015 14:07
Show Gist options
  • Save austinkeeley/c31be13c034056c54c82 to your computer and use it in GitHub Desktop.
Save austinkeeley/c31be13c034056c54c82 to your computer and use it in GitHub Desktop.
Audio captions for "Building an Ember.js Application"
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