Heads up! To view this whole video, sign in with your Courses Plus account or enroll in your free 7-day trial. Sign In Enroll
Preview
Start a free Courses trial
to watch this video
In this video we take the Collection that we created in the previous video and we create a View to reflect the contents of our Collection. We also learn how to use templates to define the look of our Views. here.
[?music?]
0:00
[Creating List Views]
0:02
[Jim Hoskins] So now we've defined our collection, which you can think of
0:06
as a model that's really modeling a list of models, or an aggregation of models,
0:09
and this is going to be great because we're going to be creating a list
0:14
of all of the items, so when we create a ListView,
0:17
that View can observe the collection and get its data
0:20
from our NoteList collection.
0:25
So let's actually create the View for our list.
0:27
I'm going to come down here to where we're defining our views
0:30
and we'll just define a new View.
0:33
So I'm going to start out by creating a view called the NoteListView,
0:39
and this will represent a full list of all of the notes in our application.
0:43
So we'll call it var NoteListView
0:48
and to create a View, we'll just extend the Backbone.View class
0:52
just by calling .extend({
0:59
and we'll pass it an object with all of the methods that we want to add.
1:01
So the first thing I want to do is create an initializer for my Views,
1:06
so when I initialize a NoteListView,
1:10
there's a few things that I want to do,
1:13
so we do that by creating a function called initialize: function(){.
1:15
Now, our View is going to have a property called the collection,
1:24
and the collection is going to be an instance of a NoteList collection
1:27
that represents all of our notes on the list.
1:32
Now, we're going to actually pass this collection in when we instantiate
1:34
the particular View here because we may want different ListViews
1:39
watching different collections,
1:43
but once it's initialized, we will be able to access it from this.collection,
1:45
and this.collection is a NoteList.
1:52
The first thing we want to do is bind or listen for the ("add") event.
1:55
The ("add") event on a collection is called when a new item is added to the list.
2:03
Now, in our particular case, this actually doesn't come up very much,
2:09
but this turns out to be a pretty common pattern in Backbone applications.
2:12
You want to handle the case of adding a single item to a collection
2:16
and then we're also going to handle the refresh event
2:21
which happens any time there are large changes to the collection itself;
2:24
for instance, when it's initialized and all the items are added in,
2:28
or a fetch is called and multiple items are added in--
2:31
that will trigger the refresh action.
2:34
So when we're jjust adding one, we're going to tie this to a method in our View
2:37
we'll call this.addOne);.
2:42
So this will be when we addOne, we will write the code for adding a single item into our list.
2:46
Similarly, we're going to look at this.collection
2:54
and .bind to the ("refresh") action,
2:57
which is when a large change takes place to the collection,
3:01
and we will create a method called this.addAll);.
3:05
Now what I'm going to do is just add in some blank functions
3:11
to handle addOne and addAll.
3:15
So I'll create addOne: function(){ and I'll do addAll: function(){.
3:18
Now, in these event bindings, we're just passing the method into this function here,
3:27
and this has an interesting side effect and now the function is sort of decoupled from this View.
3:32
So in a normal circumstance,
3:39
if this.addOne was called as a result of this binding,
3:41
the this inside of addOne would not be associated with the View
3:45
but perhaps the window or something else.
3:50
So there are different ways we could handle this,
3:53
but included in the Underscore library
3:56
is an awesome function called bindAll, and we're going to call that up here.
3:58
So to call it, we use the _ variable, which is globally available,
4:03
and we call _.bindAll().
4:07
And what bindAll does is it takes a list of functions and it modifies them,
4:10
so no matter how they're called, the this will always be a certain value.
4:16
So the first argument is going to be what this should be any time these methods are called.
4:21
and of course, we want the this to be, well, (this),
4:27
which in this current context is this View.
4:32
And the methods that we want to bind are "addOne", "addAll")
4:35
So no matter how we call these methods, addOne and addAll,
4:43
the this will be correct and predictable no matter what
4:47
and bindAll is what allows us to pass in simply the method reference
4:52
instead of an anonymous function that calls this.addOne
4:57
or any other tricky stuff.
5:02
So now we're totally safe to use this in the context of addOne and addAll.
5:04
It's just a handy little trick that allows us to write less code
5:10
and remove some confusing parts of how this is applied in JavaScript.
5:14
The last thing I want to do in the initialize here is make sure that my collection is up to date,
5:19
so what I'm going to do here is I'm just going to call this.collection.fetch();.
5:24
What this will do is make the collection look into the store,
5:29
grab any new items, and then it will eventually call a refresh
5:32
which will call our addOne and addAll functions
5:37
which will update our user interface.
5:40
The NoteList View represents a list of all of the notes in our application,
5:45
but each item of that list itself has to manage its own changes.
5:50
For instance, each item represents a specific note, and if that note changes its title
5:55
or any other information, we want that to be reflected in each of the list items.
5:59
So what we're actually going to do is create another View
6:03
called a NoteListItem View, and each NoteListItem View will be associated
6:07
directly to a specific instance of a note.
6:13
So our NoteListView is watching the collection of notes,
6:15
but the NoteListItemView is only concerned with a specific note.
6:19
So let's look at how we would add a new ListItem into our NoteListView.
6:24
In addOne, what we're going to do is construct a new NoteListItemView.
6:32
Now, we haven't defined that yet, but we're going to write it out as though we had
6:37
and then we'll write the code to make it work.
6:40
A lot of times when we're coding, we get into the situation where
6:44
we have two intertwined ideas, but we don't need to know too much
6:47
about how the NoteListItemView is going to work,
6:50
so let's just write it out, and then we can get our next View set up.
6:53
So when adding One, the first thing we want to do
6:57
is initialize the View for this particular list item,
7:00
so we're going to call var view =
7:03
and we'll just call new NoteListItemView({});
7:06
and to this, we're going to pass the actual instance of the note
7:12
that will be used for that particular list item.
7:15
Now, addOne takes one argument and that will be the instance of our note model,
7:18
so we'll call that (note){.
7:22
So we're going to associate this instance of a ListItemView with this particular note
7:24
that is passed into addOne.
7:30
So we'll say ({model: note})
7:32
and this model attribute for the node ListItemView
7:37
is a lot like the collection of this NoteListView.
7:40
It's really what this View is listening to and is focused on.
7:43
So now we have the View for this particular model,
7:49
and what we want to do is we want to add that View into our View as a child.
7:52
Now, all Views have an .el element, which we saw in our form View,
8:00
which represents the actual node in the document that is rendering our View.
8:05
And so what we want to do is we want to append into it
8:09
the contents of our subview or our ListItemView.
8:13
So what we'll do is we use jQuery
8:18
and we'll wrap that around $(this.el)
8:21
so now we have a jQuery instance representing the element
8:23
of the ListItemView.
8:28
And then, we're going to call the jQuery method append()
8:30
and then we want to pass it in the code that we want to append to our ListView
8:34
and that's going to be the element that our NoteListItemView will render.
8:39
Now I'm going to type in this code.
8:45
(view.render().el)
8:47
So what's going on here is we're taking our view and we're calling a render method
8:51
which we're going to write itself, and this render method is going to take the information
8:55
from the model associated with that ListItemView
8:59
and turn it into some HTML and save it to its own element.
9:02
Then, it's going to return itself, and we can get the .el property
9:06
or the element of that ListItemView
9:10
and then we'll simply append it to this element,
9:14
so we're rendering a view within a view here.
9:17
Now, that should work well enough.
9:21
The next step is we want to implement this.addAll
9:23
and this will be happening any time that the collection is refreshed
9:26
or a major change has been made to the set.
9:29
This is going to be pretty easy; we'll use jQuery to wrap around $(this.el)
9:32
and we'll empty() it out because we're going to have potentially several ListItems
9:38
already in our View and we want to clear it out before we start adding more.
9:42
Otherwise, we're bound to get duplicates if this addAll function is called over and over again.
9:46
So we empty out this View and then we can call this.collection.each,
9:52
and .each is a method on the collection class
10:00
which will take in a function and apply that function
10:03
and pass in one of the models each time it applies it,
10:07
so for each item in the collection, we want to call (this.addOne);.
10:11
So when we refresh, if there are ten items in the collection,
10:17
collection.each will call this.addOne ten times, each time with a different note.
10:20
So now we've defined the view that represents the full list of our elements,
10:31
but now we need to define the view that represents the specific item in that list
10:36
and we already decided we're going to call it var NoteListItemView =
10:39
and we're simply going to extend Backbone.View again.
10:49
Backbone.View.extend({
10:53
This view is going to be a little bit different from the form View we designed
10:56
as well as the ListView we just defined
11:00
in that the NoteListItemView is going to be generated programatically.
11:03
We're not going to be attaching it to an existing element on the page.
11:07
Instead, this View is going to be responsible for creating its own element,
11:11
which will be an li for use in an unordered list.
11:15
So the way we define things is going to be a little bit different.
11:18
Instead of passing it an el element, instead, we're going to define what kind of tagName
11:21
or what kind of class name we want to have
11:27
for the element that it's going to generate automatically.
11:30
So I'm going to define that for a NoteListItemView,
11:33
I want the tagName to be "LI".
11:36
So this means when we instantiate a NoteListItemView,
11:40
since we're not going to pass in an el element,
11:43
instead, Backbone is going to create an li element
11:46
and set that to our el element internally.
11:49
Now, let's go ahead and create our initializer,
11:53
so we'll create initialize: function(){
11:56
and remember that the NoteListItemView has a model associated with it.
12:00
Remember when we passed in the model
12:05
when we instantiate each of our NoteListItemViews
12:08
that's going to be available to us in the this.model variable.
12:10
And so what we want to do with this model is bind or listen
12:15
for the event called ("change"),
12:19
and the change event will be called any time that a property is changed in our model.
12:22
So for instance, if we changed the title, the model's going to broadcast that it changed
12:27
and we want to know about that because we want to change
12:31
how our View is displayed to the user.
12:34
So any time we call change, we're going to call this.render.
12:36
Now, again, since I'm just passing in a reference to a method,
12:42
we need to use bindAll on the render method that we're about to write
12:45
in order to make sure that this is always correct inside of the render method.
12:49
So at the top here, I'm just going to call _.bindAll(this,
12:54
and I'm going to bindAll for "render").
13:00
Our next step is to render our actual item
13:04
so we're going to create a render method:
13:10
render: function(){.
13:13
We could do this any which way; we could hard encode a string as a template
13:16
for creating our li item because basically what we need to do is to create an li
13:20
with an <a> tag and a link to the correct URL in order to dig into it.
13:26
While we could construct this using simple strings inside of our application,
13:33
we kind of want to keep this separate because that's more presentational
13:37
and View information and less behavioral, so we don't really want to start defining HTML
13:40
inside of our JavaScript.
13:45
Fortunately, the Underscore library that Backbone includes
13:47
has a templating function.
13:51
You can learn a little bit more about templating in the Underscore documentation
13:54
on the underscore.template method.
13:58
documentcloud.github.com/underscore/#template
14:00
Backbone itself integrates the template method into its own classes,
14:02
but it uses the same syntax and the same underlying code.
14:05
Basically, we can create a rendered template
14:09
by passing in a string.
14:12
We can use a special <%= syntax to inline various pieces of code
14:14
and data that can be re-rendered each time.
14:20
This is much like an ERB template or using echo tags in PHP.
14:24
Basically we have pieces of static code and pieces of dynamic code
14:30
that are contained within special delimiters like this.
14:33
So I could create that template string right here,
14:38
but what I'm going to do is we're going to go into the index.html file
14:41
and we're actually going to create our template string there.
14:44
Now, we're going to do this using a trick with script tags
14:51
and what we're going to do is create a <script> </script>
14:54
and we're going to give it a type="text/template">.
15:01
Now, that type doesn't really mean anything specific to a browser,
15:09
but what it does do is it says it's not JavaScript so don't try to parse it like JavaScript.
15:12
Basically, it allows us to create a script where we can store a string within our HTML.
15:17
Now, in order to retrieve it, I'm going to give it an id="note-list-item-template">.
15:22
So whatever you put in here won't show up in the flow of the page
15:29
and it won't be executed.
15:34
Basically, it's just sort of storing a string inside of our HTML,
15:36
and since this will be defining HTML which will be rendered as a template,
15:39
it makes sense to keep it in our HTML file
15:44
so all of our HTML code is in one place.
15:46
So basically, what we want to do in our template
15:50
is create a template for what's inside of the list item of our NoteListItemView,
15:54
so all of this code is going to be injected within an li for our View.
15:59
Basically, all we need to do is create the <a> tag,
16:03
so I'm going to create an <a tag with an href
16:06
and we'll fill that in in a second.
16:10
And then, we need to have some sort of text here:
16:13
>TEXT</a>.
16:16
And in a couple of places, we need to put dynamic information.
16:18
Now, where I want the href to go is going to actually be a detailed view of our Note
16:22
and we want to make sure that each of these has a URL that's unique to that Note
16:27
because we always want to be able to get back to that Note using the URL.
16:33
So something I'd like to do is have it be like "#note
16:37
and then the ID, so if it was Note ABC, it would be "#note_abc".
16:41
So the "#note_ is going to be static
16:47
and then we're going to use these dynamic tags, the <%= and the %> to close.
16:50
Anything in between these tags is going to be JavaScript
17:00
and the result of the JavaScript expression inside of that
17:04
is going to replace this whole piece of text right here,
17:07
so if this returns the string ABC, all of this when the template is rendered
17:11
will result down to note_abc.
17:16
Now, our template is going to be passed a variable called Note,
17:20
and we'll see that when we actually call render on this.
17:23
We're going to have note as the model or the instance of our note,
17:27
and there's a property on the note called id, which is the unique identifier
17:32
used by Backbone in the system, and that's a great identifier for us to use.
17:36
So then, inside of the <a> tag, what we want to do is we also want to put the title of our note,
17:41
so here we're going to again create these dynamic output tags
17:47
with <%= to open
17:50
and then %> to close.
17:53
Now, in order to get the title attribute, we need to use the get method
17:56
and pass it in title like we saw earlier when working with models.
18:00
We're going to take the note.get()
18:04
and get the ("title") and now the title should be part of our template.
18:08
So now that we have this template, I'm going to copy this id
18:15
because we need to get it back.
18:18
In this View for organization, I'm going to create a property called the template
18:21
and this will be the template we use any time we render the View.
18:26
In order to create a template, I call _.template($
18:31
and we'll call the jQuery function for ("note-list-item-template")
18:37
so now we have the jQuery reference, and to get the contents of it,
18:43
we're just going to .html()) so in effect, we're passing in the string
18:47
that's inside of our note-list-item-template script tag into the template,
18:51
and now we have a templating function stored in this .template.
18:56
So our render is we're going to get the jQuery reference to $(this.el)
19:02
and we're going to call the HTML function and pass it the HTML we want placed in it.
19:07
In order to actually render our template with specific information,
19:13
we'll call (this.template({
19:16
and we're going to pass it an object where the keys are going to be the variables
19:21
available when rendering the template
19:25
and the values will be the values for those variables.
19:28
Now, remember, we used the note variable inside of our template,
19:32
so we'll define note: this.model
19:35
so this note right here
19:39
makes it available in our actual template definition here.
19:43
So this note is the same as this note.
19:48
And so now we've updated our .el element with the template contents
19:52
and what we want to do is return this;
19:58
and that's because when we actually call render in the NoteListView,
20:04
we call view.render
20:08
so that means the view has now updated it to .el element.
20:10
The render returns itself so we can then access the .el element,
20:14
and so returning this allows us to do exactly that.
20:19
If you look at how this code is written, we come across a problem.
20:24
This code is all evaluated right away.
20:28
We need to be able to access "#note-list-item-template"
20:32
and if we look at how the code is included,
20:37
the application code is evaluated before any of our HTML in our body is generated,
20:40
including our template here.
20:47
So as we run it, it's not going to work.
20:49
This is a huge problem I came across, and it was tough to figure out a solution.
20:51
Basically, we need to defer a lot of our application code
20:58
until after we've generated all of our actual code,
21:02
so we need to run our Backbone code so it can read out the database
21:07
and generate dynamic pieces of information before jQuery Mobile does its thing.
21:11
The problem is jQuery Mobile will try to execute immediately;
21:17
there's no good way to defer it--at least in this version.
21:21
So where this becomes a problem is let's say we're on a page that is for a specific note.
21:24
Let's say we're looking at note #1.
21:30
Well, when we load the page in statically right here,
21:33
the div with the id of note 1 does not exist.
21:37
But as soon as Backbone loads and we do our magic
21:42
which we'll write a little bit later, it's actually going to generate the code to create the page
21:45
for note #1.
21:50
So the problem is jQuery Mobile is looking for note #1
21:52
before Backbone has a chance to actually create that page, which creates an error.
21:55
So basically, we need to be able to call all of our application code
21:59
before we call jQuery Mobile, so if we move jQuery Mobile
22:04
to the end, that's all good, but we still have the problem
22:09
of we want to be able to do things in our application,
22:12
like read in the template right here
22:16
and still have it execute before jQuery Mobile.
22:20
Now, one thing that you might think we could do is take all of our application code
22:23
and wrap it in a document.ready function like we did for this snippet of code,
22:28
but the problem with that is if we call document.ready,
22:33
all of our code is going to load after the jQuery Mobile code loads.
22:37
So again, we're going to run into that problem.
22:42
Now, the best way I was able to figure this out
22:44
is we actually need to rely on the way scripts are loaded
22:47
and at the very least, move the script tag for our application in jQuery Mobile
22:51
to the very end of the body.
22:57
Now, generally, I don't like to do this because it's going to lead to a flash of unstyled content
23:02
when we first load up until the application code is able to run,
23:08
but it's an evil that we can get away with.
23:12
Now, for simplicity's sake, I'm actually going to move all of our script tags
23:15
to the bottom of the page
23:18
because we can solve all of the other problems simply enough.
23:20
So in this case, now we have all of our scripts at the bottom of the page.
23:24
In our application, we'll have the ability to target through jQuery
23:29
any of these script tags or DOM nodes
23:33
without having to wait for document.ready.
23:36
So if we go back to our application.js,
23:41
we could actually remove this document.ready right here
23:44
because we no longer need to wait for the document to be ready
23:49
because by the time our application.js is called,
23:52
we should have defined our new div here, so we'll be able to use jQuery just like that.
23:55
So all of this code will run before jQuery Mobile even knows what's going on.
24:02
We'll see the specific example of when this is important when we create the detail of use
24:06
for our notes and see how they are dynamically generated,
24:12
but for now, we're going to apply this code because we want to be able to use
24:15
the jQuery function right here as well as the jQuery function right here,
24:19
so now by the time this code runs,
24:25
the "#note-list-item-template" will be on the page and accessible.
24:27
And so now we've defined collections and Views.
24:35
We've done a lot of code without actually testing it,
24:38
so it's going to be interesting when we go to refresh, but now let's go ahead
24:41
and first, create our collection.
24:45
Now, I want to store this under our apps
24:48
so I'm going to create another sub-object called collections:{}
24:50
and I'm going to say App.collections.all_notes = new NoteList().
25:00
So now that I've created our collection, I'm going to create the View for our NoteList
25:14
which will use that collection as a basis.
25:18
So now we're going to do App.views
25:22
and we'll call this list_alphabetical
25:24
and we'll instantiate a new NoteListView.({}).
25:30
Now we're going to have to pass to this an element,
25:36
and we'll remember our id is ("#all_notes").
25:40
If you look at our HTML, we can see that our ul has an id of "all_notes".
25:45
We also need to pass in the collection.
25:55
All we need to do is specify collection: App.collections.all_notes.
26:00
Now, that's a lot of code, and let's see if this does anything right in our actual browser.
26:13
So let's flip over and go back to our localhost here, refresh,
26:19
and now we're getting some syntax errors, which is not unexpected,
26:25
so let's take a look at application.js 118--
26:29
probably just missed a comma--
26:33
I sure did.
26:36
Refresh one more time; didn't get any syntax errors, so let's see what happens
26:38
when you look at All Notes.
26:42
All right! It looks like we actually have a list of notes here.
26:44
We have My Note, we have the note for the one that I missed the title on,
26:47
our Third Note, our Test, and our Next Test--these are all the notes we've been creating
26:53
throughout our code.
26:57
I'm not going to click on it right now, but you can see in the status bar
26:59
is it's linking to note_e17 or the id of that note.
27:02
So now we need to create the specific pages for each note.
27:08
If I were to click on it, we're going to get an error loading page,
27:11
but that's because we need to now create pages for jQuery Mobile to actually dig into.
27:14
You need to sign up for Treehouse in order to download course files.
Sign up