[Seminar] [JavaScript Frameworks: Why and How?] [Kevin Schmid] [Harvard University] [This is CS50.] [CS50.TV] Hi, everybody. Welcome to the JavaScript Frameworks seminar. My name is Kevin, and today I'm going to be talking about JavaScript frameworks, and the goal of this seminar is not to get you to, say, master a particular framework per se but to give you a broad introduction to a couple of frameworks and show why we would ever want to use a framework. Before I do that, I'll provide a little background in JavaScript, and then we'll take it from there. We're going to start by implementing a to-do list. Here's our task list for today. It's kind of funny. We have to implement a to-do list in JavaScript. This is what it's going to look like, so that's our first goal. We're not going to use a framework to do that. We're going to code JavaScript and get the to-do list to work. Then we're going to improve the design without using a framework. We're going to discuss various things we can do with just JavaScript alone to make our to-do list a little more well designed. Then we're going to throw in some jQuery, and then we're going to look at the same to-do list, just implemented in different frameworks, and we'll discuss the pros and cons along the way. Let's start implementing that to-do list. Let's say we're given this HTML. I'm going to make this a little smaller. As you can see, I have a little header that says Todo and a little box where I can enter a description of a todo and then a new item button, so let's try to enter a new todo to this list. Give a JavaScript frameworks seminar, and I'm to hit new item. I get this JavaScript alert that says implement me. We've got to implement it. Let's check out the code for this, both the HTML and the JavaScript. Here's our HTML. As you can see here, here's our little Todos header. That was that bold thing at the top, and then we have the input box with the placeholder, and then there's a certain attribute of this button that calls this function addTodo. Does anybody want to guess what that on click is signifying? [Student inaudible response] Good, the on click is sort of like an event, like clicking the mouse is just an event, and what we're doing is we're tying the event of clicking this button to execute that function. AddTodo is this event handler for clicking that button. As you can see, when I click the new item button the on click event gets fired, and this function gets called. Let's look at function. As you can see, here's my JavaScript code so far. What I have at the top is a global data structure for my to-do list. It looks like an array. It's just an empty array. And then I have the addTodo function that we saw earlier, and the only line of code in there is this alert. It alerts implement me, and then I have 2 tasks at hand. I have to add the todo to that global data structure, and then I want to draw out the to-do list. Nothing too fancy just yet, but JavaScript you may be unfamiliar with, so I'm going to go slow and review the fundamentals of JavaScript in that way. Let's give this a shot. Let's say the user enters something in this box. I just typed something in here, text. How do I sort of access that text through JavaScript? Remember that JavaScript, one of its fundamental features is that it gives us this programmatic access to the DOM. It allows us to access elements and their properties of this actual HTML. The way we do that with bare bones JavaScript is we can actually use a function in JavaScript called getElementByID. I want to store the text that's typed there in some variable, so I'm going to say a new variable called new_todo, and I'm going to get that element. This is a function, .getElementByID. And now I'm getting an element by ID, so I need the ID of that text box, so I've given it the ID new_todo_description. That's how I'm going to get an element. That's my argument to this function, to specify which ID to get. And so that's an element in HTML, and it has properties. You've seen these. They're attributes. The attribute of the text element that stores the user's input is called value. I saved the value of that text box now in this variable called new_todo. Now I have programmatic access to this variable, which is kind of cool because now what I can do is I can add it to my to-do list. The way we would do this in JavaScript—and don't worry if you're unfamiliar with this, but just going through it is todos.push because that's the name of my global data structure up here, and I'm going to push new_todo. This is great because now I have added it to my JavaScript representation of that to-do list. But now how do I get it back to the HTML? I have to find a way to sort of push it back. In other words, I kind of have to draw this. What we're going to do is we're going to draw the to-do list. I need to update other HTML on that page, and as you can see, I've left this little container here, this divider of the page whose ID is todos, and I'm going to put the to-do list there. First I'm going to clear it out because, say, there was an old to-do list there. I'm getting that element by ID again, and I'm accessing the inner HTML of that element, and I'm going to clear that. If we left this code as is, we'd see a blank nothing there, and now I want to start rendering my new to-do list. I'm basically going to wipe out my to-do list. Now the inner HTML inside of that todos div is completely clear, and now I need to start adding my list. The first thing I want to add back is the unordered list tag, which basically denotes that this is the start of an unordered list. Now for every element in my todos array I want to print it out inside of that HTML. I want to append it on to the bottom of this list. Just like in C, I can use a for loop, and I'm going to start at the beginning of my list at element 0, and I'm going to go all the way to the length of the list. We can actually get the length of an array in JavaScript using the length property. Basically I'm going to do something very similar inside of here to print out that element. I can again access the todos div, the inner HTML property of that, and I'm going to add on this new list item, and that's going to be surrounded by this li tag, and I'm going to concatenate with the + operator, and that's the ith element of my todos array, and then I'm going to close that tag. Now for every element we'll add a new list entry. And then all we really need to do is close off that tag. I just need to close off that unordered list tag. Do you get a feel for how that works? This opens the entire list. This adds individual elements from the todos list to the list, and then that closes the entire list, and this is my addTodo function. I basically begin by getting the todo from the text box. I add that to the todos array, and then I re-render the to-do list. Now I can add items to my list. This is kind of exciting because in just a few lines of code we've basically made a to-do list where we can add items. Great. That's kind of a basic introduction to JavaScript. Don't worry too much about the syntax for now, but think about this conceptually. We had some HTML. We had a text box on the page that basically allowed users to input a to-do item to add. And then we used JavaScript to fetch that todo from that text box. We stored that inside a JavaScript array, which is basically like our programmatic representation of that to-do list, and then we printed it out. This is todos.js. This is kind of cool, but how can we take this further? Well, as you can see, this is not like a complete to-do list. For example, I can't mark any of these items as incomplete, like if I wanted to reprioritize the items or delete items. This is okay, but we can take this further. I'm not going to talk too much about adding extra features, but we could take that further. Let's talk about adding one more feature to this to-do list, which is going to be being able to check an individual to-do item and have it be crossed out, so basically saying I've done this. Let's look at some code that could accomplish that. Notice what I've done at the top is I've added a new global array called complete. I'm basically using this to store whether the items on the to-do list are complete or not. This is one way to do this. If I look at the implementation of this, the display, basically if I enter a todo and I press this toggle button it crosses out, so every item on this list has either a complete or incomplete state, and I'm using another array to represent that. Basically for every todo in that todos array there's an item in the complete array that basically indicates whether that is complete or not. I've had to make pretty minimal changes to this code, so here's our addTodo function. Notice that here I'm pushing it onto the array, and then I'm pushing a 0 to that complete array, basically in parallel with that new todo push to say I'm adding this item, and it's coupled with this value, which means that it's incomplete. And then I'm redrawing the to-do list. Now, notice I've added this drawTodoList function. This takes a lot of the code we had before, basically clears out the box and then draws the new to-do list. But notice that inside of this for loop we're doing a little more now. We're basically checking whether the item corresponding to the ith todo here is complete, and we're behaving differently in these 2 circumstances. If it's complete, we're adding this del tag, which is basically the way you can get that strike through effect crossing out the to-do list if it's complete, and if it's not, we're not including it. And so that kind of takes care of that, and that's one way to accomplish this. And then notice when the user clicks one of these we toggle the completion status of it. When the user clicks, we'll reverse whether it's been completed or not, and then we'll redraw it. This kind of works. We have these functions that carry out their own tasks, and this is okay. Is there anything we could do better about this, though? Notice we have these 2 global arrays. If this was C, and we had 2 arrays that kind of represented data that was sort of related in some way what would we use in C to combine these 2 fields into something that encapsulates both pieces of information? Anybody want to make a suggestion? [Student inaudible response] Exactly, so we could use some kind of struct, and if you think back to, say, pset 3, remember we had dictionary, and then we had whether the word was in the dictionary, and all that information was put together inside of some data structure. One thing I can do with this code to avoid having these 2 different arrays for similar pieces of information is I can combine them into a JavaScript object. Let's take a look at that. Notice I only have one array at the top now and what I've done is—and this is just the JavaScript syntax for sort of creating a literal version of an object, and notice there are 2 properties, so we have the todo, and it's kept together with whether it's complete or incomplete. This is very similar code. We're using the JavaScript objects. This kind of improves things. Like now, all these fields of related information are kept together. When we go to print it out, we can access the fields. Notice how we're doing todos[i].complete instead of checking the complete array separately, and notice when we want to get the to-do string we're getting the to-do property of that todo, so this kind of makes sense because every item has these intrinsic properties about it. It has a todo, and it has whether it's complete or not. Not too many changes there functionally, just added some more to the code. This is an improvement on some fronts, right? I mean, we factored out the design a bit. Now we have objects to basically encapsulate this data. Is there anything more we could do from here in terms of JavaScript? Like notice that this code right here for getting the inner HTML of a div is a little, I guess, long. There's document.getElementByID("todos").innerHTML. One thing we could do to make this code look a little friendlier so I wouldn't have to keep scrolling left and right, back and forth, is I could use a library like jQuery. Let's check out Seminar 2, and this is the same code, but it's done with jQuery. You may not be too familiar with jQuery, but just know that jQuery is sort of a library for JavaScript that makes it easier to do things like access individual elements of the DOM. Here instead of saying document.getElementByID("todos").innerHTML I can use the much cleaner way in jQuery, which is just to use selectors. As you can see, this code did get a little cleaner, very similar functionally, but that's the idea. We've seen a couple of things so far, so we started with just raw JavaScript implementation. We added new features and showed how we can improve it with just what we have in JavaScript. Does anybody see any difficulties with this design? Namely, I guess—or not necessarily difficulties but let's say we weren't doing a to-do list project, and tomorrow we decided we wanted to make a grocery list or a shopping list project. A lot of these features are very similar. A lot of the things we want to get out of JavaScript are very common, and this underscores the need for some kind of way of making this easier to do. I had to build up all of this HTML access, all this DOM access, like I'm going to represent the to-do list with this model. And notice I'm responsible as the JavaScript developer for keeping the HTML and JavaScript that I have in sync. Nothing automatically made that JavaScript representation or the to-do list get pushed out to HTML. Nothing enforced that except for me. I had to write the draw to-do list function. And that may not be—I mean, it's reasonable to do that, but it may be tedious sometimes. If you have a larger project, that could be difficult. Frameworks, one of the purposes of frameworks is to simplify that process and sort of factor out these common—I guess you could say—design patterns that people generally have some kind of way of representing data, whether that's a friends list, whether that's map information or something or a to-do list. Some people have generally a way of representing information, and they generally need to keep that information sort of in sync between what the user sees in some kind of view, speaking in terms of like the model view controller that you saw in lecture, and then the model, which in this case is this JavaScript array. Frameworks give us a way to solve that problem. Now let's take a look at the implementation of this to-do list in a framework called angularjs. This is it. Notice it fits on a slide. I don't have to scroll to the left and right. That probably isn't a great reason to recommend using a framework, but notice am I ever accessing individual HTML elements here? Am I ever going into the DOM? Do you see any document.getElementByID or something like that? No, that's gone. Angular helps us keep the DOM and our JavaScript representation of something kind of in sync, so if it's not in the js file, if there's no way of programmatically getting to all that HTML content from the JavaScript how are we keeping this in sync? If it's not in the .js file, it's got to be in HTML, right? This is the new version of the HTML file, and notice we've added a lot here. Notice there's these new attributes that say ng-click and ng-repeat. Angular's approach to solving this problem of difficulties in design is to basically make HTML much more powerful. Angular is a way of allowing you to make HTML somewhat more expressive. For example, I can say that I'm going to tie or bind this text box to a variable within my Angular JavaScript code. This ng-model does just that. That basically says that the item inside of this text box, just associate that with the variable new_todo_description within the JavaScript code. That's very powerful because I don't have to explicitly go to the DOM to get that information. I don't have to say document.getElementByID. I don't have to use jQueries like DOM access. I can associate it with a variable, and then when I change that variable within JavaScript it's kept in sync with the HTML, so that simplifies the process of having to go back and forth between the two. Does that make sense? And notice there's no HTML access code. We've just made HTML more powerful, and now, for example, we can do things like this, like when you click on this, call this function within the scope of todos.js, and we could do that before, but there are other things, like this ng-model, and notice this ng-repeat. What do you think this does? Here's our unordered list from before. We have the ul tags, but am I ever rendering that list inside of the JavaScript code? I'm not ever explicitly rendering that list. How does this work? Well, the way Angular accomplishes this is this is called a repeater. Basically this says that I want to print this HTML for every todo inside of my todos array. Inside of todos.jr there is a todos array right here, and this will tell Angular go through that array, and for every element you see I want you to print this HTML. This is kind of awesome because I can just do this without having to write a for loop, which for a to-do list that was only 30 lines of code may not be the most beneficial thing, but if you have a large project, this could get very convenient. This is one solution to this problem, making HTML more powerful, and that allows us to keep JavaScript and HTML in sync. There are other possible ways to solve this problem, and not every framework does this. Not every framework works along these lines. Some frameworks have different approaches, and you may find that you enjoy coding in one framework over the other. Let's look at one more. This is the to-do list coded up in a framework called Backbone. I'm going to go through this quickly. I'll start with the HTML before we go there. One second. Starting with the HTML, as you notice, our HTML is very similar to what it was before, so not too much new on that front. But our js file is a little different. Backbone kind of has this idea, or builds on the idea that a lot of what we do with, say, our JavaScript projects is think about models and collections of these models. This could be, for example, a photo and collections of photos, or the idea of a friend and collections of friends. And oftentimes when we're programming JavaScript applications we'll sort of represent the idea of having a collection of friends somehow in JavaScript, and Backbone gives us this layer on top of JavaScript's existing arrays and objects to do more powerful things with that more easily. Here I've defined a to-do model, and you don't have to worry too much about the syntax, but notice that what's one of the properties of this? It has a default field. Backbone allows me to specify already off the bat any new to-do that I create is going to have these defaults. Now I can customize this, but being able to specify the defaults is nice, and it's kind of convenient because this is not something that's like inherent in JavaScript, and now I don't have to explicitly say that the todos are incomplete. I can say right off the bat that todos are going to be marked as incomplete. Notice then what is this? Now I have a to-do list, and that's a collection. Notice the field associated with that says model todo. This is my way of telling Backbone that I'm going to be thinking about a collection of these individual todos. This is basically the model structure for my program. Here I have this idea of a collection, and basically the items contained in that collection are all going to be these todos, and that is very natural in this sense because I do have todos, and I have them in a collection. Let's look at a little more of this. Here is a Backbone view. The other thing that Backbone says is that a lot of the models that you're thinking about or even collections are going to need to have some way of being displayed. We need to render that to-do list, and wouldn't it be nice if we could provide for each model or associate with each model this view that allows us to I guess connect the two together? Whereas before we had to use a for loop that would run through every todo in our list and then print it out here we can basically connect it with this model. This is a to-do view. This is associated with the todo we found earlier. Now every todo is displayable or renderable by this to-do view. Notice some of the fields. What do you think this tagName is, tagName: li? Remember from before when we wanted to render a todo we would have to explicitly pair our todos with this li tag. Now we're saying that where this todo is going to live is going to be inside of an li tag. And now we're also associating events with our todos. Every todo has this one event. If you click pretty much the toggle button, that's what I'm saying there, then basically mark the todo as the opposite of what it was before and then re-render the application. This is kind of similar to the code before. Remember when we marked it as either the opposite or— and then we re-rendered it. But notice now this event used to be something that was in the HTML. It was sitting there. The button had an on click. When you click the button, it kind of does the stuff to set up that todo to be incomplete. Here we've associated that event of clicking that toggle button and reversing whether it's on or off with this view. This is a nice way of setting up this event so that it's very tightly bound to this view, and so notice this one more. I have this render method, and we don't have to go through the details. It's kind of similar to what we had before, but notice I'm not looping through anything. I'm not printing that ul tag that's sort of saying I'm going to print all of the elements. I'm providing the functionality for rendering this one to-do item. This is a very powerful concept because basically our to-do list consists of all these todos, and if we can basically specify the way to render one of those todos then we can have our powerful backbone per se render all of the todos by calling the render method on the individual todos. This is a concept that is useful here. Now a good question to ask is how is this application being put together? Because we have the ability to render one todo, but how do we get the idea of multiple todos? Let's take a look at that. This is the last part. Notice we have a to-do list view here, and notice it's also a view. And to go over a couple of things, this initialize method will be called when we first create this to-do list. As you can see, it's like creating the to-do list and associating it with this view. And then I added the functions here so basically when you add an item— this is similar to the addItem method we saw before— I'm going to create a new todo object, and notice I'm actually calling this new todo method, so this is provided by Backbone, and I can pass in my properties here. And now every todo that I create using this will get that functionality that we saw before. Notice I'm clearing out the text box before—a small little detail— and then I'm adding this collection. This almost seems weird because before we just had to do that todos.push, and then we were done, and this may seem more complicated for this particular project, and you may find that Backbone or even Angular or any other framework doesn't suit your particular project, but I think it's important to think about what this means on a larger scale for larger projects, because if we had a larger project where we were representing some really complex collection, something deeper than just a to-do list, let's say a friends list or something like that, this could come in handy because we could organize our code in a really convenient way, in a way that would make it easier for somebody else who wanted to pick up a project to build upon. You can see that this provides a lot of structure. And then I'm calling render on this addItem. Render, as you can see, and you don't have to grasp this full syntax, but basically for each model it's going to call the individual render method. That's sort of where this comes from. Let's just specify how to render the individual todos, and then let's glue them together as a whole. But this provides a way of abstraction, because I could change the way I decide to render the individual todos, and I wouldn't have to change any of this code. That's kind of cool. Does anybody have any questions about JavaScript frameworks? [Student inaudible question] Oh, sure, that's a great question. The question was how did I include the frameworks? Most JavaScript frameworks are basically just js files that you can include at the top of your code. Notice in the head portion of my HTML I have all these script tags, and the final script tag is the code that we've written. And then the 3 framework codes are just also script tags. I'm including them from what's called a CDN, which allows me to get it from somebody else at this point but every framework has this—you can pretty much find the content for a particular JavaScript library available on some CDN or something like that, and then you can include these script tags. Does that make sense? Cool. Those are 2 different approaches. Those are not the only approaches to solving this problem. There are many different things that somebody could do, and there are many frameworks out there. Angular and Backbone do not tell the whole story. Good luck with your final projects, and thank you very much. [CS50.TV]