What do you want to learn?
Skip to main content
by Rob Conery
In this course, we build two applications to highlight the power of Ember.js from the basics all the way through to moderately advanced topics.
Start CourseBookmarkAdd to Channel
Table of contents
Installation and Tools
Getting Started With Templates
(intro slide) So let's start things off by doing a simple layout and then just getting Ember to render right here under the main title bar. So I'll do what most people do and I will just open up a div tag here using some Bootstrap styling. And for that I will create a div and give it the class of container, which is the main container of the application. And then inside of that I'll create a row and then inside of here I'll create another div that is, oh let's say, 12 columns wide. So what I want to do is just kind of mock this out and just say, hello from Ember, with a level one heading. So refreshing just to make sure everything shows up where I want it to, yep there it is, Hello from Ember. That's great. Right here is where we want our Ember application to render itself into. Now if you've used Angular then you might do something like this, just pop in ng-app and then Angular knows what to do from there with your controllers and the scope and everything else. Ember works a little bit differently, it uses handlebars scripting templates. So you might be tempted to do something like this, creating a script tag and then setting it's type equal to text x-handlebars, that's critical. You have to make sure you set it to that. And then inside of here we could use our same level one header and just say Hello from Ember. Ember will find that, and you'll find out how it finds that in just a second, and then it'll render itself into it. And it sort of works. And except everything is shoved over to the side. Keep in mind that your browser won't render anything that's inside of a script tag. It thinks it's executable code. So how is this being rendered? And more importantly why is it being rendered shoved way over to the side not even part of the div that we created. Well let's inspect the element by right-clicking and going to inspect element and taking a look at what's going on. The first thing to notice is this body tag with a class ember-application and data-ember-extension 1. And you can see that it did the same thing to HTML. In essence Ember has hijacked our entire DOM. Why did it do that? More importantly take a look at this, div id ember231, none of this makes sense and it's completely outside of the container that we built before. It's just kind of tacked on or appended to the bottom of the page with yet more strange markup. Well here is the body tag and you'll notice that we don't have any of that Ember class information on it. So what in the world happened? Well what happened was that we assumed that just because we put the script in the right place that it would be rendered in the right place, and that is not at all what you should do with Ember. Keep in mind that Ember compiles those scripts and injects them into the place that you specify, if you don't specify anything it'll be the body tag, but we can override that by specifying a root element using a jQuery selector. And here I will give our 12 column div a id of GitHub app and I'll tell Ember that that is where I want everything to be rendered into. This is a very common pitfall when you start working with Ember because things just don't render where you think they should. And there we go. Hello from Ember it is rendered in the right place and if we drop things open you can see, yep there it is, our Ember 231 div and Hello from Ember inserted right into it. Well now that we know how Ember inserts HTML into the DOM it's time to ask ourselves what HTML do we want rendered into the DOM by Ember versus what HTML do we want in the DOM to begin with? The answer is that any HTML that is part of your application, even if it's a layout HTML, should be inside of the template. This template that we have here is our application template. I'll explain what that is and why that is in just a second, but right now what I want to do is I want to pop the styling for our application into that application template. This is something you'll want to do with Ember. You can leave an outer container, as I'm doing here, if you like, and we can use that to render into by resetting the id. The id wouldn't do us too much good inside of the template Ember wouldn't be able to find it. And there we go. You always want to have some place for Ember to render itself into and there we are, Hello from Ember, laid out exactly the way we want it using our application template. We haven't done all that much so far, we've just created an application, but as it turns out Ember has actually done quite a lot for us in the background. This is something you'll need to get used to have happen with Ember. If we flip over to the browser and go to the Ember inspector and take a look at the view tree here you'll see that we actually have a template called application. We have a controller called Ember.controller and a view, and we didn't write any of this stuff, we'll talk more about that in a second. Hovering over the template you can see that it's outlined here and the template is application, controller is ember controller, ember view. What exactly is going on here? We didn't write any of this stuff. Well that's the magic of Ember, it wrote it for us. If we don't specify the name of a template using data-template-name, then Ember will assume it's our application template by convention. It looks for the first type of text/x-handlebars a script tag that is, and it will render the application into it. If it's called application or as I mentioned if it doesn't have a name. We can also use id if we don't want to use data template name, I prefer id myself. If we refresh this, there it is, Hello from Ember. It's generally a good idea to use data template name to specify the name of your template. Even though it's a little bit wonky and verbose. I prefer id, but the team prefers it use data template name, I believe, because they're trying to avoid naming clashes with elements that might exist on the page or elements that might exist in your templates later on causing things to break. Either way, do what works best for you. Let's keep moving, of course we don't want to have our entire application inside of our application template, that's only meant to be a harness. As you might imagine every Ember application needs at least one template or else what's the point of even using Ember? So here I am going to remove this level one header and put in the name of the application. The way I build out Ember is I do it visually. I like to lay things out and get the flow of the application as I create it. And that's what I'll be doing repeatedly as we go along here. So I like the name right there and I've put in a line break so that we can put things down below in the space here. I'm not so sure about that layout, let's keep going and see what we can do. Now just to prove the point, if I create another handlebars template and I'll do that simply by saying type = text/x-handlebars and I don't give it a name. And let's just inside of here keep it completely empty. When I go back to the page and refresh you'll see down below that we have a template named application already exists. Now this can be really confusing, especially if you just happen to forget to name your template, you might not have any idea what that means. This is one of the reasons I'm showing you this error. It's quite easy to make. We do want to give our template a name, but what name do we give it? Well what we want is we want to have a landing template, something that shows immediately when the person first arrives at our application. And we do that by naming it conventionally index. Our application is our surrounding harness, if you will, and then this template is going to be the default template that shows the very first time people come to our application and load it up. So inside of here I can put indexy kinds of things, like maybe a hello or a welcome message. So that's what I'll do. I will put this is the GitHub explorer for all of our favorite devs. And we go back and refresh and, hmm well it should be showing up, because conventionally Ember should take that template and inject it right into our application. And indeed that is the case, however we haven't told Ember where we want it to go. And we do that using the handlebars tag outlet and it is much like render body and ASP.NET MVCs, razor view engine or yield if you use Rails. So now that we've told it where to render other templates, boom, there it is. You don't need to just use outlet in the application template, you can use it in all kinds of templates and you'll see that we do that later on. Looking at our view tree you can see we've got a brand new view, it's called the index view, it uses the index template. We even have a controller and yeah where did this stuff come from, we haven't written any code? But for right now notice that in our index template we just have the simple Ember.controller. That will change based on how we use that index template. If we click on the information it tells us exactly what this thing is. It is the generated index controller.
Basic Data Interactions
Using Remote Data
In the next few segments of this module we're going to get into the thick of using Ember. If you emerge from this module understanding what you're seeing, then you will have climbed the mountain and you'll have a solid basic understanding of Ember and how it works. If you're still confused, don't worry, we'll be doing it again in the next module. We'll dive face first into getting things done with Ember. In the next few segments we'll work with data and nested views. We'll run into trouble with caching, surprise, and have to think about how we're naming things. We'll link our routes together and pass along some parameters. And then work with computed properties on our controllers just a bit more. Finally we'll pick up the pace and crack out the final set of nested views for our GitHub app. (intro slide) I moved to using an object array as my model as opposed to an array of strings, as that's what we're likely to have when working with data in Ember. Here I have the GitHub login, we'll need that for the repository, as well as the developers name. Now this is such a common thing with an array controller back in your template, that Ember has a shortcut. You can just say each and it'll iterate over the model or the controller as we've just learned. Refreshing here and you can see everything works exactly as we would expect. It just iterates over the objects. Looking at our model you can see that we have object, object, object, now instead of a string. And our controller is still an array controller, that's because our model's an array our controller must be an array. Well heading over to the GitHub API, taking a look at my data in here, you can see all this lovely information that GitHub will throw down to you. We have a login, we have a name, we have an email, we have all kinds of great stuff to show, including some URLs that we'll be using later on. There is a whole lot of information that we can show here so let's think about how we want it laid out. Right now we just have the title here with a list of links down below, I think I'm going to want that completely replaced, I want the real-estate on screen. To the right here, in red, this is where I want to show the detail. So basically, I just want to transition the app from an index page to a user page with master information on the left and detail on the right. That's called changing state with Ember and you do that by changing the URL. As I mentioned before Ember is heavily URL driven, everything comes from the URL. And this is where our incantation comes in. A URL wants a route, a route wants some data, a route creates a controller, and a controller gets shoved into a template. Once again, if it helps you can think of a controller as a bit of a view model. So let's do that. First thing I'm going to do right down here is create a URL mapping. And I do that by accessing the router that exists on my application, GitHub.router.map, and that takes a callback function. And inside here I just simply say this.route and then I give it some route that I want my application to respond to. Alternatively I can use this.resource. There's two ways of doing this. This.resource something or this.route and action. And what's the difference? Well simply put a resource is a thing and a route is an action that you do to that thing. If you're familiar with Rails or ASP.NET MVC, usually resources will correspond directly to your models, don't always have to. Routes are individual actions that you might do to that model. And that's a good way to explain it for now. We'll get into it more later on, but just know that you really need to be careful how you declare this, always just go with the thing that a resource is a noun and a route is a verb or an adjective. In addition I'm specifying the path that I want to see in the URL and I'm allowing passing in of an argument called name. Up top I will use a handlebars link to syntax, this works with routing, and it will create a link to the exact route that we just created. And I'm going to pass along the object that is being looped over using this. Now I could, if I wanted to, say each dev and controller, and I could pass in the dev variable, but it's easier just to say this, as this is scoped to the current item in loop. I'll end my link-to and go back over here and refresh, hovering over. And you can see the URL is set to the name of the user. Why is that? It doesn't really make for a very good URL does it? Well let's see how easy it is to go and change it. Well why did Ember pick the name of our model in the first place? Well it's because we set name as the parameter on our route. How did Ember know how to do this? Well up above we just passed it the entire model, so it looked at the parameters that it needed to come up with and it saw a name. If we change it to login, we refresh, and you can see down below, hey it works, it's passing the login. And that's makes a much nicer URL. And let's play with scissors and go ahead and click on the route and you can see our URL changes above, but we didn't an error and all we have is a blank page. What happened? Let's take a look at our Ember inspector, doing what we do, and head on down to the user route and you can see we have a user route, a user controller, and a user template. None of these things we have made. As you're going to keep hearing me say, Ember tries to help you out. If you don't have these things, well then Ember will create them for you, which is kind of handy. So here I'll just create a template for our user and I will put in something helpful like names GitHub. Let's just refresh this and see what happens. And you can see it's empty. We have no data or do we? Taking a look at the model we actually do have some data, it's the login, and a thing called queryParam. Why did it do that? Well the long story short here is that if you specify a path with a parameter, as we've done, it will just use that parameter as the model, unless you specify otherwise. This is where things get a little bit complicated, but right now if you're running that incantation in your head you know a URL needs a route. A route creates the data and a controller and it shoves it into our template. So all we got to do is create the user route. And inside of here what I want to do is I want to use the $ getJSON from jQuery and we'll use Ember's wrapped version of it. And then I'm going to take the login from the parameters and we get our parameters just by passing those into the callback here. So I'm going to concatenate the login in and use Ember.getJSON, and you should be familiar with this right now. I will just set the data to an output variable, actually that wouldn't make much sense, because this is asynchronous. So here I'm just going to return the data asynchronously and let's just see if it'll work. And it did. Taking a look at the inspector down below and hey look at that, we have a new model in here. With a login and all the information that we get from GitHub. Well we're not quite there yet. Let's go refresh this page and yeah this is the thing that happens a lot with these single page apps, you lose data when you refresh the page. Well that's one of the neat things about Ember, it relies heavily on the concept of promises. Every time you do any kind of AJAX call with jQuery, you get back what's called a promise. And Ember sees that as anything that has a .then function on it. You can use a promise as your data source. So here, this is all we have to do, Ember will figure out the rest. We'll just return that promise and boom there is our data. That is incredibly handy. Notice that it fixed our refresh problem, since we can refresh the page now deep inside. This part of Ember is one of its shining gems and if you're going to use just straight up jQuery as your data source, then this should make you very, very happy. So let's fast forward a little bit and I'll drop in some ready HTML so that we can have our user information showed nicely on the screen and there we go, hey that's me. Here I have my avatar, I have my name, and I'm using a little bit of handlebars magic so let's go into this. We've already seen the link-to syntax, that's just creates a link based on a route, but the image here is a little bit different. It's using a thing called bind-attr. That is a little bit off putting to people who are not familiar with handlebars syntax, but if you are then bear with me as I explain it. Here we have an avatar URL that is just the link to my picture. So I want to make sure I bind that attribute source to our avatar URL. Now you'd think I should be able to do it this way where I can just set the source equal. Yuck, but no you can't. Let's take a look at to why. Inside of here you can see that Ember want's to embed a script tag using metamorphs. This is how Ember does two-way binding on the screen and in your DOM and it can be incredibly annoying. And it can also break your CSS, which sort of stinks, but it's an easy way and an elegant way to handle two-way binding all over your site. So the good news is we can fix all that by using bind-attr. You'll use bind-attr often for images and for all kinds of other things that you want dynamically updated. Down below here we're just outputting all the other information company location, email, followers, public repos, and gists, and so on. And you do that in handlebars by simply surrounding it with curly braces. Finally I want to add a link back to our main index. And we view that just by saying link-to index. And here I'll just say back. Transitioning between routes is really easy, you just have to remember their name. Clicking back and clicking through, hmm where's Scott's picture? Let's refresh and there it is, well that was strange. What just happened there? Well I'll have to investigate that in just a second. For now I want to buy myself a little bit more real-estate I don't need to have the title repeated here it's already in the top bar. So let's move that out of our application template as our application template should just be for structure. Here I think what I'll do is I will move it down into our index template and let's drop that down inside of there. There we go and we'll format it up. And there we go and let's go back to our page and refresh. And, yuck got to get used to this kind of thing of slinging the HTML inside of the templates to get things right, but hey look at that, add a little padding. And that looks okay.
(intro slide) Now let's work with the real-estate we have on the right side, where I want the detail to go. I used some Bootstrap styling, as I've been doing, and I'll create a, let's see, eight column wide div here. And let's just put something like, for now, users GitHub stuff. And inside of here we'll have one more time an unordered list with some links. It's a really good habit to get in to just mock everything out before you try and jam data in. It's much easier to solve the layout stuff that can happen with Ember without having to muscle around collection or controllers and routes and so on. There we go, that looks pretty good, users GitHub, repositories, news and events, everything is showing up. Now let's go back and we can tweak this so we can just put in his name and we'll refresh this, good that's showing up as we would expect. But this isn't exactly what we want. We want a master detail. Now the detail is going to be nested inside of the master, which is the user route. So how do you nest these things with Ember? Well you start off by nesting the actual resource itself. And you do that by adding a callback, as we've done here. Now I'll add more stuff here later on, but for right now just know that you add a callback and then boom, you have a nested route. So I'll take the landing stuff here that we had on the user page and I'll replace it with outlet. We've seen outlet already, we just used it with our application template and as we discussed then it works like the yield to keyword in Rails or the render body in ASP.NET MVC Razor. Next I'll create a template called user index and hopefully you can see where this is going. It's a very particular name that this template has, user/index, that's going to be the landing page for when people land on the user route. There's nothing more I need to do with the route path below and there's really nothing more I need to do with user index template. It should just show up. And it does, sort of. The problem that I have now is I actually have two separate routes completely. I have the user route, which is the parent route if you will, that declares a model, but I don't have a model behind our user index route. Why don't I have that model? That's a good question. Well each route might have a specific need for a specific model. My feeling is if you have a nested route like we do, well then you should have access to the parent model and you kind of do. They have a mechanism for that called this.modelFor, in other words, go out and grab the model for user and return it. To me this is a little confusing and also a little bit wonky. However, as you can see, it works. This kind of pattern is abundant in Ember. Where different controllers and routes share data. We'll see more of this later on. For now I want to get a list of repositories and you can see the URL for it right there. That URL is also given us with the API call for the GitHub user, which we have. So let's remove this link right here and I will replace it with the link-to handlebar syntax for the link. Note here that I'm also passing this, I don't need to do that because this route, as I've drawn it, doesn't take any dynamic segments or parameters, but I added it there anyway. I don't really think I needed to do that. Okay, so we get an error that's saying, well I don't have a route called repositories. And that's what I'm going to create now. I'm going to resource it because it is a thing and that's the Ember way. And I will give it the path of repositories. Now I don't need to do that either, because Ember will figure that for me. In fact I'll just use the name repositories. Now that I have that path mapped underneath and nested under user I can access it directly at user/repositories. Ember will figure all of this out for me. Heading back over to the browser and refreshing and hovering over you can see we have a URL of users/shanselman/repositories. That's exactly what I want. And clicking it, well I don't have anything, that's because I don't have a template. So, using our incantation, URL needs a route, let's create a repositories route. Next the route creates the model. Here I am going to go and grab the user model from the user route, once again using this .modelFor user. And that's what I'm doing above here. Next I am going to use the repos URL that comes down from the GitHub API, that is going to be on our user model from our user route. Then once again I'll use Ember $.getJSON and I'll just access the user.repos URL. Once again I'm leaning on Ember's ability to use promises as a bit of data. And it knows how to make sure those promises are fulfilled in the right order. Here I will rename my template repositories and I'll use some Bootstrap styling to create a strips table. And then I'll just loop over the repositories array that is given to us and I'll output the name. Let's go over and refresh, that it was pretty easy, with not a lot of code.
(intro slide) All my user bits are coming together okay, but I still have a weird problem. And that is when I click a link here the picture disappears as well as a bunch of information. All I have is Phil's name and his login, which is kind of strange, but if I refresh the page, boom I have everything I need. Why is that? This kind of thing is common place when learning Ember, you find yourself staring at the strangest behavior, you Google it a few times, and now Haa, yeah well that does make sense once you understand it. So let's figure out what's going on. Here I will set LOG_TRANSITIONS to true for our application. Every time we go between URLs and routes that will output down here to the console, so we kind of know what's going on. This is really helpful to be sure your staring at the route you think you're staring at. Well here we can see that we are indeed going to the users index. And if we go down to user index route you can see, yeah we've got modelFor, wait a minute. Hmm, how do we know what that user model is? Well taking a look here, well that's it, we have a login and we have a name. Nothing else, it' doesn't quite look like what's coming from GitHub. Let's take a look at it again and now it's completely different. This is from GitHub, we have a login we have an id, what's that other thing and where did that come from? And why did he get passed to our route? Well to understand this you have to understand a little bit about Ember. Right here in my link-to, I am passing the model as the argument. That model is this right here, our devs array, the login and a name. Hmm, coincidence, I don't think so. What Ember wants to do, if you pass an entire object, is it wants to be performant. It thinks that if you want to pass an entire object, well then that is the model that you're going to be using in your route. So, to get around this I can simply pass a string as I'm doing here. If it sees a primitive string then it won't assume that that's the model you want to display and it will go out and refresh itself. And you can see this is working. Well our applications getting quite functional and we haven't really written all that much code. I can see our users and their information, as well as a list for repositories. Let's make this thing look a little bit better now. I think it might be a nice idea to have some breadcrumb navigation. Here we can add a link back to the lists and then back to the user page itself. And let's actually use the person's name, good, yeah. And then last link here will be whatever thing we're on. I'm just mocking this out right now, but I just want to see how it looks. Hmm it looks pretty good, but we have a bit of an issue. And that issue is that we need to display the users name at the very top of our repositories template, but this template deals with repositories and our model is an array of repositories handed to us from the GitHub API. So how are we going to have access to that user information? This is where things are going to get a bit complex so let's see if we can explain it clearly. Well the first thing I need to do is I need to come on down here and take a look at how we're accessing the data. Now in our route I can say this .modelFor and a route will go out and use another route's data. However, for a controller that doesn't work. So the first thing I'll do is I'll create a repository's controller it's going to be an array controller because our model is an array. Inside here I can use the needs keyword. This means that this controller needs the data on the user controller. Now that I have that data I need to have a way to convey it down to my template. And I do that by declaring a computed property. For fans of Knockout you can think of this as ko.computed, Ember has the exact same thing, ember.computed. What I'm doing is I'm going to the controllers.user properties available to me now because I'm using needs and I'm aliasing it to user. This is not exactly straightforward, but we'll be doing it a lot. The neat thing about this computed property is if the user changes those changes will ripple down through into our controller here. You'll see that as well in just a bit. Alright well now that I have done that, I need to go up here and I need to output the users name. And let's see, yep there's Phil and I will just output user.name. Refreshing and there's Yehuda, that's exactly what we want to see. Here I'm going to remove the anchor tag and I'll put a link-to and you've seen me do this before. And this is going to go to the user route and I'll pass the user along. And then I'll go end link-to. And here as well I will link-to index. And this is going to go to the main landing page of the application. So this should work straightaway. Saving it and heading back over to the browser and refreshing, yep good, click on repositories, and back we go to the beginning. That's not so bad. Everything is showing up exactly the way we want.
(intro slide) Our next task here is to add a link to the repository itself so we can see issues and fork dates and all that other good stuff. So I'll use the link-to syntax from handlebars and I'll pass along the repository model. Remember I don't need to do that, I can pass along a string, so it's constantly refreshed, but in this case I'm going to have a full repository model anyway, so that's okay, I would just pass it along. Next let's open up a template I'm copying and paste from above. And I'll add a title here just to make sure everything's working the way I want it to. And I will just put the name of the repository and we go. Next I need to think about how I'm going to map this URL. I could have this be nested, but if I do that I need to figure out a way where I want it to be displayed on the page next to all the repositories. I don't want that, I want it to replace it entirely. So I'm just going to create a brand new resource at the same level. And this time I'm going to declare a path and it's going to be repositories and then the name of the repository just like this, but in thinking about this we're looping over summarized information about the repository. If I pass that model into this URL here, then that's going to be my model, and I don't want that. I want the full information to come down from the API. So what I'll do is I'll rename this to reponame and then I'll use that parameter, passed in, so that I can go out and grab a snapshot of the data I need from the GitHub API. Alright we have a URL and using our incantation, we know that a URL wants a route. So we will create the repository route. A routes job is to declare a model, so that's what we're going to do next. But what are we going to do to get the data? Well unfortunately for me, well I got to construct this based on the username and also some of the information that I know about the API. So first let's start with this. I'm going to go grab the user saying this.modelFor user again, because this is a nested route and we have access to this. Next we're going to build the URL for the repo call manually. And to do that I just need the users login and I'll append that on to this GitHub URL. And then after that I will pull in the reponame off of params. And this is the API call to go get repository information. And then I'll just return a promise, like I was doing before. Here I have added in some HTML for the repo then I can display the various bits of information after the call comes down. Including a description, language, watchers, clone, URL and so on. Let's refresh this and hey, look at that, I have two watchers, two open issues. Here's the clone URL and there's a link to the website, but I don't have my breadcrumbs. So let's go back and copy and paste and we'll worry about a more elegant way of doing this later. For right now I just want to have the breadcrumbs display on the page, but I know that I'm going to have the exact same problem I did before. How do I show the users name? Well good news for me is it's the exact same solution as before, I just create a controller and I say that that controller needs access to the user controller. And I'll just return a computed alias. Note here that I put repository route, this is a spelling problem, I'll fix that in just a second. Then we can come back up here and user.name should just work. And maybe instead of repositories I can now change this a little bit and here I will add in the name of the repository we're looking at. And then here I can just do a link-to and then back out to repositories. There we go and I'll close off that link-to. Let's refresh and nothing is showing up. Can you guess why? Well as I mentioned, I missed spelled the repository route down before, so I got to change this to be a controller. It's interesting though that I didn't have everything blow up having two repository routes. No I didn't, but look at that, that works exactly as expected. And repositories name is showing up exactly as I want it to, alright, that's not so bad. So let's show some information about this repository and I want to do that down below here. I want to keep the main repo information up top. So, what this means is we get to work with nested routes. This is our little inception application, I think I'm what five levels down, master detail, detail, detail, something like that. Okay, now let's create a quick menu here so that people can click on the things that they want to see. The first thing I'll do is I'll create a link to a route that I need to create for issues. And then I'll close this thing off and just output issues. And continuing to go slightly out of order from our incantation, but let's create the template since I'm right here. And I will call it issues. And of course I have to call it issues, I don't have a choice. It needs to match my route. And then down below here I need to create the URL. What I'll do is I will nest this inside of repository. And should I do route or resource? Well once again this is going to be a noun, it'll be a thing, that's going to be a list of issues. So therefore, I want to make sure it's a resource. So refreshing and there it is and boom it just shows up. Now think about how hard this might be in something like Angular or Backbone. With Ember it's actually quite simple. So correcting my bad CSS, let's then copy and paste this and go a little bit faster. I want to show a few more things, like forks and possibly also commits or not. And here I'll just create forks and commits as individual routes. So we'll come back down here and create those URLs as well, forks and commits. And since those are nouns as well, I am going to leave them as resource. Here I will create forks and commits and then same here, I will output the stuff so we can see this all work magically and wonderfully. Refreshing and boom, forks, commits, issues, forks, commits. You know, Ember sometimes seems complicated, but when can whip this kind of stuff out that quickly with not so much code, it becomes pretty inspiring. Alright, we have our URL for our issues and URL wants a route. A routes job is to declare a model, but how are we going to go grab the issues, I got to return something? Well what we need to do is we need to go grab the model for the repository. You can do that because this is nested. And on that repository is this issues URL. Thank you GitHub for having a really nice API. So here I can just say go grab the issues URL and now just Ember $.getJSON URL. However before I do that if we go and take a look at this URL you can see at the very end it's given me a place, a flag if you will, to put in a parameter for a particular issue number. I don't want that on here or else it'll break. So let's replace this and this'll give me a list of issues. I'll just strip that right off. And you can see how that works by doing that. And so now we can see all this information about every given issue. We have title and we have the avatar URL of the user and we also have created that information and so on. So I can use that in my list above. So here let's lower this down to a fourth level header and I'll just loop over using each one more time. Since our model is an array, it's going to be an array of issues that are brought back to us from their JSON call, well I can just loop over it with each. If I refresh, ah ho, I've got a problem. I'm moving a little too fast. Can you see what I did? There we go, I put source = bind-attr, that won't work. And I still got another error, oh well. I forgot to close off each, that's okay these things happen. Refreshing and now I get something interesting 410 gone, now why would that be? Well let's go back and take a look, of course biggy, that's my repository not Scotts, Scott's not in charge of issues. Well let's go over here and take a look at biggy and issues and you, whoa whoa, that works it's just a little bit large. The good news is that if we want to change something we certainly can, we can just drop in a style tag right here. And set the width to 140 pixs, hopefully that'll look a little bit better. Cool, so now we have a list of people that have given us issues. Let's take this down to 120 pixs and see if it looks a little bit better, it sure does, but we have the issue and we have the avatar, but we don't really have a title, that's because I misspelled title. There we go, Tiny fs, goodie these are all the issues from my repository, biggy. That was a lot to take in, so let's take a second and review what we've seen. As I've been saying sometimes the only way you'll retain Ember's working details is to use brute force memorization. As such, repetition is your friend. So let's repeat everything we've just seen in summary form. The first thing we did was work with jQuery to pull some data from the GitHub API. After some initial goofing around we found that Ember works directly with promises. This means all we need to do is to return the call from getJSON and we're good to go, Ember does the rest. Including loading the nested promises for us in the correct order. We discussed briefly the notion of resource versus route when laying out your URLs. We'll get into this a bit more in the next module, but the rule of thumb you want to follow is that a resource is for a thing or things and a route is for something that you do to that thing. Or it can be an action in general, like login. One rule of thumb people find useful, is the Yehuda Katz's law of Ember routes. If your UI is nested, as ours is, then your route should be nested and that's good advice. We found that Ember js leans heavily on two of the hardest things in computer science, caching and naming. This is good in that it does these hard things for us and it's bad in that the team is working their way through this exercise and there are often weirdnesses. Like the one we encountered when passing a model to a route versus a primitive string. Passing a model to our route meant that our call to the GitHub API was completely ignored as Ember thought we already had a model so why did they need to get one? Passing a string, however, forced Ember to go out and get the data, as it figured we needed the model, and it was right. We were introduced to the notion of computed properties, properties on our controller that aren't part of our data, but still need to work with two-way binding. We've seen two ways to do this, using the property flag as well as some sugar from Ember with the Ember.computed namespace. Here I'm telling Ember to alias fork as forked, so we can use it in our template directly. We'll get more into Ember's computed bits in the next segments. Finally we got into the nested flow, pushing out issues, commits, and forks. Our incantation served us well as we were able to structure up a pretty impressive master detail inception style application.
In the following segments we'll work more with the UI and handlebars in general. And we'll finish up our GitHub explorer by pushing some data to the server. We'll create helpers for working with dates, send data to the server with jQuery's post, and we'll discuss needs versus modelFor just a bit more. Finally we'll work with computed properties a bit more as well. (intro slide) I want to output some data information for our issues on the screen and for this I'll use the moment.js library. And you can go check it out at momentjs.com. This highlights an interesting thing about Ember in that they don't try and do everything for you. They feel that if have specific output needs, just go grab a library to do it. Angular is a little bit different that way, they have a lot of output filters that you can harness in there, localization, pluralization, dates, and otherwise. Anyway, I'm going to add moment js to the page here and next I will go on down here and I will register a helper for Ember. And this is the way you attach other libraries right into Ember and handlebars templating. So I'm going to call this helper from date and I'll use moment to calculate the difference between the time the issue was added and today. And to do that you just instantiate moment and then you can instantiate a second moment passing it the date. And then you can just say target.from today. And of course I could have formatted the date anyway I wanted to but I like to see date difference as a display of time on an issue, how long ago was it added in? And there it is, that looks great, super easy to plugin. Well let's go and add forks in now and I will do the simplest thing, which is just to copy and paste our issues template and rename it forks. In here I'll just say forks. And then we're going to do the same thing, we'll have a stripped table and we'll loop over it, but let's go take a look at forks just to see how it's formatted. So in here we have the full name of the repo, so we'll want to show that, as well as the owner information. It's almost the same as my issue list, but not quite, so I'll have to go in and change some variables. Here I'll just change user to owner and instead of title I'll change it to full name. And then, as you might have guessed, we're going to need another route. So we'll need a forks route and this is going to go grab the model for repository. And in here we'll use the forks URL. So taking a look at the URL we actually don't have that name replacement so I will just use the straight up forks URL, just like that, two lines of code, and I like that a lot. So let's refresh this and oh we've got a problem. Yeah, it's because I already have a template called forks, whoops, that is the fun of copying and pasting. So refresh and there we go, nice. I can now see who forked my repository and when and I'm using the exact same handlebars helper. But what if a repository doesn't have an issues list? So let's go back to Scott Hanselman's issues list for biggy. He forked biggy for me and as you can see it's giving a 410 error, because it doesn't have an issues list, it's a fork. Well taking a look at the API for repository we can see that there's a flag here that sets fork to true if it's a fork. We can use this in our template and have a conditional output. So let's set that up. So here what I'll do is I'll use handlebars again and say unless forked and we don't really have any thing called forked, as you can see. There is no variable called forked, so it doesn't really quite know what we're talking about. So what I'll need to do is I'll need to go down here to the repository controller. Remember a controller in Ember is often times thought of as a view model, at least with me. So here what I'll do is I'm going to use an ember.computed.alias and have it look at fork. And so now since fork is true, forked is also true. And it's a property on our controller and we can use it in our template. We could also use some more of the computed properties here, we could say not fork. And then we can change this up here to be if not forked. And refreshing and it still works. I like it the first way better with the unlist stated, so we can just alias fork and say if it's forked then show the stuff below. And then what we'll do is we'll just hide the menu. Otherwise, and you can use an else statement here, otherwise let's output something that says that this is a fork. And I can do this again using a muted text and I can just say this was forked and then using from date one more time and how long ago it was forked. And refreshed, there we go, forked a month ago, and that's pretty handy. Let's go over to one of his other repositories and make sure that that's going to work for ones that he owns. And it does, it shows issues, just shows the title there, but it doesn't actually show anything if he's got no issues, let's change that. You can use else in handlebars with an each iterator and if there's no data it will show else. So here we can output no issues yet if there are no issues. Forks, he's got a fork but he's got no commit. So let's do the exact same thing with commit, while we're thinking about it, and I'll just copy and paste the exact same thing down here below. And I'll change the template around. Having a look at commits, you can see that we have a sha tag for the get commit and then also we have a commit author and a message that we can show. So here we'll name this commits and then inside of here we'll put author.avatar. And change this to the sha, why not, and then down below this let's drop the message in and let's just surround this with a fifth level header, there we go. And then we can just put output the commit.message down below. Again, this all comes from our JSON pool from the GitHub array. And then while we're down here we might as well add an else out to forks, no forks yet, no commits yet, we are set. Okay, let's go back and refresh. Ah oh, this is a message you might see quite often, especially if you're going as fast as I just was. It's telling me that each loops over an array and I passed in a generated controller and that's the key thing here. A generated commits controller, now that means there's no controller. It also means there's no route. And that is exactly the problem. So let's go back and fix that once again using our incantation that a URL wants a route and a route creates the data. We're going to use the commits URL off of our repo, just like we did before, but this time we're going to have to do another replacement because the commits URL has a sha parameter that you can pass along, we don't want that. So this is just going to go grab some JSON for the commits. And that's it. So let's go back and refresh and there are the commits. And it looks like it's all Scott. Issues, forks, commits, that wasn't too hard, was it?
(intro slide) Well we've been focusing on pulling data, now let's push some. We'll set up a URL first this time and since we're going to be doing an action I don't want to use resource, I actually want to use route. Because I'm going to be creating a new issue, this is an action. Now I could nest this inside of issues and issue and the new, if I wanted to, but I think I like the way my application's flowing together. So I will keep it on the same level as the other routes. Next I'll create a template and I'll drop in some form code here, but one thing I want you to notice right away, look at the id, it's repository/newissue. Well why is it different? It's because it's not a resource, it's a nested route. This is one of those things that you're just unfortunately going to have to remember. Resources you can put in the template name directly. If you're using a nested route or just a route in general you have to make sure you add a parent name first with a forward slash. This might seem strange, but wait until we get to the user admin stuff later on, hopefully it'll make sense. Alright we'll let's add a new link down here and we will create a link-to repository and you'd think it'd be forward slash new issue, but it's not, it's .newissue, don't ask me why. That is a huge thing that you're going to have to remember. All of these naming conventions and different ways of calling things. If you ever get in trouble, just remember you can go to your Ember inspector, it'll show you all the things that you need to be naming. Oh, okay, refreshing and everything looked good. Now let's go and write the code for this. We're going to create a brand new controller for this and because it's a nested route we need to call it repository newissue. How do I know this? Well if we go on over to our routes information it tells you and there's our controller that we need called RepositoryNewissueController with new issue being proper cased. And that's what I called it right here. You can also see the names of the routes and the other template information as well in the Ember inspector. Okay, inside of here we know we need an actions hash to pick up any actions we declare in our template. So let's do that right here, we have submit issue as an action declared on our template and that is what's going to go off when our button is clicked. For now what I think I'll do is I am going to pull the title right out of our template using jQuery. And the title is, has an id of new-issue-title and the body has new-issue-body. So I'll just use jQuery to pull those two values out of those two inputs. Next I need access to the repository and I could use this .modelFor, no I can't, that is for routes, we're in a controller, we need to use needs. And then down below I can then do what I've been doing, which is just say repo is going to be a computed property and it's going to be an alias for controllers.repository. Believe me, you do this a few times and you'll get it. So now that I have the repo I need to grab the repo URL and post to it. So the URL is going to be on the repo itself, it's going to be the repo.issues URL. And I can just pull that. Unfortunately I have to say this.get repo I can't just access repo directly, because whenever you access a property on an Ember object you have to say get. Just like you do in Angular or Backbone. Well now that I have the URL for the repositories issues list I just need to post to it, that's the way the API works. So I will call post using the Ember wrapper for jQuery and I'll pass the URL we just pulled off the repo. And then I'll pass the title and body in as well. And on success I am going to do this.transition to route, this is going to force navigation over to issues, so we can see the brand new issues list. And then I'll just pop in alert, saying hey it's been submitted, but obviously I'm not going to be posting issues to my GitHub repos. That is basically how you would do it. So here I think what I'm going to do is just show you that we have the data that we need. So I'll just log this out to the screen, submit it at title to URL, and let's head back over and refresh and create an issue for Scott's all in one test repo. I don't know, maybe I should really be pushing the issues, why not? You're awesomer than peaches and I'm sure Scott will appreciate that. Having a look at the console there is the URL and there is the title, so that worked out pretty good, but we're still sitting here on this issue. So let's go fix that and transition out. So I will just say this.transition to route and issues and so go back in here and let's do it again. My issue was that your awesomer than bobcats and then boom, we have transitioned over to issues. So what we did works, but it's not really the Ember way. Let's take a look at a different way of doing this. So let's scroll back on up to our form and here is the HTML that I used before where I pulled stuff off of jQuery. While that works, it's not really the Ember way. So here let's create an input using the handlebars helper and I'll set the value to be bound to issue title, which is going to be a property on my controller. Remembering, of course, that the controller basically acts like a view model sometimes. I'll set the class and the placeholder as well and then down below I will use the handlebars helper for creating a text area. And this I will bind to issue body. This is going to implicitly create an issue body property on my controller. And there we go. So let's refresh and take a look and well pretty much nothing has changed, except at the way we're binding things. So since we've bound the value here to issue title and issue body, let's go down to our controller. And in here I will explicitly declare both issue title and issue body, now you don't need to do this, but I do want to show you that if you do you can output a default value, it'll just show up. But we can use the implicit binding if we want or we can be explicit about having this on our controller. Okay, well down below we have set title and body to be an object, but we don't need to do that of course using jQuery. What I could do is I can set a variable to this.get properties and I can pass in the names of the properties, issue title and issue body. And what this will do is it will create an object for me and stick those values onto that object. So let's refresh this and I will add in here, Ember is starting to make some sense, I hope that's true. And then down below you can see that we actually get our object out, issue title and issue body. That's exactly what we want. So I'll change the names from issue title and issue body just to title and body. That way I can use get properties and submit it straight to the API. Down below here, since these are now properties, I have to use this.get and this.set, otherwise things won't work. That's how you have to access properties on a controller using get and set, the same with a model. So submitting, seeing if everything works, and it does. Our output down below looks good and we are submitting to the proper repository. But did we do this right? Well let's just find out. I'll submit a new issue here and then hopefully we'll see the output, and we do. And we're navigated back to the issues list, but let's go to repository a totally different one and we have the exact same form. What's going on? Well the short answer is the controllers will cache their property values. So we can't really do this. Another way around this is to create an Ember object, this is one way of creating a model explicitly. You can extend a regular old Ember object and you can give it properties, as I am doing here. So we can use this to our advantage. In fact if we want to we can put some validation functionality on it using our own validation method, that's just something I made up, that's not part of Ember right there. The next step here is to make issue an explicit property of our new issue controller. And I do that by giving it the .property flag. Now this is something that confuses a lot of people. This construct right here is a little bit strange, but let's walk through it really slowly. What I'm doing is I'm going to create an issue, that is going to return every time we access issue in our template. Since I have added repo.model into the property flag here, that is going to invalidate and change the binding on issue whenever the repo changes. It might sound strange, but the bottom line is every time we change the repo it's going to force a new issue to be created. So the next thing I need to do is to come up here and change this to issue.title and issue.body. We're going to talk more about bindings in the property flag later on. Okay, so now what I'll do is I need to pull issue off and since it's a property I need to use this.get. And down here I'm just going to say issue.get title and then everything else stays exactly the same. So let's go and refresh this and I'll put in another title here that I think we're getting in kind of deep on this stuff and it's good to know Ember's got my back however. And submitting good, it's got the title going to the right URL and if we navigate over to a different repository, take a look at the new issue, it is blank. It created a new issue because the repository changed. Okay, this looks pretty good, but there's one last thing we have to do. After we submit a new issue it's probably a good idea to blank the issue property, so we don't see it again, it accidently resubmitted. Here I'm just going to use this.set. As I mentioned before you have to use this.get and this.set for properties on a controller or a model. Here I'll just create a new issue and then reset the issue property on the controller using this.set. Okay, so let's refresh and I will try this one more time, hopefully I think we have it right. So when I submit, good everything goes off, we have transitioned and now going back to new issue it is empty, which is exactly what we want to see. Hopefully you're feeling a bit more comfortable with Ember and how you might use it to build out an application. Let's quickly review the last segments here and then we'll move onto building out something a bit more complex our user admin application. We were introduced to working with helpers in Ember. Here we registered a bound helper, which means that it's aware of any changes to the data that it's working with. We called our helper from date using the moment.js library to help us format dates. This is a basic concept with Ember, let third-party libraries help with the UI formatting. Ember wants to focus on linking things together not formatting your UI. We set some data to the server, or rather faked it, in the form of submitting an issue to the GitHub API. We finished by transitioning away and back to the issues route where our new issue should have showed up. We touched once again on needs versus modelFor, which can be really confusing. Basically controllers can need other controllers and routes use the models on other routes. Now you can remember this if you recall that routes work with models. So modelFor only makes sense really for routes. We tackled the resource versus route thing, once again, and we introduced our first use of this.route. Naming things is hard in computer science and it's pretty difficult to talk about routes versus resources and routes and controllers without confusing people, especially when the API has this.route. So you'll just have to remember that these are URL mappings, that's what I call them at least, even though it says route right here. We dove into the difference between resources and routes as well. Here in white are the names of things when you use this.resource and as you can see resources have much different naming. Routes need to have the parent resource names involved with a different naming scheme based on what it is you're trying to do. Again, this is something you'll just need to memorize so I made you a nice colorful table. One thing to particular notice is that the template name and the route name are completely different, dots versus slashes. You will run into this. And once again to remind you the Ember inspector is your friend here, it can tell you all the things that you are missing and all the things that you need to name. We revisited computed properties one more time and showed how they are stackable. Here one computer property issue is using another one, repo. Finally we got to use Ember.computed once again. I like the Ember.computed namespace because it can save you from writing a lot of code. Each one of these methods is a shortcut for writing out logger computer properties by hand. I won't be going into detail here, as you can quickly read up on what each does at the Ember website, Emberjs.com. Reading through this list is a fast affair and I encourage you to have a look. Well that is it for our GitHub app, hopefully you were able to follow along and build things up with me. In the next module we'll work with Ember data and hook up an admin application for ASP.NET MVC identity. Lots to do, so let's get rolling.
(intro slide) Here's our demo site and I will be working with an MVC5 starter template, the Bootstrap one. And inside of here if you crack open models and identity model you can see that I've extended the identity user to have email, first, last, bio, and twitter. And if you don't know what that is, it's just the newest bits from Microsoft in terms of account identity and so on. So let's go and register and you can see that I've also added email to the registration page. So I've extended things just a little bit, because I want more information stored for each user. So everything is working that's lovely. And if we crack open our default connection you can see that I'm using embedded SQL Server, open up AspNetUser and it ran migrations for us, that's one of the cool things if you extend identify model, that's exactly what it'll do. In addition I have db_scripts.sql and the app data directory, run that against this table and you just right-click on the database and then run new query and it'll do the rest. So refreshing our data here in our window you can see we have a bunch of good test data. Some of the Pluralsight authors and Phil Haacke and Scott Hanselman among others. So I'm going to log in as myself and just use my name and password, the password password. So once I log in you can see there's admin and clicking on that this is where we're going to be doing our work today. This is a view inside of Views, Account, Admin.cshtml. So I'll get started just the same way we did with our GitHub app. I have added in handlebars and Ember and now I am just going to create a new user admin application, just by creating a global variable, which gets attached to window. And I'll be doing Ember.application create. Here I'll set the root element, remember we need to have a root element where our apps going to go. And I already have a div already in place for that. Next up, I am simply going to create a type x-handlebars without any id and this is going to be our application. So having jetted through this, we already know what's going on, we should see, when I refresh, everything working and indeed we do. Taking a look at our inspector we could see that we have an application view, everything is in order, that's great. Still let's copy and paste our way down, we know what's coming next, we want to have a template for the index view. So in here I'm just going to use id this time, call it index. And then down below let's create a little description of our application and we'll put it right here. Select what you want to do from below and we'll just give a menu here that's going to be an unordered list of links, user administration, administer roles, and maybe a few logs. Today we're just going to focus on users, not logs and roles, but you can fill that our later on if you like. So it didn't show up because I didn't have an outlet and that's something that I do a lot. And now that we have an outlet, there we go, we're good to go.
The User Template
(intro slide) Well now that we want to work with users let's set ourselves up with a URL. And we do that by using the router on the user admin app. Here I am going to create a resource, because users is a noun. And up here I will create a link off to users, link-to users, just that simple, we've done this before. And I'll close off that link. Coming back over and refreshing, hmm we have to think about where we want things to show up. And just like before with the GitHub app I don't want it all to show up with that title over the top, so I'm going to move the title here down to the index template. And just have a single outlet inside of the application template. So if I refresh, whoops, have a little styling problem, there we go, refresh again, and there we go there's user lists and boom, good, but we have nothing to show, it's because we don't have a template. So here we're going to create a template with the same name as our route users. And inside here we will just put a little title, I want to put last 20 users because I want to display a list of users. And I'll do this inside of a stripped table using Bootstrap styling. And here what I'm going to do is instead of working with data I want to mock everything out. So I just want to see the way it's all going to look and this is not data driven, this is just mocked out. This is a technique that I like to do with Ember. Mock everything out, make sure the templates and routes are all correct, and then plugin the data. So that's what I'm going to do here. Well logically speaking if I have a list of users and I click on one, I'm going to want to go to a specific user path. And this is probably going to be a nested thing where we just pass in the id, users, and then username, for instance. So I will set that as my resource and path. So I've created a user template here with some styling that I am borrowing from our GitHub application with a gravatar and also just basic information about their Twitter and bio and so on. So here let's do a link-to and what I'll do is I'll just say I'm going to link to the user route, just because I want to see how things are going to work. And so let's copy that down to Scott's and I'll also link up the name as well. Refreshing, and ah oh, well this is a rather verbose strange looking error, but it happens a lot. So take a good look at it, you will see it, if you see something like this and it says expected blah, blah, blah, got eof, which means end of file, then that means we forgot to close something off. And looking this over it's probably one of our link-tos and yep there it is, forgot to close off a link-to on the image for Scott. And changing that, going back and refreshing, that took care of the error. Okay, so when I click on this, hmm, nothing's working. I have the link and it's going to user route, why wouldn't it work? Well if I don't provide the parameter data then it's not going to work at all. So here I am just going to pass along bloopy as a username so I'll remember to take it out later on. And let's go back and refresh and now if we take a look at the URL, yep, users, ah oh, users bloopy. It's not exactly what I want, that's a weird looking URL. So I just go back and take out the users, that part of that. And what I also want to do right now is I want to log transitions again so I can see when we transition into a given route. Okeydokey, let's go back and just start from the very beginning, I'm going to go to the user list and you can see transition to index, transition to users, and transition to user, hmm, nothing's showing up. Hmm, can you tell why? Why wouldn't one template show up in the other? That's right, you don't have an outlet. Okay, let's refresh this and if you're thinking to yourself, hmm this isn't probably what you want Rob, right, and it's not. Now there's a handy rule of thumb that the Ember team gives you that if your UI is nested then your routes should be nested. Well in this case our UI is not nested, it is just a big fat table right here. So that means that our route's probably shouldn't be nested either. So that's a really good rule to keep in mind. So what I'll do is I'll go back down and remove outlet here, because I don't want a nested route. Next I will just remove the resource declaration up above and there we go. And instead of having a path to username, I will make sure it's users username. This is no longer nested, so it's not going to append users into the front. So refreshing, that's exactly what I want to see. Hitting back, hmm, this takes me to the index page, I don't want that. So let's change that link to point to users, the users route, that'll be the list. So let's refresh this one more time, good. And now the URLs look the way I want, I haven't had to write all that much code, short of the template code. And this is really a handy technique, if you don't have to worry about dealing with data just yet. Lay everything out ahead of time.
(intro slide) Okay well now that I'm on the user page I want to be able to show user information. So let's add another div here, just like we did with the GitHub app, I'll make it an eight column wide using some Bootstrap styling. And I want the users name to be the title. And then I'll have a hard return that will throw down a line right there, that should be good. So refresh this and that looks good, but it's making me think we have the same problem here as we did with our GitHub app. I don't want that title to be part of every single page, so let's move it out of the user template and into an index template, which will serve as the landing for the first transition to the user route. And if I'm going to do that well then I have to put in an outlet. So refreshing this, hmm nothing shows up. That's weird, I have outlet there, do you know why? It's because it's not a nested route. And again this is one of those weird things with Ember that you just have to get used to. Personally I don't like the idea of having an empty callback just to have a user landing page, but I guess this assumes that we're going to have more route mappings later on. So I guess this is okay for now. Okay, we'll going back up and refreshing and there is my index template. So once again I am looking at this and I am thinking that I don't want to have just a big fat title at the top, although it does work. I'd rather have something a little bit more functional. So let's borrow once again from our GitHub template and here I will just simply drop in some breadcrumbs. And this, as you can see, is taken directly from the GitHub application. So let's refresh this and I've updated the link name so now it says admin users, my name, very good. So in here I can just drop in a link to the index and close off that link, don't forget to do this. In fact it's probably a good idea to have a snippet in here. I should probably have done that, I'm sure someone out there has some Ember code snippets that I could use, but I will just suffice to copy and paste right now. There we go, refresh, and boom that looks good. Going back to the very first page, yes that is very functional, I like it. Alright, so what should we do on this landing page? Maybe we could just start with some basic form information like being able to edit things. So I will add in basic information and then down below I will cut and paste some ready HTML, so we have username, email, first, and so on. I grabbed this from the registration page and refreshing, yuuu that looks kind of hideous. Let's go back and style it up using some Bootstrap styling. And to do this I need to create a horizontal form and then surround everything in it, let's drop that in really quick and format stuff, there we go, refreshing. Now that looks better, but I don't want to stop right here, I want to lay everything out so I can visually see in mockup form. How this is all going to flow together, this is going to be a complicated application. I'll worry about data driven stuff later on. So let's take this and drop it below and I might have something like rules set out here. And then, yeah, let's just copy and paste the form and I'll drop in some HTML and the see Admin, Role 1, Role 2, just to see how these check box things will look in this page. Hmm. Yeah that looks okay, I kind of was thinking that if I click on any of these check boxes it'll automatically update the roles in the background, but let's also add and change password just to get the feel of this thing. I just dropped it in off camera and it looks okay, but it's not feeling very Embery, if you know what I mean. And I kind of like the idea of having these in separate URLs or separate screens if you will. So then let's go in and do that. I'm going to create one called change password and I'll drop in the HTML. I'll create another one called basic or no let's call this roles and I'll drop in the roles information. And just copy and paste, that's how we do it. Copy and paste, why not? That's how all the best developers do their stuff. So now what are we going to show on the user index page? I'm not really sure yet, I do know that I want breadcrumbs in all the other templates. So let's take those out of there and I will drop it down and let's see, admin, users, that makes sense. Let's take this and make a link back to the user index page. And I'll take this out. And remember when calling over to a nested route you have to use the dot notation here, users.index. A good handy rule of thumb is if there's a forward slash in your template you need to have a dot in your route. It's just something you're going to have to remember. So then, yeah, let's put these breadcrumbs back over here and then I do want the name. So that should work and then down below here let's put a menu like we had in our GitHub app, choose what you want to do, and then in here, of course, I'll add links off to basic info and then also roles and our change password. And just like anything when creating the mockups, you know, you don't have to worry about the naming just yet so much. And so here I'll call it change roles and then edit info, I kind of want these to be really descriptive as to what they are for. And there we go, that looks pretty good, edit info, make sure we named that template correct. And then down here let's make sure we name this change roles. And let's add some routes and these are going to be routes, why? Because they're verbs. These are things that we are doing to the resource, it's a really handy rule to remember. And because we're using routes remember there's special rules that go along with naming. If you've been watching closely then you know that I've already violated those rules. In fact I violated them a number of times up above. Extra bonus points to you if you can figure out what I've done. Alright, refreshing and boom we get an error, assertion failed, the attempt to link-to the route edit info failed. Now why would that be? Well it's saying here are the possible routes, it's very nice of Ember to help us out. One of the possible routes could be user.editinfo and I haven't found it. It's because I didn't name it that. And you have to do that for routes that are nested under resources. Note it doesn't really stop there, there is all kinds of naming rules that you're going to need to memorize. The first is if you have a route under a resource, your template has to have a slash in it. Second is any reference to that route needs to have a dot in it with the name of the parent resource. So in our case user.editinfo or user.index, which as it turns out I violated. Over here we're going to users.index but it should be user.index. Slashes, dots, takes a while to get all this, but eventually you do. So cruising around here it looks like everything pretty much works we're linked to our templates and now we just should pop in the breadcrumbs to each template. I'll do that here and then I'll just drop in the new active node and paste that in here as well and now we have change rules, good. So let's go back and feel the flow of the application. How does this feel? I like it so far, yep everything's looking right and we're navigating between templates okay, I'm liking this. So let's pull up the console and take a look at our routes. In here is another handy way of knowing what to call what. So in here you can see what Ember is expecting you to override and to create in case you want to have some special functionality. And sure enough we do. So we'll get to that in just a few minutes.
Finishing the Mockup
(intro slide) Well I've made a few adjustments along the way here. I've added a user logs and a user notes table. The notes are just for administrative notes, like set as admin, changed avatar due to complaints, something like that. And then inside of user logs, well you can imagine logged in, logged out, change password, set reminder, that's what this table is all about. So let's head back on over to our admin page and plugin our notes here on this list. This is one of the things I really like about Ember, it's really, really simple to just add some functionality in. So here I'll add a link to our new template and our new route right here and I'll call that route add note. And remember it's got to be a user.addnote, which means a template name is user/addnote. And the reason for those two things is because this is going to be a route nested under a resource. It's just a rule you're going to have to remember, because this is an action, it's not a thing, it's something we're doing to the resource. Okay, so here I am just going to add some HTML, just make it a text area. And I'm going to add a note to user.names account. And let's do this, we'll make it a form control. And then down below we will, once again, add note. And we'll set the path. Now if your path is exactly the same you don't need to set it, but here I want to have a dash, because it makes a nicer URL. There we go, add a note to someone's account. And again I will copy the breadcrumbs and there we go and then down here I will change the active note to add note, right. That looks good and so below the text area I'd like to see a list of notes and see how that looks and I can't write HTML apparently. So yeah, let's close off our text area tag and heading back refreshing, good this is what I want to see. Five days ago, here's a note, seven days ago, another note. Changing password, clicking through, functionality is starting to make itself apparent, I like this. In fact as I'm thinking about it, we might want to have notes and logs show right up on the index page. And kind of have this be a bit of a summary of activity I suppose. We could have other information in here as well, but for now let's just make it logs and notes. For a subscription site I could see like when their last payment came in, or I don't know, what subscription level they are, what rights they have, etc. Good, notes and logs, that looks okay to me. Maybe we want to have a hard return here to make it look a little separate, yeah I like that. Well once again I'm going to mock out exactly how I want this to look with data. And so I'll add a stripes table here for both the notes and logs and I'll just stub out some stuff like logged in with a username and password. Because not only can you do that, you can also log in with separate accounts using oauth and so on. So I'll also have logged in with Google. Looking at this I think that looks pretty good and then maybe I'll just add a placeholder here to add your note and yeah I like that. I think we're at the point where we are ready to start plugging in some data.
Hello Ember Data
(intro slide) Let's log into the site and do this flow from top to bottom. I log in as me using Rob Conery and password and there's the admin screen. Click on the user lists, see a list of users, great. And so, yeah, everything is looking pretty good. So let's plugin some live data. The first thing I'll do is I will create an adaptor and this is going to use the fixture adaptor, which is a fancy way of saying JSON on a page. Now I am setting this as the application adaptor, but I could just do it for the user using the user adaptor. So that's a nice flexible way to work with Ember and Ember data. So now that I have my adaptor identified telling my application I'll be using some JSON on the page, then the next thing I need to do is create my models. And you do this by using Ds.Model.extend Ds stands for data store. And we're extending a data store and model. Inside of here we're using the lovely cryptic Ds.Attr, don't ask me my why it has to be Ds.Attr, but we are specifying that each one of these things, email, username, first, last, bio, and twitter are going to be strings. The final thing in there, roles is identified as a relationship. If you're familiar with Rails than has many should be familiar to you just saying that this one user has many roles. Unlike Rails, however, it is the singular role as opposed to plural roles, don't ask me why there is that difference, if you're going to use has many why not follow through. Alright the next model I need to create is role and that has a name and a relationship back to the user model, which I am identifying with belongs to. Again a Rails idiom. And I have to use the singular form again of user. Now a few things to keep in mind here, Ember data is still being heavily developed and you can probably count on a lot of this code changing, it's just the nature of Ember. However, once you go through all of this and define each an individual property you will have a lot of power when it comes to querying, which is what you're about to see. So in our site I have a web API controller that is located at API/users that is going to feed us some data later on. For now I'm just going to navigate there and I'm going to copy and paste the JSON from that page. And use it directly right here. And I'll set those records to two variables, one called user fixtures just an array of user information from our database. And the second I will set to role fixtures as you see here. Now this isn't going to stay this way, I will move to doing a proper JSON dump later on. Right now I just want to do this in Gorilla fashion, okay. Now that I have the data I just need to pass it to our models. And I do that by using the fixtures property of our model. Which is there expressly so that we can create some test data, just like fixtures in Rails. The reason why fixtures is all caps is to remind you that it is a constant. And when you save something it's not going to go anywhere because there is nowhere to save it to, it just exists in memory. Now I'm not sure if constant really applies here, but it is a good way to remember that you're not going to be saving any data. Now you can save data and manipulate on screen, it's just not going to be persisted anywhere, if that makes any sense. Anyway, let's go and add our user fixtures variable to our UserAdmin.User model. And then we'll do the same thing with roles. So now this is going to load our models with data. So we need some data, what do we do? Now remember URL wants a route, a route is responsible for kicking up the model, but this time we're going to do something different. Instead of using jQuery to go out and do an AJAX call and ping our API, let's call down some data. Here we are simply going to access our store. And we do that by saying this.store.findAll and then tell it the model that we're trying to find. This.store is going to access our fixture adaptor and it's going to get some data for us. So refreshing we don't have any errors, that's always a good first step. Now we need to output and we do that using each. I'm going to take baby steps here as it's easy to mess up. What I first want to know is do I have all the users showing on the screen? And yeah there, good I have six records but they all have my name. So let's change that and I will put first and last output here. And good, those are all the names that we expect to see. Things seem to be working with our test data, that makes me excited. So let's plunge ahead and plugin all of our data to this page.
(intro slide) Need to replace my hardcoded GitHub avatar with the gravatar for each one of my users. And gravatar is a hosted service that uses your email so that you can have an ubiquitous avatar all over the internet. So to do this I am going to use one of the neat features of Ember called a component. The reason I'm going to make this a component is because I think I can reuse it in a lot of places. Now the first thing you should know about components is they have a very specific naming scheme. Surprise, Ember likes its naming schemes. So when you're using a component you have to make sure you call it exactly this, components/the name of your component, and that hyphen in the middle is critical. And if you don't have a hyphen then it won't work. And the reason they want you to hyphenate the name of your component is because eventually someday HTML might evolve and it might have the same name as your component and oh boy that would be a horrible clash. So let's impose this naming restriction on everyone, anyway. That is the deal. So you have to have a hyphen in the name of your component and make sure you put components forward slash in front. Here I'm just going to output an image tag with the source set to gravatar URL. What's gravatar URL and where does it come from? Well let's create our component. And here, of course the name is very important, it has to be gravatar image component all proper cased and I'm going to extend an Ember component. These things have the same style as a controller or a model, you basically set properties on it as you see I'm doing here. We're going to have the email, we're going to have size, and then our gravatar URL is going to be a function that we need to convey as a property. And that property is going to change whenever the email changes or the size changes. So we're binding this computed property, gravatar URL, to email and size. The first thing I'll do is I'll grab email and I'll grab size using this.get, because that's how you access properties in Ember, using get and set. Then I'll create the gravatar URL call, but I have to hash the email. And for that I'll need to have an additional script here. And this is what every single gravatar library has to use md5 and thank goodness there is md5.js. And with that I can say create me hex md5 email and then I can send that off to gravatar. And that's it. There's our email property and our size property and we're saying that gravatar URL is also going to be accessible as a property using the property flag there. Okay, let's head on up to our list and I'll replace this with a call to our component. And this is kind of like a handlebars helper, except we use the componentized name, gravatar-image that's how we know it's a component. And here I'm going to set the variables in line. Just like that, email and size is 500. Let's see, did I do it right? No, something's wrongs, hmmm. And noting is showing up, that's not very nice. If I go over to our view, hmm nothing looks any different and your data looks fine, hmm. If I take a look at our routes, well there's nothing I can see that's causing a problem. Sometimes it just comes down to not necessarily a problem with Ember, can you see the problem here? Well this time I can let Ember off the hook, it's just me being a bad programmer. I forgot a return statement. Of course if we don't return anything from my Gravatar URL function well then our component will have nothing to display. So alright Ember you get a pass on this one. Although I could blame you for making me concentrate so much on your API. And whoa, there's Jon and Jon and me and Phil, and whoa. That's a bit too big of an avatar for Mr. Grossenbach. Let's fix that and I'm going to set it to 80. And make it a little bit smaller, I like that, that looks pretty nice. The picture next to a username I think it's very personable.
Wiring Up User Data
(intro slide) Okay, well let's fix these links, I need to pass along the username and if you remember our issue before with our GitHub app. If we pass a string that's going to force the reload of the model, but in this case I'm not sure if I really need it. Because I have all the data on the page. There it is. It's just a username and relationships and so on. So this time I am going to pass the entire model. Because that's all I need. So let's refresh here and, uh oh, hovering over this my URL looks off. I have undefined, can you imagine as to why that might be? Well the simple answer is that since we are using a model and we're passing that in as an argument to link-to, our mapping down below here is trying to find a property with username on our model it's all lowercase. That's not going to work because it doesn't exist. What we need is a capital N in there for the proper casing and boom it works just fine. Now while that works I think it's probably a better idea to use id, so I'm going to change this here. And change the dynamic segment or parameter to be id and then down below I am going to look up the user by id. I can also look up the user by username if I want to, but Ember expects an id to be passed not an arbitrary string. So I'll show you why later on. So clicking on Jon and hmm, here's my information but this is all the hardcoded stuff, but our URL looks correct. it's a big long GUID that is the idea of the user. Okay, well let's continue on removing this hardcoded information. And for this I'm going to create a user controller, once again you can think of a controller as a view model if that helps. Here I want to create a method called full name. And this is going to be a computed property so it is going to return the first and the last name, which is a very, very common thing to do. And I have to use this.get because it's accessing the model. And because it's a property I need to flag it so that it's bound to first and last. So when first and last changed the full name will change as well. So refreshing this, yeah there's Jon, under my picture. Okay, so let's change this here our image, our gravatar image and I will use our component, gravatar image email =email and size = 200. Now if you're wondering what is the deal with handlebars sometimes you set one attribute equal to a value, like I'm doing here. Other times, like with link-to, I will just pass a string and then next to it is another string. To be honest with you I have been trying to figure this out forever and I think the difference is that one is just a constructor, it uses a constructor, and the other, like this, are variable settings. All I could really tell you it's just something you're' going to have to memorize. Oh, I wish it was a better answer for you, but moving on, hey there's Jon. Nicely sized too. So we have our gravatar, let's go and output username and down here we'll output twitter. And down below we'll output bio, as well as the email. There we go. And let's go back up and refresh and it looks like I have some weird formatting issues. Let's make this column wider anyway and I'll refresh that, yeah I like that, that looks much better. Okay, take a look at the way I am constructing this route. And you can see it is your very typical master detail route. Find a user with the params id passed in, in fact this is so common that I can delete that route all together. And using a little bit of convention here I can say that what I'm expecting is the user id and Ember will say ah ha, well then you must want a user with that id. And it will go and set them model for me completely. A little bit of voodoo magic under the hood. That's kind of nice to not have to write code if you don't' need to. Refreshing to see if it works and it does. Now all of this is hardcoded but over on the left side there you can see we have all of Jon's information.
(intro slide) Probably not going to write all the JSON data for our application by hand, hardcoded on a page. That might sound actually silly, but it's great for getting off the ground and getting started with working with data and Ember data. What I want to do instead is I want to output the model that I am sending down with this page, which happens to be an entire users dump. And I'll just use JSON.encode for the model and assign it to a variable called JSON dump. So down below I can still load the fixtures, I can use jsonDump.users and then jsonDump.roles. Where are those users and roles coming from? Well again let's go and take a look at our API and you can see that it's just an object dump with users at the top and then roles and notes and logs, which I added recently. So down here we just do jsonDump.roles, good. Alright and now that we've done that everything should hopefully just work. And refreshing the page, hooray. I guess why am I surprised, why wouldn't it work? Okay, now let's go and plugin notes and logs on the user screen. So I'm just going to copy the model for role and just rename a few things here. And I'll do the same thing for note, they're pretty simple, they're just text entries and we have a relationship that is what I would expect, okay. Then let's just output a loop here with our each iterator in the notes and logs section. And right here I'll just put note.note. And refreshing, nothing. We don't have an error either. Debugging this kind of thing is just not fun. Well fortunately for us handlebars has a helper for us called log. And we can just log the user out to the console to have a look at it. And it's undefined. Hmm, well having a look at our breadcrumbs, ah ha, there is the answer, we need to use controllers.user. Because we don't have a user model backing this view. Why is that? Well because we don't have a route for this template. Alright so that should work, but it doesn't. Hmm, can you tell why it doesn't? Let's log controllers.user out to the page one more time. And I'll refresh this and yep as I expected there is our class and we have some information. Well this time let's use the inspector. And there's the model that we expect with the right id and there's all the information we expect as well, but looking at the relationships, ah ha, has many roles, I don't have notes or logs associated with my user. I remember doing that, but not as part of the user, I did it down below as part of the logs and the notes. So to fix this I just need to add a has many relationship for log and note, remembering to keep them singular. Let's go back and refresh. Oh geez, another error. You looked up the notes relationship on user, but some of the associated records weren't loaded. Make sure they're all loaded together with omitted. Hmm, it would have been nice to read the rest of that message. Now the deal with Ember data is that frankly it's just not done. And of all the things that are half-baked and not very well thought through, it's relationships. What you're about to see is probably going to change. The loading and unloading of relationships well it's a little bit shaky at best, but let's get to it. So here I have a has many relationships with logs, notes, and roles and if we take a look at the data you can see that well that each one of them does not actually have a relationship back to the user. And this is what Ember data requires. Even though in the user data itself I have said this is one part of role and this is one of log and this one of note, this user has, we need to do the same thing for each one of the notes and logs that we return. Now this is a little bit weird to demand that of your API, but that's the deal. So refreshing and taking a look at our user information and our logs, there we go, I just had to change the API a little bit on the backend. Okay, so now we have this bidirectional relationship, but we still have this error. It's basically saying we didn't know how to load this information. So let's go on and take a look at the jsonDump that we have on the page to make sure it looks right. And indeed it does. There's our logs and our notes, so everything is coming down as we'd expect, there's no surprise here. So let's go and start at the top again and we'll pick a user, refreshing, ah let's pick me, and boom. I still get the error, so let's go take a look at our inspector here and wow we have no data, we have no roles, no logs, no notes ,but we do have a user. What is happening here? Well I'll just cut to the chase. Sometimes it's really easy to blame Ember, especially Ember data. Other times it's just your own fault. In this case I forgot to assign the fixtures for log. Now the reason I'm showing you all this and didn't edit it out, is because this kind of thing is really easy to do. This kind of error is easy to make. Sometimes it's not Ember's fault. A lot times you just forget to do something and that happens quite often with Ember. Having said all that, ahh we still have the same error. So here we have a user and that's right, but we still don't have any data. So let's bring this thing over and take a look at our user in the big inspector here. And then down below, oh roles, logs, and notes, we have the relationships and it says it's computed, which is dandy, but if we look over here at the list you can see we have 0 roles, logs, and notes loaded for my user. Okay, so what do we know? Well we know that we have the data on the page, we know that we have proper relationships for the user and the role and the notes and the logs. But what we don't have our loaded associations. If you've ever worked with an Orin before then the concept of _____ and lazy loading is probably familiar to you. With eager loading all associated records are loaded with the main record. With lazy loading all the associated records are loaded only when you need it. So it looks to me like that is the problem here. So with Ember data the way you turn on lazy loading is you send in asynch is true on your model. That tells it for this association only load it when I ask for it. Now evidently it looks like asynch is false by default, and you would think that if all the data's present on the page and all the associations are there, that Ember data would just go ahead and do it, but as I said relationships are a weak point with Ember and I'm sure that this will change in the future, they'll probably make this a little bit more apparent. So refreshing and that solved our problem. Well it seems pretty obvious to me that the lazy load is the way to go if I want to see associated data. So let's set that for logs and roles as well. Refreshing, and hey no errors. That's always a good thing. So let's go and do the same thing with our logs. And I will just iterate over the logs list in the same exact way that I'm doing with the notes. And I'll end our loop and switch everything here to read for logs instead of notes. So refreshing, okay good. So that's showing up exactly as we want. This is looking better and better. The only problem is that I'm not outputting any date information so let's go back into our server and change our API output so that we have created at. And then we'll just output the create date for the note and we'll do the same thing for the log down below. Back in my template I will swap this out so it's going to be log.CreatedAt. And then up above I will do note.CreatedAt. So let's flip back over to our page and refresh and nothing. Well let's see. Taking a look at the API, yep looking at notes and logs I see the date, why wouldn't it be showing up on the screen, isn't it part of our data? Hmm, well the answer is if you didn't put it on a model then it's not going to show up. It's not considered part of the model, so I have to come in here and I have to tell our model, that yep we have createdAt and it's ds.Attr date. And it's completely unreadable. That's okay, I've got moment.js and I also have that groovy handlebars helper that we created for our GitHub project from date. So I'll use the exact same code and just copy and paste it right here into our app and then up above, when you see I've created that, I will just say from date to parse it into a nice readable date. There we go, 18 hours ago, 18 hours ago, well the reason it's all 18 hours ago is that's when I ran my db scripts, so that's when the created date is set. And I did all that 18 hours ago. so that still works, it's okay. By clicking on poor Jon and he's got no notes or logs. So here I can handle that, as we've done before, using else. Inside of here I can just put something like, I've got nothing. Oh maybe just nothing yet. And then let's refresh. Yep that looks nice. It's always a nice idea to output something when there's no data to show. It'll help your user understand what's going on.
(intro slide) Let's make our users page just a bit more useable. By default here I'm just showing the last 50 users, but of course if you have 100's or 1000's of users then that's not going to work for you. So here I'll just drop in some ready HTML and this'll create a search form for us at the top of the page. Now taking a look at this there's nothing terribly new in here, we've done all this already. Including using the action helper, but this time we're going to put it right in the form tag and we're going to say on submit we're going to perform this action. Okay, so if we click on the Search button above and then check the console, yep down below we don't have an action for search for user. So let's create one. We do that by creating a controller. The users controller has always existed, it's just been created for us by Ember, but here we're going to override that and we're going to create it for ourselves. And we're going to specify the actions on that controller. Search for user is the function that we will create, because that's what we specified up here in our template. The other thing we did is we set the input value of our search form to be bound to the properties search term. Now I don't need to create that, but if I did want to and default it to something, that's how I would do it. Just create search term as a property on the controller. Down below let's just log that out by using this.get, I'll type in boom and boom out it goes. Okay, nothing earth shattering here. Let's remove this and let's do this properly. I will pull the search term off of the controller by using this.get and I go get the search term property. Back in my controller here you can see that my get method will accept the parameter of q, which is going to trigger a search. And here I have a horrible query and that's doing a partial string search with an or statement, yuck. You'll probably of course want to use full text searching. For this, since it's just an admin app and it's not getting searched very often, it might work for you. Well following my nose, this is probably what I would do, I would just do a jQuery get JSON call to the users and expect some results to come back and use those, I can't. Undefined is not a function. What does that mean? Well let's console.log the data out to the screen. And we'll refreshing this and I'll look for Scott, anybody. And as I expect I have two bits of data. Hmm, in looking at it, yep Scott Hanselman, K. Scott Allen. Those are the pieces of data I would expect, but why didn't, oh geez. Can you see the problem? Sometimes it's Ember's fault, sometimes it's mine. This is a scoping problem, of course. Since we are inside the get JSON callback, this has been rescoped. So we have to do ourselves equal this. So let's refresh and I'll type in boom. There we go that's exactly as I would expect to see. Now if we go back and let's type in me, and coming over here, ah boy, that's not what I would expect to see. This is even stranger because it's showing the right amount of notes and logs, three and four apiece. I's even got some weird data information but it doesn't display property. Do you know what's going on? If I refresh then yes it shows exactly the way it should be showing, hmmm. we've got a bit of a mystery. Well we can answer this simply by going and running a search and taking a look at the data returned. Notes and logs, 1, 2, 3, 4. Oh, here I am asking for note.note, note.createdAt and of course my AJAX call is going to have no idea. Here I'm overriding the model, I'm overriding Ember data and I'm saying just shove this JSON in. All the relationships, all the caching, all the management from Ember data is out the door. So the short answer here is, if you can go in with Ember data, you go all the way. Let's set Ember data up to work with the API that's on my server. Rather than work with a JSON payload. I want to go out and fetch records as I need them and Ember data's rest adapter is perfect for that. So I'll flip my application adaptor over to ds.restadaptor and I'll extend it and I'll tell it the host is a local host and I have to give this port address and I also have to tell it that it is under the virtual root of API. And I do that by setting namespace. Well that's really it, there's nothing else to do. I just have to delete the JSON payload and I have to delete the fixtures assignment and believe it or not, everything else should just work. Now it's going to seem hard to believe, but it's true. Ember data is able to work out URLs based on the resource and route names that you give it. So for users, for instance, it's going to append in the namespace and users after it. That it is the conventional way in which the Ember data rested afterwards. So let's go and see if everything works and believe it or not, it does, it just rolls over as long as you go along with its conventional settings. Having a look at that network calls yep we can confirm that it called out to our users API. Looking at the console we don't have any errors so we're good to go. Even the notes show up, that's exciting, sort of. Yuck, well they'll show up if I click through using the list, however if I do a search they won't show up, because of this call right here. So let's fix this. How do I search for a user? Well the simple answer is we just tell the store to go find the user, passing along any arguments that we need. In this case I have a variable called q and my servers expecting my call to q, so that's what I'll set. The final thing I need to do is to override the current model that's backing the controller. And for this I just say this.set model and it will be set to the results of the Ember data query. Now it's worth it to point out one more time, that the model and controller are actually separate things. Remember the model is created by the route and then passed into a controller. The controller then proxies a model, basically it takes the model, the data, and graphs it onto itself. And that is for convenience for using your template. So when I say this.set model I am changing the data. If you find all this confusing just think of the controller as a view model or as a presenter. Okay, we'll let's see if this works. So I will search on me again, there we go and hey I have notes and I have logs and good everything seems to be showing up. Taking a look at our inspector, we have user data, role associations, we have logs and notes, that's good. Now let's make this a little bit more friendly, because right now the search term is there, it's cached, which is good, but I would like to output a better title. So let's create a title property on our controller. And in here I will just check if the search term is filled out and I will output a title based on whether we have a search term. So here I will just say searching for it, this.get search term, because remember that's how we get properties on controllers, use get and set. Otherwise I'll set it to be last 50 users. Out here I will just put title. Okay, so refreshing, ahh ha, we have seen that before. We forgot a return statement, I wonder if that'll fix it? Nope. Do you know why this is happening? I forgot to make it a property. And we need to come in here and say this is a property that will change whenever search term changes. Well then we have nothing do you know why? Because I forgot to use this.get. That is one of the most common errors when using Ember. So let's formalize this a little bit more, I will just set a variable to the property's value and I will pass it here and here, good. So let's try this one more time and again I am bound to search terms, so whenever that happens that property will change. And you can see, ahha hha, very nice, good, I like that a lot. So clicking through, that looks good, clicking back, well the titles right, the search field is right, but the data isn't. I'd like the search results to be displayed instead of the default load. And for that I need to do something that seems a little counter intuitive. And for this we get to crawl into the guts of Ember for just a second. Here in my users route, I have declared what the model is or what the data should be for this route. Again that model gets proxied onto the controller. As such it's cached. So when I go and click away and come back now what I'm seeing there is the cached data. What I need to do is override this, I need to make the model dynamic. basically saying every time you come to the users route, load the model a new. And to do that we need to override the way the route pushes data into the controller. And that is done in the setup controller function. If we don't specify anything, then the route will just do this by default for us and proxy the data as you've seen. However, we can override the way this is done and to do that I'm going to have a central point where the data gets loaded. I'll create a load users function here and basically do everything that I was doing down below. And then instead of controller I will have our controller called load users. Taking a look at the set up controller function you can see that the controller instance is passed in. As is the model instance. Now that model is exactly what we are declaring above, this.store.finduser. If we leave it this way then we'll have the same data show up that we've been seeing. So I'm going to get rid of that and let the controller set its own data down below. Okay, we'll let's see if this'll work. And yeah well it looks good so far. And there we are. Well I'm not sure if this is a very good idea, in fact Ember people out there might be throwing things at their computer screens right about now. But I will flag this for something to go over in code review. As long as it works I'm happy with it, but it might not be the best possible way of doing things. Anyway moving on. Back in my API controller I have a parameter that is defaulted to 50 and this limits the amount of results returned. So I'm going to pass in three, and good that works. I want to make sure that the actual data from the server is being returned instead of some cached model here. So I'll search for a model that's not there, me and yep it shows up, that's good. And going back, good, it's exactly what I want to see.
Creating and Updating
(intro slide) We're now ready to lean on one of the nice features of Ember data, which is saving data back to the server. So here we are on the edit page and you can see that we don't have a model and our controller is just your basic ember.controller. Of course we're going to have to change all that. So I am going to change this to a UserEditinfoController. And this follows along with our route naming and our template naming, but there's no dot or slash here it's just UserEditinfoController thrown together in the German sort of language way. Okay, so we've done needs user, like we've done before, and now we have breadcrumbs if they work, which now means we also have a model. And that means I can go and plunk that user data right in here into our Bootstrapy form. I'll do this using handlebars input helper and I'll set value username and I'll also set the class to form control. And I'll just cut and paste my way down bio, twitter, first, last, all of that and nothing shows up. Hmm. Do you know the error that I made? It seems right, a bio, we forgot controllers.user again, remember we're using need so we don't have the concept of a user directly, we have to access it through controllers.user. It basically just sets up a sim link if you want to think of it that way. There we go. So now pasting all that into place and refreshing there we go, there's the data and this is one of the cool features of Ember to a data binding all the way across the board. We don't need to do it this way, in fact a lot of people find this annoying when they're playing with an editor. I kind of like it, because then you can see the display, make sure that it's showing up correctly. However if we didn't want to do it this way, can you think of a way around it? One thing we could do is just create a new user property on the backend of our controller and then set the values based on what's passed in on our user model. And then we're just working against the newer user. Given that we're editing things however, I like to keep it this way, I think it seems pretty reasonable. Well now we just have to create a way to send the data back to the server and I'm just going to lean on good old HTML forms. I'll create a submit button here for our form, and then head up to the form and then set an action. And here I will call it save user. And then again we'll set this to be on submit. Again you're just going to have to memorize that handlebars syntax, it's just the way it is. Action, action name, and then what event you want it to be working against, okay? Clicking on Save, oh boy. We get a deprecation warning again. Using a quoteless parameter with action is deprecated. Please update to the quoted usage. Hmm makes sense, I suppose, why not strings and strings. Okay, so let's go back over and then we will just put quotes around this, it's nice of Ember to tell us that's what it likes. And then down below let's create our actions hash. And in there we'll put our save user function. Now you might think this code would be complicated but that's it. We just have to grab the model and save it. Remember it's already updated because of our binding. Call save, Ember data will do the rest. So let's see that, I'll change my name here and then down below in the console take a look. We have a put statement. That's what we're using for our edits, put, HTTP put. So that's great because that works exactly with our web API backend. So here's our put method and what I'll do in here is I will change this around to be using dynamics. And then pull in the id another dynamic model and then run explicit updates. I like doing explicit updates like this because it avoids mass assignment. And also notice at the very end here that I'm returning that model, that's what Ember expects, if you don't do that you'll get confused. So running save again, there's our responses. And everything looks right. No errors. For all the quirks of Ember and Ember data, I have to say this is one very pleasant surprise. Now let's go and work with adding notes to a users account. I want to be able to add a note and have it show up in the list immediately. Right off the bat I have a problem and that is that our list is sorted in the wrong order. I can fix this by going back to the API controller and then sorting the notes by descending order based on created date. That would work, and it's an easy way to solve this, but there's a different way we can do this using Ember if we wanted to. And this comes in handy if you don't have control over your API as I do. So in my add note controller I can add a switch here called sort properties and this is a built in thing, Ember array controllers, that allows you to sort based on some property in the backend array. Here I don't really have a backing array, but let's see if it works if it tell it I want a sort on user.notes.createdAt, nope it's not working. At least I don't think it's working. Sorting by a year ago, then six months ago, hmm. Well this is my problem, the add note controller is just a simple Ember controller. It's not an array controller so sort properties is meaningless. Normally you'd be able to control the sorting order by putting in sort ascending false and it would do the proper thing. Well here, as you can see, nothings working at all. Well there's a couple of ways around this. The first way around it is to set up a notes controller. And then that would be an array controller and we can extend that and then drop in our sort property and sort ascending just right in here and that'll work just fine. All I need to do now is to create a notes template and to render it inline, none of this we have talked about just yet, and I will I promise, but that would solve the issue. Create a controller, create a template, as you see here, and everything is good. But all we're doing is just wanting to sort so this is a bit of overkill so let's see option two. And that is to create a notes property and have it be a computed property. Now if you're used to using Knockout then you're familiar with computed properties at ko.computed. And that returns a function of something and usually based on the items that are passed in. Well Ember goes a step further giving you these helpers, sort is one of the helps, there's any, there's not, there's a few that we've already seen. In this case I'm going to use sort and reading the IntelliSense here, provided by Visual Studio which is awesome, I tell it what I want to sort and then what I want it sorted by. Again I'm going set it to a notes property. This means that I put my template instead of going and iterating over controllers.user.notes, I'm just going to iterate over the notes property. And I get an error, hmm, created at is not an array. Well now that's weird. Hmm, taking a look at our code down below, well yeah it's a string, let's just make it an array shall we? It looks like it needs an array as a second argument to sort, well that's even weirder yet. Created at is not an array. That's very frustrating. Well what we need to do is to set sort properties as a property on the controller, we can't just pass in an arbitrary array. Here we'll just place this with the string sort properties and refresh. Yep it works. Well that look sorted, but it's sorted exactly the way we had it before. What I need to do is I need to tell it it's in descending order. And so if I refresh, there we go, the notes are sorted from newest to oldest. Well in my mind it's not the most elegant solution and it's also a little bit wonky. However we've saved ourselves from writing separate template and saved ourselves from having a dedicated controller that's just there for sorting. You'll find a lot of stuff in Ember like this. There's always ways to solve a problem. The more you get to know the API well the easier it becomes. Well let's work on adding a note now and I've updated the form here to have a submit action of save note. And I've also added a submit button down below for add note. Let's refresh the page and you can see the new HTML, great. So now if we click add then we get the familiar error, there's no action called save note. So let's add that. And in here I'll use jQuery to pull out the note again I don't have to do this and I will be changing in a second. And I'm also pulling the user off of controllers.user as above I have neat and I can have access to controllers.user. Next I need to create a new note using Ember data. And I do that by using this.store.create record. I need to tell it what kind of record I need to create, which is a note. And then I get to pass in some initialization data. All I need to really send in here is the note body itself and what user it's associated to. Finally I'll call save, seems pretty simple. Let's see if it's as easy to do a new record as it is to update, oh boy. Nope, hmm, you can only add a user record to this relationship, okay then. (waiting) Well I am and I'm specifying that it's a user, oh boy. The way to get around this is to access the notes property on the user and push the new note onto it. By doing that the id will be set for the user on the new note. So hitting Save then post is called on our API controller and hovering over the model you can see there's the data, this is a note and the user id is indeed set as it should be. And boom, there it is, it shows up right in our list. That's pretty cool. Alright well now that we have this what we need to do is we need to of course add the note to our database. So I'll do this using a little entity framework kung fu and then here I'll just pull out the user, sign the note, and so on. Nothing really too earth shattering in there, and let's refresh and see if it works. So I'll add a new note to my account and it shows up there and we got no error, that's good. And refreshing, there it is.
(intro slide) Well I've put this off long enough, let's deal with these breadcrumbs. The UI is coming together, but I don't like having the repetition everywhere. And I think we can do better. So, let's make sure we're not dealing with _____ scripts, but instead we'll be dealing with handlebars. And I need to set the id of this template to _breadcrumbs. Now that underscore is really, really important. As I keep saying you're not allowed to name things arbitrarily with Ember, that underscore identifies this as a partial view. If you have used Rails before then that underscore should look familiar and there's _conventions and other frameworks as well. So I'll just copy and paste the breadcrumbs up there and then I'll use the handlebar helpers partial. And then I'll tell it I want it to render breadcrumbs without the underscore. As you can see, it works. Pretty simple and what we can do is we can come up here and replace the breadcrumbs up here, render edit info and let's go take a look at that edit info and, yuck. Change roles, we don't really want that there of course we want it to say edit information. Partials work well for repeated UI stuff it shares the same date, the same scope. In this case well it sort of does, because we still want to work with the user data, but we also want to be able to set properties. For that, we might want to step it up to use a view. So what we can do with a view is we can specify just add hock to the view name, breadcrumb view as we're doing here. And that extends in Ember view. Then we tell it to use our breadcrumb template. that means up here on our template, we need to remove the underscore now because this is not a partial. And we have set a variable on our view, called page title. Here we could just use the handlebars helper view, followed by the name of the view that we want it to render. As we become aware, if we have any properties on a thing that we want to set from handlebars helpers, we have to use the equals syntax. Here I'm saying page title should equal change roles. Now you might be wondering why's he using a component, seems like breadcrumb would be right for our page component and that's true. The reason I'm using a view here is because it's rather simple. I just have one property I'm sending in and that's that. If I had any more complex code or interactivity, then yeah I might use a component, but a view works perfectly in this use case. Alright, let's refresh and see what we got. We'll go to changes or roles and, oh, I can't find view at the path app.breadcrumb view. Hmm, oh dear, let's see. I think it's just a case of naming. If we go down and take a look at this, hmm well the casing seems to be okay. There shouldn't be a problem in that way, yet we still have a problem. Can you see what it is? Well my application namespace isn't app, it's user admin. It used to be by default that a Ember team recommended you always use app, so it's a bit of a habit for me and for many others. You'll see many examples online that do this. I prefer to use something specific. Well it's showing up, but we don't have our title. Hmm, well the reason for this is that the view is trying to access a property called page title on the controller that our breadcrumb view is sitting inside. We don't have such a thing. So here we have to specify that it's view.pagetitle go look for that property on the view itself and boom, it shows up, there it is, change roles. Views are great for this kind of thing, they're a little bit smarter than partials in that I can access properties on controllers as well as have properties on the view itself. but they're also not quite as complex as components. So they sit somewhere right in the middle and they're very useful. So let's go and drop our view call into our other templates above here, we have change roles, edit info, add note, and let's refresh and make sure everything looks the way we hope it does, edit, yep that's good. Add a note that's good too, change roles, that's good too.
Many to Many Relationships
(intro slide) Okay let's jump into what's sure to be the most complex thing we do all day. And that is dealing with the many to many associations between users and roles. So I'll start by getting rid of the hardcoded stuff here of course and I'll need to iterate over something. So, hmm, I need all of the roles that are in the database I could use controllers.users.roles, but that wouldn't make any sense. Because it's a many to many relationship, the users only going to have one or none or all of the roles. So I want to be able to show every role, which means I need to create a new model for this. And I'll output the role name right here. Hmm, I want to show all the roles that are in the database not just the roles for a particular user. So let's use Ember data to do that. I'll create a property on our user change roles controller and in here I'll say this.store.find role. because we have a model declared down below. So let's see what happens. And yep and we get a 404 error and rightfully so. I don't have API roles created on the server. I do that really quickly by creating a web API controller called roles controller. Well then just grab all the roles out of the database and loop over them and format them as Ember data bits need to see. In this case they just needed an id and a name that will match the model that I have on the client. And let's return some JSON. So refreshing, oh boy, no model was found for 0. You will see this error a lot when you're working with Ember data. And that's because you need to have rules equals and then an array of roles. So this just needs to have an object come back, not an array. And you can see it works, sort of. I need to go up here and say role.name, not just the rule object itself. And there, good our one little role called admin. Now as I'm staring at this page and I see a single checkbox here, we have so much more functionality that we need to convey to our user. I just don't think a checkbox is going to cut it. So let's rethink this whole thing, I know this is going to get complex so let's just jump in and deal with it all. The first thing I'm going to do is I'm going to create a brand new template. And I want this dedicated to the idea of role management. And I'll call it roles. Inside of here I will just iterate over some data, the data that's going to be backing this template. Where's that going to come from? Well the good news is we don't have to declare routes or controller in all that, just to render a template passing data. Here I'll just use the render keyword and tell it to use the roles as it's data. This is a really powerful feature of Ember, where you can just declare a template on the fly, pass in some data. Using render, as I have here, is one step above using a view. As if offers you access to a lot more functionality. To see what I means let's refresh and you'll notice that it works. Which I was hoping that it would. Taking a look at the inspector however you can see that inside of our change roles template we have an embedded template. And backing our template is an array controller. Which means that we can override that array controller to do all kinds of fun things. So let's do that. We'll set our roles controller to be an array controller and we'll extend that array controller of course. And as we've been doing before we will say that this controller needs access to the user controller. Alright well let's see if everything works fist I want to make sure that we're overriding the right things. This is why the inspector is really invaluable. Yep we have our roles controller in effect now we can do something really cool. We can specify that this roles controller needs to use an item controller. That means that for every item in the array each one of them will be backed by an object controller. But when you're referencing another controller it's well the name without controller, and it's lowercase, that's it. Alright, the nice thing about having an object controller like this is I can have some logic conveyed. What I want to know is, is the current user assigned to a given role? I can pull the role off of the model of our role controller. And that's going to be set because we specified item controller above. Then the user, I can say get controllers.user. We can have access to that because we're using needs. So to recap just a little bit, we're using the render keyword and we're passing in an array. That render keyword will render a template that then goes to see if it has a controller, and it does, a roles controller. That roles controller has a model, which is the array that we sent in using render. Since we specified that the rules controllers has an item controller that means that an object controller will be instantiated for each item in the array, as the iteration goes off. For each item in the array, well that's a role, it means it's going to instantiate a role controller and pass that role in as the model. Which is good news for us because we have some functionality that we need to wire up. Speaking of, let's wire things up. And I will replace my hardcoded HTML with the handlebars input helper and I'll set the type to checkbox. I'll then tell handlebars to use the user is assigned a property on our role controller to see if the checkbox should be checked. And finally I'll output the name. Well I can't use Hansleman for this because he doesn't have a role assigned only I do. And that is as an admin. So heading to change user roles calling user is assigned should have output some information to the console as you can see here. but it doesn't look like user is assigned is being recognized or called. Can you see why? It's something that I forget all the time. You have to flag it as a property. Well it looks like all of our constructs are playing nicely together. Our edit roles template is rendering out our roles template, which is using a roles controller, which in turn is iterating over a role controller. So that's good. Well the final thing we need to do is to take a look at the roles that a user has and then return whether the user is assigned to the roles that we're currently iterating over. And we can do that using the Ember array extension contains. Alright, so let's refresh, yuck again, nothing. Well this could be a data issue so let's go and take a look at the data. And you can do that by looking at our JSON response. Um yeah we have the association data in there so everything should be good with that. Let's do a console.log and see what we have for roles. Refreshing, and that's not really much help. Although it does tell me that we do have roles, so that's good. The last time this happened it was a problem with the association so just for fun let's take out the lazy load flag. And wouldn't you know it, it works. I honestly have no idea why this is working, but it does. So I guess that okay. So we'll go into Brendon, yep and his is not checked. Hmm this is a bit of a mystery. Well now that we have things broken out a little bit and we know if a user is assigned to a given role let's clean things up. I'm just going to remove this checkbox entirely and instead I want to put in a striped table, as I've been doing. And let's iterate over some table cells. I want to show more information and offer just a bit more functionality to the end user. Here I will just interrogate user is assigned with an if statement using handlebars. And I'll output certain cells if they've been assigned to a given role. So here I'll just say that they're assigned to a role and if they're not down below I will say they're not assigned simple enough. So refreshing, whoops, each doesn't match if. Ahh, and that's the pound sign there. Okay, so yes Brendon is not assigned and let's go check my role here, because yes assigned to and I forgot to put name, instead I just put role, so let's see assign to admin. Alright well that's a good start, It's a lot more informative than just a checkbox. Okay, well let's do something a little bit more, well let's add some Bootstrap styling. And here I'll set it to alert success which should make this green. Much better. And we need to expand on this a little bit, because green doesn't really tell me much. So let's just leave this as the name and we don't need assigned to. Let's add a little glif icon checkbox here and refreshing, that looks better. Checkbox next to the name of the role. There's still some sizing we need to deal with, but we can handle that in a second. So if a user is assigned, there's only one thing we can do which is unassign them. So here I'll just say revoke and I'll drop in a little Bootstrap style button. And then I'll set an action to it and just say remove role, I'm not going to wear that action up just yet, I want to see how this looks. Yuck, it doesn't look so good. That buttons a little bit too big. Let's set it to button small and see if that works. And then I'll also move alert success up the table row so it covers the whole thing, that doesn't work either. Hmm, well let's take the button out and set it to be an anchor tag and let's just see if that works. Refreshing, that looks better. Let's see, and clicking that of course the actions not there, we'll handle that in just a minute. I need a little more test data however so let's go and add some more roles in. Right not it just is a lowercase admin, let's create a role called supreme master, why not, and I need to put id two in here and then three, I'll just say nobody. And then up above I will reset this to be administrator, alright, list of three roles that should be good enough. So I'll need to log out and back in so it sees me as administrator and going to the user list and picking myself and going to roles okay that looks a little bit better. Now we have some good data to work with. So let's go over here and basically copy and paste as we do down below and I will change that action to be assign role instead of remove role. This is looking much better. But I really want that green back, I like that green bar. I think it's because I'm using a stripped table, so let's go back into our Bootstrap bits and remove table stripped. And refreshing, hooray, I got my green bar that makes me happy. I think that conveys a lot of functionality and meaning to the user. The next thing I need to do is to wire up you actions and I have two of them that need to go on the role controller, no the roles plural, but the role controller. I need to have remove role and assign role for the user. Let's wire those up next. Well as many of you may have noticed by now, I have goofed up the relationship between roles and user. Previously I had it as a one to many, with a user having many roles and a role belonged to a user. Of course that is not correct. This is classic many to many relationship our user has many roles and a role can have many users. And to finalize this association using a joining model called membership. And all that membership has is a user and a role. And it specifies the relationship. User can have many memberships and a role can l have many memberships as well. So if you're a Rails's person or a Rail's fan then you recognize this as something like has many through. Okay we'll let's go down to our console and take a look at our data. And we had to do a little work on the backend but as you can see here I have one membership, that's the administrator. Taking a look at our API data here, that's out on a page, with JSON Dump, you can see that every user has a specified array with a membership associations. Now that one right there, keep that in mind, that's important, because that points to the memberships id not to a role. The same with roles, inside of roles you have to specify that membership. Again, at the top there you can see there is a one for administrator. Finally down at the bottom you can see there is a single membership record with an id of 1, and a user gwid, that's me. And then a role of 1. Now this is critical, because this is an Ember model it has to have an id. And here I have specified the id as 1, I just output this in the backend of my server. but it's important that when you specify relationships using s joining model like this, that you use it's id. I've specified this as an arbitrary integer, but I'll be changing this in just a few minutes. For now let's jump back into our code on the frontend and I will replace roles here with membership. And this is going to go access the membership property of the user. And now I can just say membership.contains role. Yeah I wish, unfortunately this isn't going to work. because many to many relationships aren't supported yet completely in Ember data. What I need to do instead is I need to use the m function, which is an ember extension function for arrays. And it will do a Boolean evaluation based on whatever you return from the function. Here I am simply going to grab the id of the role of the membership item that I'm looping over, and I'm going to compare it to our model that is backing our role controller. And I do that just by saying role.getid. And well let's see if that works? And it does. Woo hoo. That was a bit of work, but we got it to go. Now what it want to do is I want to set this flag this property flag, to make sure it tracks each item in the array. This funky syntax here will allow me to do that you use at each and then tell Ember to stare at each member of an array whenever any member of that array changes, well it's going to rebind the list. And the reason I have two @ symbols here and this is really important, the reason I have two is because I'm using the razor view engine and it sees at as, here comes oms server code. So if you put two at symbols together then it will only output one to the page. Which is suppose is handy. Okay, let's wire up our remove role function. The first thing I need to do is to find the membership that we are wanting to remove. To find that I'll use Ember's filter function, which is another array function that it offers you. And that'll return the first where the condition matches and I again, just matching the membership role id to the current role id that is on our model. Finally, if I have membership I will call delete record, which will tell Ember data to go and delete if from the server. So let's give it a try and see what happens, ohh. I'm defined as not a function, well this looks like something that I did. So it looks like, hmm, membership is undefined, I think. I don't, hmm, I think I'm using the wrong method here, I don't think I should be using filter, I think I should be using find. Because that'll return null if there is no membership matching. Otherwise it will return the membership I need. So let's update our console message and output it only if we find a membership and hit Revoke. Down below, yeah, moved Rob from 1. That's what we want to see. Sort of, none of our lists updated, our UI didn't change. Called delete record, but it looks like the server wasn't even accessed. So hm, something's going on here. Let's simplify everything we're doing here. Let's clean things up by declaring assignment as an alias computed property and that's going to reach up into user and grab the membership array. So that way I can use that down below. And that'll help me remove a lot of repeated code both above and down below. In addition I have sort of screwed up the way this property watcher has been set, you need to have the array first in each, at each needs to be second. I had that backwards. So in this case assignments.ateach and again I have two at symbols there because I'm using the Razor view engine. Finally I could use assignments down below and I could still use the same find functions, assignments is just going to be an array of memberships. And this is simplifying my code a lot. Let's see if it works. Refreshing and hitting revoke, hooray, I still have an error though and that's because down below here I'm using user.get to output the information and I don't have a user, I just deleted that record. So let's go and do the same thing for assign role. And down here I am going to create a new assignment and this is going to create a record called membership. And, hmm, we had tried to create an association before. Let's see if we run into the same problem. For the role I'll just say this.getmodel, for the user this.getuser, because user is a computed property of my controller above. Okay, so let's go and refresh and hit Assign. Oh geez, well at least we know what this is all about. We need to push our new object here into its associated lists. So let's get rid of this code here and what I need to do is to access the membership on our role as well as the membership on our user. so our role is just our model. So I can say this.get model and then get the membership on that model. And then push our new assignment in. The same thing with the user, doing the same thing. Just pushing our new assignment into the users membership. And it works, sort of. Down below well we are getting a 404 because API memberships isn't found. Whenever I save a new record of course it's going to try and post that new record back to our API. I don't have a memberships API controller so let's add that right now. Well here's my new memberships API controller and I've got two new methods on it for post and delete. I've also changed the way that I am dealing with the memberships association id. That id was a completely arbitrary structure that I created before that was just a simple integer that I used in a loop. Here I am going to concatenate the users quid with the role id 1, 2, or 3, but the aspi.NET identity system gives to each role. This is a little bit hacky, but I don't have any control over that many to many join table in the back that the identity system it using. And plus I want to go through the user manager. So this will suite my need for now, even though it's a bit hacky. Okay, so let's take a look at the post routine. Here I'm just grabbing the role id and the user id off of the post that is come in and I'm grabbing the role from the database that matches the role id. And then down below I'm using the manager to assign the user to the role. I want to be sure that I use the Asp.net security stuff and I don't want to roll this stuff by hand. Over to the right side you can see that I've added a memberships controller and a notes controller roles controller, and those are the only one that I need so far. The only thing left that I might want to do here is to add a note saying that I've added user x to a given role. and for this I'll just create a new note and say exactly that and then add the note to the user and then save it. Let's do the same thing for removing a user from a role, we'll just use the exact same code, I can probably refactor this later on, but for now I just want to have a little note that goes along with adding and removing a user to a role. Alright that looks good, so let's refresh and go here to our roles there we go. I'll assign a supreme master and going over to our notes and let's refresh this and there it is, added user to supreme master. It works.
Organizing App Code
Template Precompilation - Visual Studio
Template Precompilation - Grunt
(intro slide) Having Visual Studio compile your templates for you is a fine idea, however, a lot of Ember developers many, many of them, will use a different tool altogether, one called Grunt. Grunt is a scriptable task runner that is made to help developers do all kinds of things, like compile their code and run linters and also compile Ember templates. It runs on node as you can see here I'm running node version 10.5. And to install grunt you just do npm install and make sure you add the --g flag grunt. And the reason we want it to be installed globally is, well I'll show you in a second. Next we want to make sure we install the Ember template compiler. And that's in the grunt Ember templates project, so just do npm install grunt ember templates. Next in order to script grunt I need to create a grunt file.js, it should live in the root of your project. Inside of here we are going to configure grunt and then create our tasks. Now if this looks confusing or you think I might be going too fast, I'm not going to talk about grunt itself too much. I will talk about some of the tasks I'm creating here, but I invite you to go have a look at the grunt project if you find yourself getting lost. Okay, well this is how you configure grunt. One of the first lines you should write is init config. Inside of here you're going to set configuration settings for the various tasks that you write below. Next I'm going to load the tasks from grunt ember templates, that's the module I just installed. It has a bunch of grunt tasks already built for us inside of it, I just need to load them up into grunt. And that's how you do that with grunt.loadNpmTasks. When I compile the Ember templates I simply need to set the options here for grunt ember templates by telling it what the base path is or how to get to my templates. And here I've specified that. Notice that I'm using links that are relative to the grunt file itself, that's the way grunt works and node in general. So I don't need to use absolute links that go from my root all the way down to the file. Grunt will know exactly what's going on. In the file setting here I am telling it that I want output into Scripts/user-admin/templates.js. All of the source files in scripts user-admin templates. This is going to be a recursive deep look for .hbs files and it will be able to pull out all the files using that lob **/*.hbs. If this looks really strange to you, yeah using grunt for the first time was a little bit foreign to me as well, but after I got used to it, wow I absolutely love grunt. So let's run it, and oh boy, we have a bit of a problem and reading this the term grunt is not recognized. Well that's because I forgot a step here. Grunt is separated into a bunch of different projects. I need to install grunt-cli. There's a grunt core library, which is already installed, but grunt cli gives me command line interface access. Once that's installed I'm good to go. It found my grunt file.js and it ran the default task, which is great. Compiled down my templates. Where are those templates? Well if I refresh here and I show the hidden files, by the way it looks like that one got out of order here, rules needs to be inside of my templates directory. Let's just move that really quick. And okay, taking a look at templates.js to see if everything went together okay, well it looks like it. Let's check the names, hmm that does not look right. Remember how I said these names are very important. If you use a compilation tool like we have here that should be the first thing you check, is the name correct, and it's not. So let's debug this a little bit. I'll replace the bundle reference with our templates.js file that we generated with grunt, refresh, yee nothing's showing up and no errors either. Which is something you should get used to. If Ember can't find something it'll just generate it for you. In this case it's generated our application, index, and everything for us has no markup, so of course we're not going to see anything. Well to solve this I need to tell it how to do the template name. I don't know why I need to do this manually, but I do. So I'm just telling it to replace scripts user admin templates with nothing and I'll regenerate this and it's reloaded. Having a look, yep there we go, that's pretty nice. So let's make sure that this is working. And I'll go and refresh, yep there's our application. It's just a bummer that I have to run that command every single time I want those templates to be compiled. It'd be nice if I could have some automatic way of doing it and indeed there is. I can install gruntcontribwatch. This is a super handy module that everybody loves to use, because it basically fires a given task based on when a file is changed. So here I'm going to configure watch to stare at the files in our templates directory. So for this I'm just going to copy this exact lob right here, tell it to look at all the hbs files, whenever they change I want it to run the task ember templates. And that's that. So to run this I just run gruntwatch. And you can see it's waiting. So let's make sure that this works, I'll just go in here and put a dash in place. And boom, done without errors, it even tells you how fast it took to run. And then Visual Studio's also telling us that the file changed. You might be looking at this going no thanks I'll just use Visual Studio, why would I ever use a tool like grunt? Well there are a lot of reasons. Number one is that, well maybe some people on your team, especially the frontend folks, might not have Visual Studio. Maybe in five to six years from now you might not have Visual Studio either because your team might have moved off to a different platform.
A Tour of Discourse With Robin Ward
A Tour of Discourse
Review and Refactor
Code Review With Robin Ward
A Discussion With Tom Dale, and Goodbye!
Discussion With Tom Dale
We've reached the end of Ember.js fundamentals here at Pluralsight. In this course you've learned how to use Ember in two different ways. Using jQuery's AJAX methods for data and also using Ember data. Along the way we've encountered some of the more interesting aspects of Ember as well as a bit of my opinion on things, hopefully not too much. I mentioned in the beginning that in the end you should know how Ember works. How to create an application using the majority of the framework and techniques that Ember developers use. In addition, you should also know how to solve some tough problems with Ember. This requires some repetition as well as memorization. And hopefully at this point you feel comfortable kicking up an Ember application and then adding features to it. The code for the user admin application is up on my GitHub repo, which you see here. If you'd like to help or add any features, please do. I'll be adding some tests and building this out as I need to and I welcome anything you've got for me. Before leaving I'd like to offer some final thoughts on Ember and everything we've done. It's no secret that I'm a bit opinionated when it comes to APIs and how complexity is handled by a given abstraction. I want to be very clear that Ember is amazingly powerful, it's only weakness is the way it expresses that power through its API abstraction. Over time I think this will change, Ember isn't backed by a large corporation with a corporate agenda, things are not locked in. It's just a group of developers that have a strong vision for how the web should work. The polish will come, I am confident in that. Whether you work with Ember, Angular, ReAct, Backbone, Durandal, all of these frameworks are trying to help you do the same thing. How well each one works for you is completely up to you. Just be sure to invest the time to know what you're getting into, where it will fail you, and also how it will help you win the day. There is no right answer in terms of which one is best. There's only the one that you choose. So go with it. And that is it for me, Amapow, as they say, I'll see you again soon. Thank you so much for watching and if you have any questions you can send me a note on Twitter using the Twitter handle you see on screen or the email address, which you also see on screen. ahoiho, see you again soon.
Rob Conery co-founded Tekpub and created This Developer's Life. He is an author, speaker, and sometimes a little bit opinionated.
Released29 May 2014