Ember.js Fundamentals
-
Introduction
(music playing) Welcome to Ember.js Fundamentals here at Pluralsight. My name is Rob Conery and I will be your host for this course. You can reach me with any questions at the Gmail address on screen or on Twitter as well. In this course we're going to learn all about Ember js. What it is, how it works, and how to get it to do what you want, which can be difficult at times. Also I want to be perfectly clear up front I am not an Ember js expert, I am, however, relentless when it comes to learning how to use a tool to build things. And every time I learn anything it's with an eye toward passing on what I've found. And how others might benefit from my obsession and this is why you're here watching today, I hope. So what is Ember js? Well simply put it's a client side JavaScript framework that's very useful for building single page applications. According to the Ember team it follows a somewhat opinionated version of model view controller and is all about building ambiguous client side applications. So what is Ember js really? Well let's just lead with a punch line here. Ember is an MVVM framework, as are most client side JavaScript frameworks, with a pretty steep learning curve. It's heavily opinionated, uses conventions that rely on arbitrary naming schemes, and the API changes rather often. And this might sound off putting, but when you master Ember you'll find that you can make incredibly complex applications rather easily. As an example here's one of the apps that we're going to build today, a Github Explorer. If you're familiar with Angular js, Backbone or Knockout, think about what it would take to put something like this together. We'll be making simple AJAX calls to the GitHub API, showing master detail, and master detail detail information about our favorite developers and the GitHub repositories. How many lines of code would you guess this would take using Angular or Backbone? With Ember it took just over 90 lines to push the framework around to do our bidding, not counting HTML and the templates. I won't lie to you, it wasn't drop dead simple, you have to master a few of Embers idioms to make it all come together, but that's why you're here. And if I do my job you'll be on your way quickly.
-
Why a JavaScript Application?
(intro slide) Fewer lines of code, single page applications, JavaScript, why should you care about any of this? Simply put, single page applications can offer an incredibly compelling user experience. Especially if the interaction is data intensive. Here's the stripe administrative application written in Backbone. Stripe is a payment gateway that retains payment, subscription, and customer information for you. And that's a lot of information. This is test data I used with techpub.com and as you can see there's an incredible amount of functionality on this page. And the user experience is greatly enhanced by not waiting for this screen to refresh constantly. As a counterpoint to this, go log into your PayPal account, if you have one, and marvel at just how bad a user experience can be when waiting for a slow site. Here's another popular site that uses a single page application approach, the square space editor. This is my wife's site, so don't tell her I'm messing around back here. Square space uses the UE framework, which isn't really an MVC framework at all, it's just a collection of UI widgets that you can program, but it's still a single page application that is incredibly compelling. In fact it's the hallmark of this service, an amazingly useful tool that sets square space apart from its competitors in terms of both style and utility. This site here is bustle.com an online magazine for women. An interesting thing about this site is it's a single page application written in Ember, but it's also public facing. Single page applications have a major problem when it comes to indexing content. Search engines don't speak JavaScript for now. So clicking through the links on this site, if you're a Google spider, is a bit of a problem, if you want it found on Google. Bustle solved this problem by using a backend service, which renders each page as HTML when a spider is detected. A bit of work to be sure, but the end result is a very, very, very fast site that looks gorgeous, and is amazingly useable. I've been working with these JavaScript frameworks for so long now, that I can't look at an admin interface, like this one from wrapbootstrap.com, without thinking about making it a single page app. The more complex, the easier the choice. Here's the customer profile page that I wrote using Angular for the old techpub.com site. It sits right on top of the old account page that shopify provides. And I used Angular to talk to our API on the backend and our old server. You report purchase information, rights to videos, and so on. It took a total of eight hours to put this all together, that's how powerful frameworks like Angular and Ember have become. So why should you care about using a single page application in your web application? Because it can provide amazing user experience for complex interactions that are heavy in data and they're fast. And it separates the user experience code from backend business code. And that can be really useful now and into the future if you ever decide to change your backend framework from platform X to platform Y, which does happen.
-
Why Ember?
(intro slide) Another question in your mind right now might go along the lines of how does Ember stack up against framework X or framework Y? I've built and deployed applications using four different client side frameworks that I think are the most popular. Backbone, Angular, Knockout and Ember.js. Each has their strengths as well as weaknesses, so let's talk about that for a second. Comparing Ember and Backbone is not quite fair, since Backbone is an incredibly small set of libraries built to sit on top of jQuery. When working with Backbone you typically write your own abstractions to sit on top of Backbones core parts, you use collections, models, and so on. Backbone doesn't try to abstract everything away, rather it leaves that to you. If you're a developer who doesn't mind writing code and you like things exactly your way, Backbone is a fine choice. If you don't want to have to deal with every single small detail in your application, you might just want to focus on solving the problem at hand instead of how you solve it, Ember is a good choice. Comparing Ember to Knockout is, once again, not quite fair. While both are a flavor of model view view model, or MVVM, Knockout does not have the concept of routing or models, but you can create those things if you like or plugin third-party libraries to help you. Knockout uses the DOM directly, using HTML5 data tags to wire up events and dependencies. Ember's focus is keeping your DOM clean, using handlebars templates and code based event hooks. Knockout is incredibly easy to pick up and run with, Ember is not. Ember allows you to scale your application in terms of complexity and speed gracefully. Whereas Knockout, you'll quickly have to work around the abstraction, or provide your own through plugins as I mentioned before. If your page is pretty simple and you don't need routing Knockout is a great choice. Otherwise Ember is much more full featured. And finally we get to Ember versus Angular, one of the webs greatest grudge matches. The Ember community, led by the core team, is rather vocal about their distaste for Angularjs and how it does things. Some, like myself, find this sort of rivalry to be a good thing, but it does get tiresome at times. Both of these frameworks do essentially the same thing, but in a slightly different way. The main difference between the two is that Ember's idioms stay the same throughout development. With Angular the abstraction tends to break down as your app scales up in terms of complexity. And from a functional perspective the two frameworks are more the same than they are different. They both claim to be MVC, but are technically MVVM. If you're wondering why this is important I'll get into it later on. Both have the concept of controllers, they use templating, and allow you to work with URLs for routing. Both have the notion of UI componentry, Embers components versus Angulars directives. They implement caching for you, although a bit differently, and both have a somewhat disjointed and difficult data story which we will get into later on. Now let's get into the main technical differences. The biggest by far is Angulars use of dependency injection. Here's a controller in Angularjs, it's a simple JavaScript function. To use Angular you simply inject the services you want into your function by using the dollar sign and global objects that Angular creates for you to use. It's an amazingly simple concept to grasp, but it doesn't stay this way, we'll talk about that in just a second. Now here's a controller at Ember. Now the first thing to notice is that there are three different kinds of controllers that you can use or you don't have to use one at all. What a controller does is also a mystery until you come to the understanding that a controller in Ember can behave just like a view model in other frameworks, specifically Knockout, but even that's up for debate. I mentioned on Twitter one day that I thought of Ember's controllers as view models and received some interesting replies that amount to basically sometimes this is confusing until you've used controllers in Ember enough. And then you see how it all fits together. And I have to agree, they aren't view models all the time, but sometimes they are. As I mentioned Ember has a steep learning curve and it's for reasons like this. Ember's machinery can be vague. Well the one massive thing that Ember has on its side versus Angular is the ability to scale your application elegantly. Note that I'm not talking necessarily about speed or performance, now there is that too. I'm talking about having to change what you do and reverse course. Ember's approach is rock solid all the way through, no matter how complex things get. You can refactor the complexity down if like, but you won't back yourself into a corner. Now there are multiple different ways to develop an Angular application. You can go to the simple demo friendly route and ng app everything. Or you can define modules within modules. Most people start out the simple way using the controller code that you saw before and then they realize that they need routes and services and have to change their approach completely. This is not a simple switch to make. Understanding the nuances of HTTP versus resource services is very important, but it's only after you've tried to swap one for the other that you realize that you have to rip apart your code and change your approach. Again not a simple switch to make. As an example of this let's say we wanted to use ng resource in Angular library for fetching remote restful data. Instead of the built in dollar sign HTTP service. To do this we'll need to create a module so that we can declare ng resource as a dependency, which we can then inject. Or maybe we want to do some routing or basic configuration, either way are simple code has to change now. In order to use ng resource, or the implement routing, I need to go backwards and rewrite my controllers using the controller function on the module that I need to create. Not really a big deal all together, but our app is now completely different and it's put together in a completely different way. And our simple demo has disappeared. Think we're done here? Not necessarily. Most JavaScript code is run through a minifier before it goes into production. If you use ASP.NET bundling or Rails asset pipeline, it will do this for you automatically if you're running in production. That's a good thing for you, unless you've written your app as I have here, which is actually a very standard way of writing Angular. The problem is the way Angular declares things and magically creates them for you in the background. Here we're defining a service and giving it a name, coupon service. And then we're injecting it into our controller as if it were a real object, which it becomes later on. A minifier won't understand this, instead it will shorten these variable arguments to names like C. While leaving the string name coupon service untouched, treating it as a string literal. To get around this you need to change the way your code is called. Wrapping it in braces and passing strings instead of variable objects. This doesn't read nearly as well, it looks even stranger when you're feverishly trying to figure out why your minified build in production isn't working, which is the worst part of all. You wouldn't find this in development, you'd only find this after you've pushed to production. At which time your framework will minify your code and everything will break. The next thing I'll touch on is community. Both Angular and Ember have a strong developer community, but Ember seems to have one that's just a bit tighter knit. If you ask a question on their discussion site, which is discussion.ember.com, or on Twitter, you'll usually get an answer very quickly to your problem. The communities effusive when trying to help out, which can actually be a bad thing. When you start working with Ember you'll be Googling for help quite often. You'll be happy to find that there are tons of Ember answers on stackoverflow and you'll quickly find that unless these answers are less than three months old, they're probably out of date and wrong. Ember's API has changed so many times that it's utterly ridiculous, this is a good thing in that the team is constantly trying to refine things. And it's a bad thing in that keeping up and finding answers, good answers, correct answers, can be utterly maddening. Well more good news is that since they've reached 1.0 a few months ago they've slowed down breaking their own API. And what the bad news is that they're still sort of breaking their own API and deprecating things and it's probably the main reason people use Angular instead of Ember. So these frameworks are all a bit different to be sure. And developers usually find themselves drawn to one of them for a variety of reasons. I invite you to suspend whatever opinions you might have based on frameworks that you know about or have used, and give Ember a try with me today. I've been through the ringer with this framework and I've grown to really, really like it. Whatever your choice of framework, just make it an informed one. So today we're going to build not one, but two applications for this course. Why two? Well because I wanted to capture two different workflows with Ember and it was actually easy. For the first I wanted to build a GitHub explorer using Ember with jQuery. We'll be hitting the GitHub hyper media style API, it's a great API to work with. Having it like a repository is a various friends of ours. Along the way we'll learn the basic concepts of Ember. For the second app we'll put together a user administration application for ASP.NET MVC projects using Owen ASP.NET identity and ASP.NET web API. You'll be able to drop this application right into your web application with some minor adjustments to the data store. In addition I'll be publishing this to GitHub and you're welcome to work on it with me. I was amazed at how quickly I could put this app together. A few times however I was ready to give up, but once I cleared those obstacles it was smooth sailing as I added new features and ramped this thing out. To get the most out of this course you should be reasonably familiar with JavaScript. I won't be discussing the language all that much, nor the patterns that I'll be following. You should know how the web works and be a reasonable web developer. The basic understanding of what the MVC and the MVVM patterns are and how they work will really help you. Finally, please be patient. We'll be tackling some tough concepts right away and you might feel overwhelmed. I'll do my best to explain things clearly, but there's nothing like writing code to learn and that is what we're going to do. When we're all done you'll know how Ember works and its various idioms. You'll also be able to structure a complex application together using regular AJAX calls or Ember data. You'll also be able to troubleshoot problems with Ember and to identify common stumbling blocks.
-
Installation and Tools
(intro slide) So here's our starting page and I've dragged in the scripts, as you can see, jQuery, Handlebars, and Ember, those are the three that you need in that exact order, do not change them around or else nothing will work. And we are ready to create our application. So below those, open up a script tag and I am just going to create an app variable, I'll just call it app. And I'll use Ember's application and I'll call create, the create method and look at that, I even get some IntelliSense. Visual Studios smart enough to go in and read through the build version or the debug version excuse me, of Ember and give us some IntelliSense. Now the docs usually say to use app as our application name, but you certainly don't have to, in fact I know a lot of developers who are moving to using a global declaration so you can play with it in the window in the console. So here I'm going to assign the global GitHub variable to our new Ember application and I'll refresh, opening up the console down below. Now it looks like everything's working. And you want to use Ctrl + Shift + I to open the dev tools in Chrome, which is what I'm using. So the debug build of Ember will output all of the information, just so you know what's going on, and here the Ember inspector is also kicked up and live, although we don't really have anything in our view tree, we don't have much of anything at all. And that's that. We are running Ember, our application works, we have the right bits installed, and we are ready to write some code. If you don't feel like setting up Visual Studio or whatever editor you're using to play along today. Basically if you're just feeling lazy, I often feel very lazy, head over to jsbin.com. It's an in browser editing experience and it's really, really handy. You don't even need to have an account, just go to jsbin.com and this is one of the first things that you're going to see. Here we have HTML on the left and the output on the right. So if I come in here and just type in H1 and hit Tab, notice it's got zen coder stuff in here. I can type HI and boom it shows up on the right side. This is really cool for working with things like Ember. So how do you work with Ember? Well what we can do is we can add a library and if you're scrolling down sorry it is off screen. There's a choice Emberjs 1.0 and look what it did. Right in here if we just move this on over you can see that it added a few things for us. It added jQuery, it added Handlebars, and it added Ember, all the right versions of everything, it's that groovy. If you want to play along with us later on when we use Ember data, go over to cdnjs.com, cdnjs, it is a place just for hosting all kinds of JavaScript. If you type in Ember you will see Ember data, copy that, and paste it in there. So the neat thing is, is down below you can just write out script and let's hit Tab and then we can say alert hi and close that off, and hit Run, boom, it's kind of neat. You just type the code in right here and you're off and running. For the initial parts of working with Ember this might be a lot of fun for you if you don't want to set up your IDE. When you're done, if you want, you can save it and just create an account, it's free. Or you can just share it with people, if you like, hey look at this code I wrote. In addition there's also a console output, so if we log something out to the console you can click console and see it right there. And if you want to actually have JavaScript embedded, you don't want to write it on the page, it gives you a JavaScript file to pop right in. So anyway, it's a real nice option for playing along. Let's go.
-
Github Explorer
Getting Setup
In the following three segments we'll install Ember and jump in writing some code. I'll show you how to create an Ember application and get it to show up where you want it to show up. In addition, we'll get to know Embers templating system, handlebars. Then we'll get right into it dealing with Ember's complex learning curve right up front. You might feel like turning this video off or you might feel right at home. If you're like me it'll take a little bit for Ember to sink in, but we'll keep repeating things until it does, so have no fear. (intro slide) In the downloads for this module you should see our starter site and we don't need a web server for this. We are just doing frontend work so we can double-click index.htm. Inside of here I've included Bootstrap as well as our scripts that we're going to need to get started. I want you to notice something special here, that first Ember and handlebars are required together. And you need to have parity in the versions. This is step one of some issues you will face with Ember when you're getting started you have to make sure you have the right version of handlebars, which is the templating engine as well as Ember. We'll talk a little bit more about that in just a second. You're also going to need jQuery. If you take a look at this Ember file you will see that it is 1.2MB, that's massive. However, this is the debug development version of Ember and this is what you want to be using when you're developing and debugging. The production version is a lot smaller, I believe it's around 230K, so don't worry about that. Finally we have momemt.js, which I'll be plugging in, it's a date formatting library. Alright well if you double-click index.htm what you should see is our GitHub club. I'm using Chrome, I highly suggest you do too, because we'll be using the developer tools. If you're on Windows hit Ctrl + Shift + I, if you're on a Mac hit Command + Shift + I. What you should see down below here is debug information from Ember itself and in fact we do. And it's telling us hey go get the Ember inspector. That's the other thing I wanted to tell you before we start writing code go grab it. It's at the Chrome web store, Ember inspector, or you could just drop open your extensions inside of Chrome and add it, it's very useful. We're going to be using it. If you want to play along from scratch you're welcome to, go grab the starter kit when you're using Ember and you want to install the bits they've conveniently added everything together into a starter kit. And inside of that starter kit you have an index.html, you can see some code, it shows you how to run tests, and how to even get started, and it gives you the libraries and here you can see. Woohoo, finally the production version of Ember is right up here on their site. If you don't feel like setting up Visual Studio or whatever editor you're using to play along today, basically if you're just feeling lazy, I often feel very lazy. Head over to jsbin.com, it's an in browser editing experience and it's really, really handy. You don't even need to have an account, just go to jsbin.com. And this is one of the first things that you're going to see. Here we have HTML on the left and the output on the right, so if I come in here and just type in H1 and hit Tab, notice it's got zen coder stuff in here. I can type Hi and boom it shows up on the right side. This is really cool for working with things like Ember. So how do you work with Ember? Well what we can do is we can add a library and if you're scrolling down, sorry it is off screen, there's a choice Emberjs 1.0. And look what it did. Right in here if we just move this on over you can see that it added a few things for us. It added jQuery, it added handlebars, and it added Ember all the right versions of everything, isn't that groovy. If you want to play along with us later on when we use Ember data, go over to cdnjs.com, cdnjs, it is a place just for hosting all kinds JavaScript. If you type in Ember you'll see Ember data, copy that, and just paste it in here. So the neat thing is is down below you could just write out script and let's hit Tab and then we can say alert hi and close that off, you hit Run, boom. It's kind of neat. You just type the code in right here and you're off and running. For the initial parts of working with Ember this might be a lot of fun for you if you don't want to set up your IDE. When you're done, if you want, you can save it, you just create account, it's free. Or you can just share it with people if you like, hey look at this code I wrote. In addition there's also a console output, so if we log something out to the console you can click console and see it right there. And if you want to actually have JavaScript embedded, you don't want to write it on the page, it gives you a JavaScript file to pop right in. So anyway, it's a real nice option for playing along. Let's go. So here's our starting page and I've dragged in the scripts, as you can see, jQuery, handlebars, and Ember. Those are the three that you need in that exact order, do not change them around or else nothing will work. And we are ready to create our application. So below those, open up a script tag and I am just going to create an app variable, I'll just call it app. And I'll use Ember's application and I'll call create, the create method and look at that. I even get some IntelliSense. Visual Studio's smart enough to go in and read through the build version or the debug version, excuse me, of Ember and give us some IntelliSense. Now the docs usually say to use app as your application name, but you certainly don't have to, in fact I know a lot of developers who are moving to using a global declaration so you can play with it in the window in the console. So here I'm going to assign the global GitHub variable to our new Ember application. And I'll refresh opening up the console down below. Now it looks like everything's working. And you want to use Ctrl + Shift + I to open the dev tools in Chrome, which is what I'm using. So the debug build of Ember will output all of the information, just so you know what's going on. And here the Ember inspector is also kicked up and live, although we don't really have anything in our view tree, we don't have much of anything at all. And that's that, we are running Ember, our application works, we have the right bits installed, and we're ready to write some code.
-
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
(intro slide) Next let's keep filling out our index view. Right here in this space I want to have a list of developer names with links so that when I click on them I can see things like repositories and issues and all the GitHub information about them. Here I'll just hardcode an unordered list with my name and Tom Dale and Yehuda Katz, those are the primary developers for Ember. And for good measure I'll throw in Scott Hanselman as well. So if we go back to the page, let's refresh this and see how it looks. And yeah that looks good to me. And going over to our console, we have no errors, that's always a bonus, but I think that's about to change. So obviously looking at this list you would think that maybe this should be data driven. Now let's do that, let's work with some data and crawl into the complexity of working with Ember. And I'm going to warn you straight upfront, that we are going to dive face first into this, I am not going to shy away from it. So whenever you have to crack the lid on Ember it helps me to resight an incantation. And I do that all the time. And that incantation is pretty simple. Everything in Ember starts with the URL. A URL maps to a route. A route prepares the data, the route prepares the controller, and then the controller is shoved into a template. You can think of that as a view model, that's sort of what I said before. You might be looking at this going, wow that's a lot of moving parts. And while that's true Ember will try and do it's best to help us out with that. So to see what I mean let's map some data for our index URL. And as the incantation goes, URL wants to go to a route, and the route prepares the data. This is our index URL and therefore we're going to go to the index route. And we're going to extend an Ember route. That's basically how you do inheritance with JavaScript. The naming of this is very important. Because we're at the index URL we have to use an index route. Change the name, it won't work. So in the route we're going to specify a model. And in here we're just going to return a simple string of names, in fact it'll be the four names above. This is a very simple data structure, but we'll expand on this in just a bit. So for me personally this was a strange moment when I was trying to set a model on a route. Conceptually it didn't make any sense, because I was thinking of a route as a route handler. And indeed that's what it is, but why would a route handler structure the model and the data? I didn't quite get it and if it's confusing to you, as it was confusing to me, well hang on we'll talk about it in just a second. So here I've added Phil's name, Phil Haack, to the list because I want to be sure that I'm outputting the data to the page. And how do we do that? Well we use some handlebars syntax and for this we'll use the each keyword it's a simple iterator and it iterates over a thing in a collection of things. So our model is a string array. So we're going to output for each string in our string array and we'll do something with it. And then you end the each call down below as you've seen as I've done here. So dev is a string and we just want to output it to the page. To do that we just surround it with these curly braces. Curly braces, handlebars, comes from mustache, get it? Yeah. Anyway that's how you output a variable and it should just show up in our link. And the syntax isn't the most straightforward, why a pound symbol, why that forward slash? It's just something you have to get used to, this is handlebars, this is a templating language. Every templating language has a different way of doing things. So if we go back to our page and refresh, ah there we go, you can see we are now iterating over our data. Something else that is really interesting for you to notice, if we take a look down at our inspector, our controller has changed. Remember I said keep an eye on this thing it's going to change, and it did. We now have an Ember.ArrayController. It used to be an Ember.Controller. In addition we have a model and it's a bunch of names and just strings that you can see right here, because we've defined a model right there on our route. So I'll warn you, this is where things get a little complex. If I flip this over to be each dev in controller, what do you thinks going to happen? If I refresh, nothing. And if you take at a look down at the inspector you can see we still have an array controller that we're dealing with. Hover over it and you can see the template, the controller, and the view, but that doesn't make any sense. Well this is where things go a little bit sideways in initially understanding Ember. And the first thing that I'll tell you is that if you think about a controller as a view model, things start to make a little bit more sense. To continue on here, let's override that controller. Because remember how I told you before, Ember will generate things for us in the background. And indeed it created that controller for us, otherwise known as our view model. Just think of it that way for now. Here I'll just create an index controller and by naming conventions that's what you have to call it, index route, index URL, index controller. And what I'll do is I'll extend the array controller and override it so we can now output some information of our very own. And I've added a method here called renderedOn. Now you might think that I can just invoke it, it's just a function, I should be able to invoke it. And let's refresh this. And what we see is an error. I'm not able to parse this, that's a handlebars error, doesn't quite know what to do with that thing and this is one of the benefits of using handlebars. It doesn't want you to write arbitrary JavaScript that you can invoke in the template, the templates don't understand. They're just handed data as opposed to something like Knockout where you can invoke these things directly inline, not with handlebars and not with Ember. Well here I have to flag this thing as a property. If I flag it as a property then handlebars will be able to output the invocation right inline here. And then I could just remove that right there and move the open and close parens, refresh, and look there it is, Monday March 17, 2014, that's when the page was rendered. Are you lost? I know I was when I first saw this stuff. If you're feeling any bit of confusion, just hold tight. Let it wash over you, let the code just kind of flow over you right now and we'll write a little bit more code and hopefully you'll start to see how things go together. So right here I've opened up a paragraph tag and what I want to do is let's put in something that we can interact with. Then it'll show you a little bit more about what controllers are and how they're supposed to work. So here I'll just some Bootstrap styling and I'll output a button using button.success or button success. And I'll say click me. Now I want this button to do a thing and I want it to interact with our Ember app. So I'm going to use handlebars once again and use the action keyword. This means go and find this action on my, can you guess, controller. That means we need to go and specify what click me does. And we just include a function here and we'll just pop in alert. I've been clicked. If you think of, again if you think of a controller as a view model things start to make a little bit more sense. So let's go ahead and click and I've been clicked, hooray. But down below we also have a bit of a problem. We have a deprecation warning that's, you get used to this when working with Ember. Action handlers implemented directly on controllers are deprecated. So in other words we don't want to have the function just right on the controller itself. Instead, you want to use an actions object. And it even gives you some code as to how to do that. So that's what I want to do next. So we'll go back to our code and do what Ember has asked us to do. So I'll create an actions object here, which is just going to be a simple JavaScript object. And I'll open up the braces and I'll just move click me right inside. Notice that it did work before I'm just trying to get rid of the deprecation warning. And hit click me, good, and no deprecation warning. You might think this is a bit of wonkery, why did I need to attach that to an actions object? And the simple answer is, that's how Ember likes it. They want things tidy and you're pretty much supposed to do things their way. Now let's take a look at our inspector one last time and you'll notice down below here that we no longer have an array controller, it's our index controller. And how did Ember know to use this instead of its generated array controller? Well because we named it the index controller. The index controller works with our index route, which works with our index URL. All these things go hand in hand and work together by name. Now if you're confused, don't worry. We're going to repeat this process over and over and eventually it's going to make sense. Now a lot of you might just get this straightaway, and say what's complicated about it? That seems to be the thing with Ember. For some people it just resonates, for others, like me, it takes a bit of head pounding and repetition to get it right. In the last three segments we got to know Ember and we even wrote some code. It's also likely that I completely confused you. Let's quickly review what we saw. We got rolling by creating an Ember application and an application template. The contents of our application template were injected into the GitHub app DOM element. And we saw our Hello message properly displayed on screen. Next we added an index template and changed our application template to deal with structure only. We then went right into the thick of things, working with Ember's constructs directly, namely the controller, view, and route. We found out that we don't need to create these things every single time, Ember does it for us in the background. We saw this by using the Ember inspector in Google Chrome. We also found that if we need to implement a feature we can override these generated constructs as needed. And we did need to do that when we started working with data. We plugged in a string array, names of developers, whose repos we wanted to browse. To do this we used an incantation that I find helpful, but URL wants a route, a route sets the model, a route prepares the controller, and the controller backs the template. This led us to understand that controllers, unlike their server side brethren as you might find in Rails or ASP.NET MVC, can be thought of as view models or presenters. Ember calls these things controllers, however, and so will we. When working with data we had our first encounter with Ember's naming conventions, which are in place to reduce manual configuration. When navigating to our application index we are able to use an index route, which in turn prepared an index controller for our index template. And finally we encountered Ember's somewhat wonky API, which makes sense after you use it for a bit. You can't invoke functions in handlebars, so we needed to use the property flag for our renderedOn property so our template could render it. This isn't the last time you'll see syntax like this, in fact we'll dive deeply into it, but just know that things will get easier the more you do it. However, a lot of this is just getting used to Ember's API.
-
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.
-
Nesting Routes
(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.
-
Sharing Data
(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.
-
Deep Nesting
(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.
-
More Handlebars
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?
-
Saving Data
(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.
-
UserAdmin App
Setting Up
(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.
-
User Details
(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.
-
Gravatar Component
(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.
-
Debugging Relationships
(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.
-
Getting RESTful
(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.
-
Using Views
(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.
-
Better Organization
Organizing App Code
(intro slide) Take a moment and clean things up, right now all of our scripts are in our content JavaScript vendor folder. And our application is on our view page here, if you go into Account and Admin.cshtml, yeah that's where our entire app is. Well obviously this is suboptimal for going live, especially with Ember. So let's change things around. First I'll create an apps folder and inside of here we could create a user admin folder specifically for our user admin app. And inside of here we can create an app.js file. So going back to admin.cshtml and taking small steps, probably the easiest and simplest thing we can do is to take all of the code that we've written and basically sweep it under the rug and put it into app.js. And then of course we need to take app.js and source it back onto the page. Refreshing to be sure we don't have an error. And no surprises. Yep, everything works. Well having a massive app.js file really isn't idiomatic Ember. You tend to like to spread things out among files. So here I can create a controllers.js file. And I can also create another one called models.js. And maybe another one, I don't know, routers or routes.js. Of course if you have a really large project, like Discourse for instance, you might have things like users and posts, comments. So what those projects seem to do is have subfolders, a folder called users and inside there you might have controllers and models and so on. Our project's pretty simple. So right now what I'll do is I will move our model declarations into models.js, and then I will go ahead and grab all of our controllers and drop it into controllers.js. And I think that's probably good enough, I mean I could keep splitting things out if I like, but you know I'm not a big fan of a file with a single function in it. I think that makes things a little bit, I don't know, hard to find. So here I'll just leave everything inside of app.js and call it a day. What this will allow me to do is use ASP.NETs bundling feature. So here I'm in appstart and bundleconfig.cs. What I'll do is I'll add a new script bundle and I'll call it apps/useradmin. And then I'm just going to include the entire directory that I just created. This is going to be super handy because it's going to concatenate and minify my JavaScript. And then shove it all together for me so that when I go into production I can serve up a minified JavaScript code. Another benefit to having individual JavaScript files is that Visual Studio recognizes it as JavaScript and says hey I'll run JSHint on it for you. A linting tool that evaluates the validity of your JavaScript, which is really handy. It told me I had a missing semicolon and that I should be using triple equals instead of double equals, which is absolutely true. So I have to go in there and fix those little errors. It's always a good idea to use some kind of linting tool, especially with JavaScript. Now you want to be careful using include directories I have here. I'm going to change this later on, but the main reason you want to be careful is you never know what order these files will be added together. In other words if app.js is added after controllers, well we'll get an error. And we'll only see that error when concatenation occurs and that will be live and that's not a good idea. So let's refresh, take a look at our page, and hopefully everything, yep, pops up. So it looks pretty good.
-
Template Precompilation - Visual Studio
(intro slide) I've done a bit more clean up, specifically moving the scripts out of the content directory and sticking them in the scripts directory. Here I have my user admin directory and our vendor bits. I've also updated the links on the page, as you can see here, to point to the scripts directory. Finally, I have also had to update our bundle so that it points to the right directory. And as you can see, everything is still working and it's always nice when everything still works. Well let's continue cleaning things up here and what I want to do now is to take all of the handlebars templates off the page itself and I want to run a pre-compiler. The thing about it is handlebars is a templating language, but it gets compiled down into straight JavaScript functions. And it's a whole lot faster when you precompile them. And it's also a little bit easier to use, because can separate them out into different files. So there's a number of handlebars, compilers that you can use with the bundler that comes with ASP.NET. Here's handlebars.net, this is an interesting project that you might want to use. It's not the one that I'm going to be using however, I want to use handlebars helper, it's one I've used in the past and I really, really like, but I do want to point this out to you. Because it's an interesting project that you might want to take a look at. Unfortunately the handlebars helper, the one that I'm looking for, is nowhere to be found when you search on handlebars. Don't ask me why that is the case, you would think that that would be a keyword in here, but it isn't. So if you enter handlebarshelper, all one word, there it is. And you can take a look at it over here and it's rather a snarky funny read me. However it is exceedingly simple to use, this is why I like it, it's also very fast and it seems to be fairly reliable. So I'm going to go and install that into our project and all I need to do after this thing comes down, after of course accepting the license, is to set up our bundle. As I mentioned all of the handlebars templates that we've created so far on the page are going to be squeezed into JavaScript functions compiled down. So I want to make a script bundle in the same exact way that I created a script bundle for our application code. We're going to have one for our template code. So I'm going to copy this right off of the read me file for the project and just paste it right in here. And I just need to make sure I have the name spacing, right-clicking and saying include the namespace. And then I probably want to update the names here. So what I'll do is I will open up our app here and by convention a good idea is to add a directory right under your app called templates. And inside of here are going to go our handlebars files. So I will tell it to stare at user admin templates and pull out every *.hbsfile. HBS of course being handlebars. So let's start easy. I'll put it in application.hbs and all I need to do is take the contents from inside those script tags and then paste it right in here. That is valid handlebars. We're just going to have an outlet for our application and we are good to go. The final thing we need to do is to put a scripts.render out on our page. And I'll just drop it right here below our app, simple enough. Okay well that should be all we need to do and indeed it is. Refreshing and it works. Now what just happened? Let's take a look at the script that we just included on our page. Ember.TEMPLATES, uppercase, there's that constant thing again, .application = a bunch of JavaScript. It's important to remember this is where you're compiled templates live, ember.TEMPLATES. Then after that it's going to give you the name of the template that you need. We're going to talk more about naming in just a second. But next can you imagine all the names that we're going to need to include here? The next one should of course be index. In fact the naming of these files should match exactly the name of the handlebars script ids that we used before. We'll just paste this in and this will be our index page for our app and we'll refresh and no surprise there it is it just shows up. Everything is working the way we want. We'll just keep on going here and we'll drop in users.hbs, because our template is called users. Then we'll go over here and we'll copy and paste well you get the idea. It's generally a good idea to start things out this way. So if you're thinking about building an Ember app you might want to take a look at using something like handlebars helper and figure out how it can help you from the beginning, so you don't end up filling your page with script tags and then having to go backwards. Well here's an exceptional case, look at this name, user/index. Can you guess where that template should go? We have to create a user directory and inside of it a file called index.hbs and I was hoping to trick you with that one, but I guess it seems obvious. Maybe it doesn't. If you're using rails in the Ember Rails gem it would do these things for you with a generator. It would just pop all these files in place and separate them all out, and then I think also arrange for their compilation. And well let's make sure we haven't broken anything and indeed we haven't it's still working and it's probably a touch faster. Although it's a little bit hard to judge when you're running it locally as I am here. Okay, well we have another interesting thing we need to do, what about components? Remember the forward slash we needed to use? That's the same deal. You have a directory called components and in here you got to make sure you use gravatar-image.hbs and that's going to go right off the root of templates and there we are, that's our last thing. Our page looks a lot cleaner doesn't it? Refreshing and hoping that that gravatar shows up, and it does. Let's add a note to Brendan here and make sure it still works, yep, everything is still working. We haven't done anything terribly magical other than to compile our templates, which is made for a better experience all the way around. Just for fun let's take a look at that script file for out templates again my oh my things have changed in here, lots of inline HTML being concatenated. And look at the name of this template, users/index. It's exact same name as the template before, except this time we're compiling it ahead of time, saving ourselves an additional step. The naming of these templates, as I keep mentioning, is really, really important. It gets you into trouble if you goof it all up, which I'm about to do in the next clip.
-
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.
-
Testing
Testing Ember
(intro slide) I'll be using QUnit today to test our application as that is what the Ember team likes to use and they have some helpers built for it, so why not I'll just use that. So here is the QUnit site and I have already downloaded the code and the CSS and dropped it into our vendor directory. And this means I'm going to need to organize just a bit more. Inside of our user admin app folder I'm going to create a test directory. And unfortunately that means that I'll need to create an app directory as well. I'll take our files here and I'll rename app.js to init.js and I'll move them into the app directory. And then, of course, go back in and update our bundle. We need to do this so our test files don't get concatenated and added to our application. Well the first thing I need to do is to create a JavaScript test file. The file that all of our tests will be located in. And I'll call this basics.js, you can call it whatever you like. There's no naming convention issues here. And then I need to have an HTML page that is going to be used to report the results of our test. And there is some ready HTML here on the QUnit home page and I'll just copy and paste that in. And let's rename it to our UserAdmin Tests. What you name this file, by the way, is up to you. There's again no need to follow any kind of convention. So I'll drag in our vendor QUnit CSS as well as the QUnit js framework file, making sure to put that above our test file. So I just want to be sure that I have everything wired up together here and things are laying out, the CSS files are working, before I do any heavy testing. So I'll go back to the QUnit home page and drop in their sample and then I'll right-click on Test.html and view the browser. And there it is, hello test. It looks like everything is wired up just fine. So let's remove our initial test, which is absolutely meaningless. And then I will drop in all of the app files we're going to need. So here's all the vendor files, Ember and moment jQuery, md5, etc. As well as each of our application files including our templates. Alright we are ready to write our test and to set up Ember. Now just like before we need to set a root element and this is going to seem a little bit weird, but remember we're embedding this on a different HTML page. This is going into our test.html. And on here we have a div called QUnit fixture. Now we could just drop it there or we could create a div with a separate id, calling it whatever if we want. I'll just use QUnit fixture, it doesn't really matter, nothing's going to be showing up. The next thing we need to do is to set up our application for a test environment. By using setup for testing we tell it that we don't want it to start immediately and instead we will tell it when to start by using reset. Setup for testing also tells the router not to track history so we don't want that to happen and have our test.html page get all goofed up. The final thing we need to do is to inject test helpers into our application, this will attach helpers to our app as well as to the global namespace that you'll see me use here in just a second. So let's refresh our page and make sure there is no error and there is not. We don't have any tests so let's write some. The first thing I want to do is to identify different groups of tests by using a module, this is a QUnit thing. And I'll give it a name of home page and the module does not contain the tests as you might think, however, it does contain setup and teardown. We don't really need the teardown but we certainly do need setup, because inside of setup we need to tell our application to go ahead and start. And we do that by calling useradmin.reset. In later tests if we call reset again it will completely reset everything about our application. Alright let's write out first test. And in here I'll just say it displays a list of links. And we will use a callback here and inside of our callback we'll put visit and then forward slash. This is one of those global functions that Ember gives us. Calling visit is emulating when a user goes to the home page. Down below we'll use the next function called and then. And this is all the stuff that happens after Ember has loaded up and run all the things that it needs to run to load the home page. Inside of here we can run our assertions. So I want to analyze the DOM, but what exactly am I going to be looking at? Well we know that we can use jQuery, so I'm just going to pump in an id here for the list of things that we can do to our app. And I'm going to call this home list. And it's at this point that I'm really happy that I'm using grunt to compile the templates because the change I just made well it would result in a failing test, if I hadn't been able to compile the template. Using grunt like this without Visual Studio makes things much, much easier. Although I suppose you could put this on a cshtml page and have a controller behind it, that will trigger the build if you like. Okay, so what I'm going to do is I am going to use the find method that's going to go and use a jQuery selector to find the items in the list and I'll set that to a variable. And then I just want to call equal. This is an assertion that is built into QUnit. That's it, I want to make sure that the length of that list is three. And it is, test pass. That's handy. And also much, much easier than I ever thought it would be. Okay well let's do another test here and let's do something a little bit more complicated. Clicking the User links takes you to a list of users. I'll simulate the click of a link by calling click, but click what? And in our index template here I need to give an id to the link for the user. So I'm going to call it userlink. So that's what I want to click. Again our template rebuilt itself, thank you grunt. So down below here I will just say click and I'll use a jQuery selector and tell Ember test helpers what I want clicked, there we go. Okay, well now what? What I want to do is to make sure that I can see a list of users, so let's identify our table. So you know we're going to have to do that. And I'll give that table an id of users list, call it whatever you like. Here I'm going to pull out user list, users list excuse me, and all of the table cells, why not? I don't know how many there are, so I might want to say something here that the length is greater than zero. That seems like a reasonable test to me, at least for this minute. So refreshing the click goes off and yep we're seeing the users list table and they're indeed more than one. I wish I could tell you that testing Ember is harder than this, it's really not. And it's quite nice that they have all these helpers where you can do integration tests the way you have just seen me do. I could fill hours talking about all the different ways to test your Ember application, but I'm going to pull up short, as that is a subject of another video. Perhaps I'll put that together for Pluralsight. For now I encourage you to read the testing guide up at Emberjs.com. You'll see a lot of the same information that I just showed you.
-
A Tour of Discourse With Robin Ward
A Tour of Discourse
I asked Robin Ward, the cofounder of Discourse, to do a code review of our user admin application. Robin is working on one of the largest and longest running Ember apps currently in production, discourse.org. Robin deals with Ember every single day and I find that really interesting. So before we got started I asked Robin for a tour of his code base. And I thought you'd find it interesting, I know I did. (intro slide) Hi Robin how are you? Good how are you? I'm doing well, so for those who don't know who you are you're Robin Ward. Yeah. You might know me as evil trout actually, a lot of people only know me as the cartoon fish. A cartoon fish, yes you are the, I don't know is it fair to say you're the frontend development lead on Discourse or you're the frontend guy, the Ember guy. Yeah like technically my role is like cofounder because I, you know, started it, but like in terms of where most of my skills are applied it's the frontend Ember side. Is putting up with Jeff, is that part of your (laughing), Jeff puts up with me too, okay. I poke fun at Jeff mercilessly, but I think he needs it, don't you think? I think he does and he likes it too, I think he does at some level. Okay what I wanted to do to start here first, thank you so much, I really appreciate you doing this code review, it means a lot to me, no problem. Because I, you know, the one thing I don't like to do is ever pose as some kind of expert. I don't think training needs to be done by experts, in fact I firmly believe it should not be done by experts. It should be done by people who are trying to understand that do that for a living, and that's me. What I want to do is I want to take a look at the Discourse app and see how you organized your code. And for those out there who don't know Rails at all, what I'm doing is I'm diving into the app folder this is where all the Rails code is stored. And then Rails puts all of its things to be compiled, de-minified, and whatever inside the assets directory. And then inside of JavaScript's you'll find, da da da daa, JavaScript. And inside here the first thing I notice, right, is Discourse. You have a directory named Discourse and up here I see Discourse, here I see Discourse, yeah. And there I see Discourse, but that makes perfect sense. Because you're naming your, you're saying this is where the Discourse app is, is that right, right. So it's like the client side app, this is the client side discourse app, right. If you make the, I guess the distinction is that the server side app, which is written in Ruby is kind of just like an API that spits back JSON. And then you have the client side app, which uses JavaScript and consumes that API. So in a way there's two applications, right, yeah. And Ember is quite complicated, I'm sorry, Ember is complicated too, yeah. Discourse is quite complicated just in terms of its interactions, it is beautiful, thanks. But I'm a little bit scared to click this, should I be scared, sure, no, no just go ahead. So, yeah, here you have things broken out by what they do rather than what they are, which I find interesting are you using Ember Rails for this? They are. And is this the way Ember Rails breaks things out? Yeah generally, I mean, you can do it a little differently you don't have to do it this way, but this is pretty much the conventional way using Ember Rails to lay things out, okay. So we know what components are, here's your breadcrumbs component, I should have stolen this. Yeah. It's very specific to us though, this one, because of no dropdowns for categories, hm hum, our component, I'm sorry, our breadcrumb is strictly no categories on the site, yeah. So this is neat because I hate to say it, but I think I can understand this, crazy, I know, it's nuts. Well it's kind of nice, you know, as you look at these things I have found that there's not too much wonkier involved necessarily. Because, you know, if you're looking at like .NET projects or even Rails projects, you know Gems or what not, you see some pretty intense stuff going on inside. And here it seems like most Ember apps follow your standard stuff, so yeah that's neat, okay. So you're components all make good sense and you've put your controllers separately in here, hm mm. Now we know what a controller is, but you have a separate file for each controller now, is this you doing this or is this Ember Rails? It gives you the option. I mean we'd actually gone back and forth as to like whether it's a good idea to group them all into one file or not. I prefer the separate file approach, but some people like to have, I mean especially if they're really short, they like to have them all in one file so you can just you know, not have to constantly switch, hmm. One thing I like though is like I use an IDE that, you know, I can just start typing the name of a file and switch to it, hmm mmm, So that actually suits my workflow really well, so I can just start typing invite and it'll show like invite controller and then I'll just hit Enter. Which one do you use? I use them, but I used to use sublime text, ahh. They both have a similar feature, yep, well with a plugin. So I've notice that you're also, it looks like you're over, not overriding, but you're basically stepping in and having your own object controller and you're extending Embers, is this what I'm seeing right? Right. So this is, we don't always use this, I mean we often just use the Ember one. But you'll see there there's two mixions included in the object controller, ones called presence, which is a helper for like kind of seeing if something is present, like an object. It's equivalent of Rails' present, okay, which is a little more flexible than is something null or not. And then has current user is our way of saying, like getting an easy handle on the current user. Now I should say that there are in Ember there is now a way you can do dependency injection to get something similar. Like Angular, but, right, say it isn't so. Yeah it is, it's similar to Angular, it's just designed to set, you can say inject the user into each controller that I have, hmm, and then it'll do that for you, but we didn't do that. In this case we just used standard object oriented programming with a subclass and it works well for our purposes. I have to say I would much prefer to have that in terms of we can talk about this in a bit, but the model force nonsense and needs, I just you know, yeah, that's so, that's really. To me it's messy, that's my opinion, by the way this is my opinion, sure. I have to be very clear about that, but we can talk about all this in a bit. So that's, alright that's interesting, would you recommend that when people write apps and they're not sure the scale that they're going to go to that they might want to have their own object controller, a rate controller whatever? I mean we didn't start this way, we did it as we found that we needed these things in many of our controllers. So I would not suggest starting that way, I'd suggest, you know, start with the standard primitives or the standard objects and if you need to extend them you can, okay. Because the object model supports that. So let's go take a look at your model. So you're, excuse me, Discourse.model, now this, I'm looking at this I'm, is this Ember data? No, no, it is not. So Discourse does not use Ember data. The reason is when we started Discourse two years ago I looked at the Ember data, like GitHub project, and it said not suitable for production use. And it wasn't at the time, it had gone through some major API rewrites. And I understand they're very close to 1.0 now, so that's really good, but at the time it wasn't stable. So Discourse just uses like a really thin wrapper on Ember's object model so that Discourse model is just again, it's something convenient, like a convenience extension like you just saw for controller. Just extends Ember.object and then we add AJAX calls to it and stuff. Oh that's, as you can see there, you see destroy there, it's just an AJAX call, yeah that's a great idea. And as we've seen already in this course, it's all based on promises. And that's just wonderful, so wouldn't be too hard to do this extension or inheritance if you will, I guess, I don't know I hate calling it that, yep, but that's what it is. It's not too hard to do that since you got promises everywhere, you could just have a base class that you then reuse and reuse and reuse, yep. So it's just, I mean you don't have to use Ember data, that's something that I think a lot of people don't understand when they first start. Ember data is really convenient and helps you out in many ways and solves many typical problems, but if you have a use case that isn't a great fit, yeah don't bother. And you know like Discourse we've been fine so far without it. Well in this course I didn't show you the first app that we built, I just showed you the second one in our little preview here. Now the first one I built was a GitHub wrapper. And it just went out to GitHub and pulled the API down and it wanted to show how to work with a restful style API that gives you the URLs that you can transition through. And it worked out really well, just used jQuery for that and I didn't need Ember data, although Ember data would work. But to get it to work you had to jack into the serializer and change things around and I'm like you know that's just a really bad abstraction if you have to go and doodle with it like that. So that's, yeah we just used jQuery and it worked out really well, I liked it, yeah. Okay, so I'm trying to think if there's anything else, the mixins, is this where the presence, yes there's presence, yeah. So walk me through this, I haven't really used mixins with Ember, it's not something we're covering here. Well what do they do and what's the point? I mean if you've used a language like Ruby it's the same, pretty much exactly the same concept, but the idea is if you have shared functions that you want to include in a class, you can just include the mixin without having to extend a base object. So I guess it favors composition of objects rather than inheritance of objects. That's rad. So okay, so here blank and present, that's what you were saying, yeah they're very simple. Yeah, I know that's great. So this looks at isempty and this.name, okay good. That's great and so you're just passing that into the Ember controller or any kind of class and as you pass that in, it gets mixed in and you're good to go? Yeah you just, you can then call this.blank or this.present and it just works, rad, that's neat, geez. So this is the thing with Ember, I swear, you're like, you have no idea that you could even do this thing and then all of the sudden you talk to someone like you and it's like, wow I didn't know I could do that. Well that's cool. So let's see what other mixins you have. You have presence and, scrolling is an interesting one, oh AJAX is cool too. AJAX is essentially like a wrapper on jQuery's AJAX thing, just to make sure that it always returns promises. So you can see there's like some logger messages that you just scrolled past. So basically we don't want you to use AJAX the old way, which is, you know, where you provide a callback. We always want you to return a promise. So this is our little thing. And then we also have a little helper there, you can see where your mouse is, there's that URL fixtures. So in a testing environment we can just have a variable and then have that return from this automatically. So it's easy to just, you know, test what an AJAX call should return. Wow Robin that's some good work right there, thanks, you're not bad. Now, thanks, it's also, it's not all just me, it's like it's an open-source project. So we've had a lot of contribution, I thought before we were going here you said it was all you? No, I said that, oh you mean before we were recording, I'm just giving you a hard time, I can't help it. Because you're going to be, you know you're going to be lighting me up in just a few minutes. So I just got to get my digs in as I can, that's a good idea, yeah. Alright, is there anything that you'd want to highlight here that we could take a look at before we get going? No I think that's a pretty good intro to our code base, so I'm good to go. Yeah well for those watching and out there that want to see some whip crack Ember, go through the Discourse app, see if you can understand what's going on. And you know if you have to fire us some questions and we'll do our best to answer. Well thanks for the tour Robin, no problem. I should say that Discourse is, you know, it's a fairly big app at this point. So if you're brand new to Ember it's good to poke around, but don't feel like too bad if you're a little alienated by the size, because it is a pretty giant project at this point.
-
Review and Refactor
Code Review With Robin Ward
I keep saying I am not an Ember expert, I'm just someone who loves to know how and why things work and I like to take what I've learned and share it with others. As such I think it's critical to have your code reviewed publically by someone who knows what they're doing. So I asked Robin Ward of discourse.org, one of the biggest Ember apps in production, to review what I have done. Let's see what he has to say. (intro slide) Well this is my application, it's not quite as big as yours. It's set for learning a smaller application is better, yeah. I have a whopping three files in here. Yeah but you know it works really well, I gave you the tour before, but yeah just to remind you this is good old Jon and I like this role functionality, I think it's pretty cool. And I got it to work actually and I really want your feedback on this to see how badly I contorted this. So, anyway, where to you want to start taking a look at this stuff, Mr. code reviewer? Well let's start the router, because I feel like, you know, Ember applications are URL centric and so it forces you to start from the URL and drill down. So I think that's a good place to start, yep, so there you go. So this is a pretty typical router I think if you look at an application, you have a resource for your users and then user and then within there you can edit info, change password, change roles, and add notes. Yeah, so that's all pretty standard stuff and I think it's pretty explanatory. Like if someone looked at this they'd understand where everything goes, okay. So you would start, what's interesting, so you're starting at the router to understand the application, which I guess makes sense, because Ember is a URL based deal, I suppose, right. Okay, so I didn't do anything wrong? No that's all pretty good I think, oh that's boring, yeah there's no complaints there. So let's, so users is your, I guess, list of users, right? And user is viewing one particular user, yeah. So let's talk about that. What I wanted to do is to have a multifunction screen here if that makes any sense. So I wanted to be able to say last 50 users, of course this only three, but let's just say you have 100's and 100's of users and I wanted to show who just signed up, right? So this was all pretty easy to put together, but I also wanted to have the ability of typing in a search right here, right. And I ran into some trouble trying to get this to all go together, it was not easy. And what I thought about, well what I thought was well Ember will probably want you to have a search screen and it's dedicated so I go to search controller and do a bunch of stuff. So I actually had to kind of hack this together a little bit and do some fancy pants stuff. So yeah, check this out, here's that users controller I was telling you about, I am not proud of this, Robin, not proud of this at all. Because it goes in reverse is what it feels like. Because normally in the route, right, in the route you say here's the model and the route goes out and instantiates the controller, drops the model onto it, and off to the races, right, right. I'm saying this.store.find here in this load users function, right, and I'm setting the model basically forcefully. And it's passing along, if there's a search term, so this is being called twice. It's being called when the templates loaded, right, and so if no queue term is sent into the server it just returns at the top of whatever server, or user is. And here I've just passed in how many I want, three, right. The rest of this is just, you know, title changes as we type stuff in, but then down here when you hit the search button, it calls load users again and it just reloads itself, right. The reason I dislike this is if we go over to init where the route is declared, which is right here, I have to use setup controller. And I found this to be really unintuitive, because if I didn't do this, if I just said here's the model, right, the model is this.store.find whatever users, then every time I will go back to this search page, after searching. So like right here if I click my name and then say okay I want to go back to the users, every time I do this, this state would be gone. And then this would be a list of users, the last three, right, you know what I'm saying? Yeah because by default it expects you, well like if you just linked to a page and you're not passing in a model, it'll call the model hook and get all that data again. So it would reset your search, right, right. Is, and you don't want that, no is there a better way to do this or what I read is got to use setup controller, okay. Setup controller is the correct way to do it. (slide telling what is happening with Robin and Rob) Okay let's go down here to this other thing, this was intense to write, this change roles controller and, or it's just a role controller. There's a couple things about this that I really enjoyed, I like being able to say I don't necessarily have a role route or a roles route, it wasn't like a resource and you know you're not going down this very structured path, right? I like saying that I've tried to change a role and I got this list here and I have this arbitrary view I want to render. That's called role and I'm going to give it a controller and it just works, I thought that was neat. The thing that I didn't like so much is trying to figure out all of the syntax, but I'm going to walk you through it. Needs worked, which was nice because this is a nested controller and it went back up and got the user, that's good. And then I'm just aliasing the controller here so I can go grab the user. Assignments I thought was cool, if that worked, right, hmm. User is assigned, I'm not very happy with this code because I felt like I should be able to use some kind of Ember computed for this, yes you should be able to. Do you know what that is off the top of your head? Well before I just want to show you a cool thing. You see here you have get role.getid, you could actually just say role.id in the string there, no, oh this. Yeah, getrole.id, yeah, yeah. So I've just, like, yeah but that if, the cool thing about that is it gives you safety. Like if you don't have to constantly check if something's null. So let's say you said get role and role was null, this will do the safe check. So you can, like, let's say you have a property x.y.z, ah huh, it'll just, it'll always make sure you won't get a null exception if one of those things is missing, ohh, it'll just return null. So that's really cool, that's nice, but let's, yeah let's take a look at this. So it looks like what it's doing is it's saying in assignments return any of them, yep, where the id is equal to the current auth. So we go into change roles, right, and so Brendon, the supreme master Brendon. So right, right, so these are all the roles that are in the database and yeah it's just passing a check. Is Brendon an administrator? And, okay, so let's take a look at it again and so there will be if it looks like, it looks like this returns just user is assigned, so this is the particular role. You're seeing are they, do they have that particular role, right? So it's only a true or false if the, that's right, exactly right. So in that case it's what you could do is you could use the filter property thing that Ember has. So it looks like here you're filtering where role.id is equal to id. So I think what you could say, at first, is rather than that is you could just say return this.getassigments.filterproperty, return this.getassignments, yeah and then work towards what I think is the best solution. So what filter property does is you can pass it a property to check and a value to check it against. Okay, so could say role.id is the first parameter and then I guess this.get, like just this.getid I guess, because this is an object controller, so it should proxy. So let's try that first and see, oh yeah, and see if that works. Okay, because if that works that's a one line difference, which is pretty nice, right? Yeah dude. Alright, da da da daa, and you win the day. So that's a lot simpler. Alright, so we're clear about what that did, is assignments is a collection or an enumerable and an enumerable's in Ember have that method filter property where you can supply it with two parameters. One is the property that you're filtering on and one is the value that you want that to equal. And it'll return the first one where that matches. Right, and so in JavaScript this is a truthy statement or, you know, so if it, yeah, if it's null then it would be false if it's not null then it would be true, right. So that's exactly right, very nicely done dude, I like this. And by the way for those out there before you saw me have two at symbols in there, the reason I had that was that this was on a Razor page and you can't have an at symbol because it thinks that that's code. And so I changed that to just be at each because now we're just in a plain old JavaScript file and so it's just something to be aware of. Yeah, so that, I think that works pretty well compared to what was there before. So, let me show you why, what's going on here really quickly so, I showed you a little bit before, but just by way of refreshing people's memories. In the models I had to do a many to many, this is a Rails style way of doing it, it's kind of like has many through, I guess is the way you'd say it. Well I had to formalize that relationship into a membership and call anything. So, yeah, roles have many membership, users have many membership, and if I add and delete these records then I was able to have a controller over here using web API. Memberships controller that was dedicated to that relationship. A little bit of extra code, but it seemed to work out in the end, I'm okay with that. So that's what that is, what do you think? I, that looks pretty good to me, I mean I'm not the biggest expert on Ember data as I said earlier, but I mean it seems to model it in a fairly straightforward way. There might be a better way of doing it, so hopefully Tom or someone else can help you. It's, yeah the line where you say, underneath var role id, you have var membership equals this.getassigments.find where the role is the same. Actually isn't that exactly the same? I think that is exactly the same? It is exactly the same, yeah, so you could just this.get user is assigned. Oh and that'll bring back the, yeah, oh man, and it's even cached and everything, so, Robin you're a Nicky. Alright, so there you go, just blow that all up, yeah. So that looks good to me, that's a lot simpler. Yeah I'm not liking that, this is just me and my quirky tendencies, I'll leave it, I don't care. So right this is the same code, bam, done. And cached like I said, like it will not recalculate that unless anything changes, there we go. Alright, so cruising through here I wanted to show you, oh yeah, this filter where in the add notes so let's go over here to Brendon and add a note to Brendon. I wanted to make sure this was sorted descending and I wanted to show how to do it on the page as opposed to just having the server code sort it for you. A hmm, you know what I mean? And so here's what I did. I looked forever so sort properties is typically what you would use on an array controller right, yes, but since this isn't an array controller it's just an add note controller, I guess it could be an array controller, but I didn't set it that way. Anyway, so why did I not do it this way. Because, right, because the array would be user.notes and does that make sense, how I'm explaining this? Yeah, so it looks like, okay, so this has its own model, pretty much, you know what a weird thing to me is about this, is that I feel like it's doing two things. It's called add note, but it's really also a note list, isn't it? Let's say it is, like so conceptually I would probably have like a notes controller that lists the notes and it's an array controller and then I would have an add note or just a method to add a note on it so. So, alright, so inside of here what might work is, let's see, user, yeah let's look at notes. Add note, so what you're saying is probably to take that out and (working) Oh so yeah, controllers.user.note, should be good. Okay cool, and so then here we're just each note in notes, so now it's just each note or just each. And then just input that, note, okay, yeah that looks good. And so now what I can do is go back into the controllers and there's the add note controller, right, and so what I would do is I'd say useradmin.notescontroller, right, and this ember. Would you imagine me ever doing this just a few months ago? No. Are you impressed Robin? Yeah, you think, I could tell you just knew, right? Like that's the difficulty with learning something like Ember which is, you know, has a lot to learn upfront, but once you learn it, it just kind of rolls off the tongue right? Yeah, like you end up writing less code, but there's that initial investment, that initial peak that we expect people to climb over right now and that's a little hard. Ah ha, bar coding at its best. Bam, ta da, there we go, yahoo. So you get another one for this, yeah. So that's a little nicer, right, because like that controller is only concerned with listing right? Yep and that's, this is much cleaner, okay. Much cleaner, dude thanks for walking me through this, that was good, no problem. I want to try to think of any other cranky points, oh I can see one right away, okay what? So you have a user add note controller there and I see inside there you're doing a jQuery call, oh right, yes, okay. So I should explain this, this is on purpose, hmm mm. The reason why is in the demos I've done before with Backbone and Angular and whatnot, I wanted to show how you could or could not use jQuery if you had to, just saying if you had to, there could be a reason. So here I just wanted to show, like, you know you can use this and so I left it to show that jQuery actually worked on the DOM after it was dynamically constructed by Ember. Ah ha, Backbone does not work that way. And so I thought that was interesting to show, cool, but actually, we should just say with a caveat don't do that, right, don't do this it should be an explicit property, yeah, it should be a property. Now having said that you can use jQuery with Ember, I just want to be clear that it should be within a view. A view classes are allowed to use jQuery and in fact every view class you create, if you say this.$, has a reference to that view, right or a model if you're using AJAX stuff. Right, I mean, in that case, I'm talking more about the jQuery that manipulates the DOM, not the jQuery that, you know, goes to a, does an AJAX request or whatever, but yeah it's totally cool that you can mix jQuery, but the view is basically the trust level or I don't how to say it, the silo. Like you should surround your jQuery by a view so that it's easy to, you know, refactor and you know exactly what it's doing and you can tear up and tear down the events as views get inserted and removed. So, okay, so yeah just don't do that, but it's cool as an example, interesting. Well yeah and the other thing I was getting around to, and this is going to sound a little hacky, but yeah when you make this an alias or a property up here that you, like new note, let's put it that way. It gets cached, and you know, you could put a property method on it and say it'll change whenever the model changes, right, but in this case it doesn't have a model and it just gets kind of weird. Yeah, in that case you could also use setup controller so that, that's true, to set it to an empty string when you enter the controller. Or I could just issue a click, yeah, okay. Well is there anything else that you can think of that you, we might have missed, what do you think? No this looks pretty good. One thing that I would, suggest users to look at for the future, I don't know if you want to get into that in this lesson, but, um hmm sure, there's kind of a movement towards ES6, hm mm, and module using ES6 modules with Ember. And Ember is really pushing this forward in the future. So there's a project called Ember app kit that you can try out if you just want to load a little app locally that uses ES6 and poke around and see how that works. And they have a really nice layout and basically each file, it looks similar to this, but it'll automatically compile all your code and put it all together and stuff like that. Now they recommend one file per controller and one file per model and stuff like that. In fact, out-of-the-box it wouldn't work if you tried to do what you're doing here, but it is very organized and it's just, it's a nice thing to peak around to, okay, see what the future of where Ember wants to go. Good, Robin I can't thank you enough, no problem, this was really fun. I love refactoring sessions, they're just, ahh they're great. And I'm not looking forward to one with Tom. Actually I think this code is pretty good and pretty straightforward. Like I said, the Ember data stuff, maybe there's a couple things, but otherwise I think it's very idiomatic, so. Right on, I don't think you're going to hear many complaints from them, whoo hoo. And well hey take it easy up there in the frozen north, yeah, okay, feeling sorry, talk to you later, no problem, see you later Rob.
-
Refactoring
(intro slide) While talking to Tom Dale during our conversation about Ember, he took a look at the code and echoed something that Robin said when you were looking over Discourse. And that's kind of how Ember does have pretty much a convention when it comes to storing your files on disk. So at first I kind of just wanted to go with whatever was simplest and to me I hate file explosion. So I just went with having controllers and init file and models. I just kind of thought, we'll keep it easy, but in looking over the Discourse code base, which I really like, I find it to be pretty darn simple the way they have everything put together in there. So let's take a look at how they did it. Again this is a Rails application and everything inside the assets directory, inside JavaScript, gets compiled and minified and concatenated and all that stuff. So we can set that up as well with bundler, but we don't have a gem that will generate this stuff for us and do all the compilations. So we'll have to do it ourselves, that's okay. Okay, so how did he organize his stuff? Well taking a look what you can see here is discourse.js and a discourse directory, where it has the app files in it. So taking a look at discourse.js you can see he's setting window.discourse as a global to an Ember application. And he's using create with mixins, which I really think is cool and he's passing in a bunch of mixins as well as some configuration, including root element and so on. So I'm not too sure what the rest of this stuff is doing. So I think I can do this much. So let's go back to our app and restructure it just a little bit and inside of scripts I'm just going to have an app called useradmin.js. So we'll just call this useradmin.js and then let's see we have a directory called useradmin and inside of here what they tended to do is to have everything split out by directory. So we might want to have controllers (working) and test I will leave in there as well. Okay, this looks alright, of course it's all broken. So let's just take a look at controllers in here and we'll just have to jam it on out, it would be kind of cool if we could refactor this, can we? No. So I will just snip that and then add JavaScript file users. Okay, a lot of cut and paste, but that's okay. You'll notice that I'm using an underscore here and, you know, there's a couple ways I could do this, of course. I could follow this proper casing, title casing, if I wanted to, and it would be more .netty, that's up to you. Whatever your file name and convention is, go for it. I went with underscore because that's what Rails, I'm sorry, that's what Ember does, so I just went with it. Okay, so we've done that and we've killed our controllers file, so that means I can delete this, groovy. And now in init let's take this and we will put it up here in useradmin. And just for safe keeping and clarity I will put in window.useradmin, this Ember.application create, alright. And then the router, hmm, where does Robin put his routing? I wonder if it's in a routes file? Let's see. Discourse, okay application routes goes in the routers, okay, yep I'm good with that. So inside, and we'll do this, we will cut that out and inside of routes. (working) It looks good to me, application route. (working) Well that was it. And now we should be able to delete the models file here, okay. Well as you probably have figured out, everything will break. The neat thing is we don't need to worry about that necessarily because we have a bundler, which is lovely. Now I just need to tell it what to do. So here the templates we're going to leave exactly the same. For user admin I'm doing includes on individual files, but I can change that. So now I'm just going to change this so that the very first thing that gets included is useradmin.js, which is right here on the root. And then what I think I can do is just include the entire directory of user admin and I wonder if that'll work? So we'll say include Scripts/user-admin, ah ha, and then it's going to say which files and it'll give it this glob of *.js. And then recursively search, yes, we'll say true. And I believe one more and. Oh my goodness, okay. What are the chances? What are the chances? Let's see if it works and then we'll come back and talk about what I just did. So flipping over here, hmm. Module is not defined, oh right of course. Haa, so close. Well we can't really have a test directory inside of here, darn. Hmm, so maybe what we can do is just say, let's just move that out, what do you say, and move it in here. And what I could do is just say user-admin-test, yeah that looks good to me. Alright moving that out let's go back and reload, oh oh oh, something, something? Oh my goodness. Whoa, that's pretty rad, it looks like everything is still working. Okay, well that was a, that was not a huge refactor, it's just a reshuffle. Now looking at this, you know I got to tell you I am not exactly sure how I feel about it, but something that Tom mentioned to me is, embracing the complexity of your application. And Ember is built for big complex apps, this one could easily spiral, you know. You could easily build on top of this to do all kinds of things. And one of the nice things about having a structure like this in place is it makes it easy for you to keep building it. And one of the things that I've learned about Ember as I've been using it is, that's, you know there's a lot to it and it's difficult stuff, but it's really simple to add things to it as time goes on. So this structure, well verbose, just specifically yeah, I'm not a big fan of a file containing six lines or even two lines of code, but hey man, maybe that's just what you do when in Rome when using Ember. So this is, this kind of refactoring makes me happy. I dig refactoring as we've done here, but we've kind of opened ourselves up to a bit of problem with our tests. Before I was able to just drag in those files our init controllers and models file, but those have disappeared and they no longer exist. Now there's a couple of ways that I can fix this. The first is to make this just a plain old razor view, drop it in views, maybe even under account, and put user-admin-tests, if I wanted it to be a page. And then I could add in at render scripts and I'd be good to go. If you are not happy with my second solution that I'm about to offer you, that is a perfectly viable way of doing things. And I'm sure you can figure out how to do it, just copy and paste this stuff out, put .renderscripts right here or write scripts.render and make sure to output your template. And you compiled stuff and your good to go, you can run these tests and everything is fine. Unfortunately then the tests will become part of your app. So make sure you lock your controller down for administrators only. Okay, that's thing one, thing two, if you want to be adventurous and break free of relying on Visual Studio for everything, which I highly recommend and that's not meant to be snarky. It's just meant to expand your horizons, that's what we do when we learn things. Using node is really, really fun and we are already using it for our tests so why don't we just continue on in that vein. I have, in my package.json file, which I'll find here in just a second, package.json here. I have added grunt-contrib-concat, that is the grunt task set for concatenating JavaScript files or any files really, it could be CSS or any files on your system you can concatenate. They also have uglify, which will minify, if you want to use that, but what I just need to do is concatenate all these files together. That's all I need do because I want those to be output here. Alright, so how did I do that? Well let's take a look at the grunt file. I just did npm install, grunt-contrib-concat and here I'm saying load those tasks for me please. And that gives me a task called cancat. And then what I did is up here I configured that task and I said here's what I'm going to do for the distribution. Here's my source files, I have scripts, useradmin.js, these are concatenated in order. So, it's good for us that we put useradmin.js at the top. Then I just said go into all of the js files that are inside and use a recursive glob for a subdirectory search in the UI. And then output this at useradminapp.js and call this whatever, that's that. And then down here what I did is I told watch, remember we used watch before, I added these scripts here, I added those files there so it'll watch our js files. And when that happens I want it to run two tasks, I want it to run Ember templates and concat. So basically build the app every time I change anything. And it's quite fast. Flip over to PowerShell and if I run grunt-concat, it's pretty quick, there it is. And it runs concat dist, which is the distribution, this guy right here and it's done, Scripts/useradminapp.js is created. So if we go over here to useradmin, I hate that warning. Look what it made for us and this is cool. Our app is appearing at the top, which we want, and as well as our adaptor and then on down the line. Pretty neat stuff. Notice that it's not minified, which is good because we want to use this just for testing. And we're probably also going to want to get in and separate points as well. Oh wow, it looks like it's dropping the templates in, I guess that's okay. So what I can do then back on my test file, is get rid of template and then drop in useradminapp here, cool. And then inside of our tests I should be able to go and view in the browser, hopefully. Oh whoa, it can't find this. Right, so what I need to do is change all these relative to, I knew that, scripts, okay. absolutely from the root, whoop whoop. Alright so let's try that again. Yeah there we go, very exciting. Okay, well so the last thing I did here, if we take a look at our grunt file, the last thing I did is I created obviously a watch task and I've updated it. And I set the default to watch. So if we want to run our tests and work on Ember only you certainly can, we just say grunt-watch and it's going to run the watch task now anytime we change any of our js files in here. So if we go to, I don't know, actually we can get rid of templates.js, let's do that. Let's go to, oh ha, it saw that change and it fired. Ah geez since it's creating that you know what, we don't even need, do we need? So that's outputting, yeah, so now that I'm thinking about this we need to move that from scripts to templates. Let's do this. Because templates is being, this templates file, right, is being concatenated in, so let's delete that out. Goody. Ah and now that we've done that we should have also have user-admin-templates, perfect. So that means back in our test app then we need to make sure we drag the templates as well. Alright. Okay, there was a little bit of jockeying right there, but yeah we changed that and you can see everything's going off. So it built a couple files for us, user-admin-templates and it also did user-admin-app.js. And just to confirm that my tests are running, let's view this in the browser one more time. And yep, there it is and this is currently running and done. Na ha ha ha ha ha, much better, okay, I think that's it.
-
A Discussion With Tom Dale, and Goodbye!
Discussion With Tom Dale
You've heard me express quite a few opinions in this course with respect to Ember, some good, some of it challenging. I thought it would only be fair to ask Tom Dale, the cofounder to Tilde.io, and co-creator of Ember js, his opinion on my opinion. This is largely a conversation with very little code, but I included it here because I found his thoughts fascinating. (intro slide) Sitting here with Mr. Tom Dale, can I call you the master mind godhead of Ember, is that accurate? I prefer supreme dictator for life, thank you so much for taking the time to do a code review with me, this is, I consider it a privilege, you wrote the dang thing and now you get to see how bad my code is. Oh I didn't write anything I'm a thought leader not a programmer. A thought leader, okay, well that's I just lead thoughts all day, let's see if we can lead some of your thoughts, alright. Alright, well what I want to do is I want to talk about my thought process a little bit, I also want to show you my code. I'm sure there's tons of areas of improvement and I also have a few questions for you. So, first off I have to say I got through this, it's been a long and rough road with Ember and me, as you very well know, but I got through it and it like it. Well I'm glad to hear that, it's about time. You've worked on me long enough and here we are. Well after you got spanked so hard in and I'm not surprised you off, came around, oh man. Tom is referring the cage match that he and I did in Oslo at NDC. I have to find the link to that video, yeah, okay let's jump in. This is my incantation, this is what I do to kind of lead me through what I call the Ember process, right. When I have a new URL, a new thing, I want to transition from like index to users or whatever. Alright okay, what do I need to do? And then I recite this to myself because I have a URL, because Ember likes URLs. Well I would say their web likes URLs, the web likes, sorry, right because Ember and web are the same thing, anyway. What do you think of this little incantation, do you think that sounds about right? Yeah, I think this is the right mental model, you can think of it as a flow and a lot of users, a lot of developers who are coming from a server side background, they think in terms of the request, right? The request comes in at a URL, you have the method type, so is it like get or post. Then you have the path URL and then that usually goes to a router of some kind. And then the router gets the controller and the controller invokes a template, hm mm. And I think that's largely the same thing here, except you don't have a request, you have a much more complicated UI, right. You're building apps that are more complicated than what you've built with the server side framework. So it's the exact same model, it just nests. Speaking of controllers and models, I noticed that you said a controller invokes a template, is that right? Ahh well I don't know that it invokes the template, but that's one way to think of about it, it certainly backs a template. Like a template is plugged into a controller, right. So I put this tweet out the other day, because one of the things I've been saying in this entire course is if it helps you can think of a controller as a view model or a presenter. And for me that opened up everything. Because all of a sudden I understood that the model is your raw data and that gets kind of proxied or grafted onto the controller, and the controller just kind of becomes the model I guess is one way to think of it, yep, but then anything you want to add to it functionality wise, if you want to have computed properties, you want to have actions invoking, then you put it on a controller, yep. For some reason that just never sat right with me, but the minute I said, oh this is a view model, this makes more sense. I actually put that on in Twitter, which is the slide you can see here, and I got some interesting responses. So what's your response to this? Yeah I thinks that's, I think that's a great way to think about it. We chose the controller terminology, we inherited it from both Viscore and Coco and the Smalltalk MVC pattern, but there is so many, I think MVC is such a natural approach to designing rich UIs, that many different people have invented it and adapted it and defined it. And every time you go to that pattern it's almost like a game of telephone, that's right exactly, where people are creating different terminology for things that are largely the same. And I think the view model is a really great way to think about the controller. I think if we'd called it a view model we would just sort of confuse Coco programmers and we would have the same problem. Well that's makes sense because that's your background, right, and you use to work at Apple as a matter of fact. So, knowing the little that I do about objective C and iPhone development I do remember that each screen has a controller behind it, that's correct. And so that would, okay, so now this is, and the controller in Coco serves the role of a view model, okay. So this is one thing that I posited right in the beginning, that if, you know, Angular and Ember actually have a lot more in common than they have not in common. It's just a different way of doing it. And a lot of people have pointed out that these frameworks are actually MVVM all the way down. It's not really MVC, and of course that's just misplaced syntax, poker right? What do you think about that statement? Well I would say Angular describes itself as MVW, where the W stands for whatever, whatever. I think that's what they say on their website and I agree that Angular and Ember are definitely designed to do similar things. I'd say that Ember, the thing that Ember does different from Angular is there's almost like a layer on top, mm hmm, which defines the roles of objects in the system, okay. So Angular gives you a set of primitives that you can compose however you want. You can put whatever you want on the scope, right, and then that scope gets passed to your directives, and that's basically how an Angular app works, mm hmm, but Ember has really strongly defined notions of the roles of objects in the system, mm hmm, right? Well that's interesting because the scope in Angular is basically the view model. And the controller, in the controller you decorate the scope, right. It seems roughly the same, yeah it's roughly the same and I think the big difference in Ember is in, well in Angular through the dependency injection API you can access whatever you want, right. So, controllers can request any particular service, you pass around models to your directives via the scope, and there's basically whatever access you want is granted. And that flexibility could feel really good especially when you're getting started. And conversely in Ember instead of you requesting certain other objects in your app, Ember says we think that all templates should have a controller. We think that routes should have access to controllers so they can set them up. And so these roles aren't, access to these different objects isn't something that you are requesting, you are given the things that we think that you should use. So if you have a different idea of how you want to build apps, that feels pretty bad, and if you're getting started building Ember apps you have to really understand that architecture, because otherwise you'll feel very frustrated. But the pro is that because these boundaries between objects are enforced by the framework, switching between different Ember applications feels very similar. Once you learn that pattern of how these objects talk to one another and what their roles in the system are, it's a much easier to reason about and keep things clean as the app grows, okay. Well that all seems reasonable and I have to say so there's a good and bad to what you just said. There's a very definite architecture, kind of holds your hand, you walk down the path, and if you adhere to it and understand it, you will have a whip cracking app in no time. In fact this GitHub explorer that I've created was 90 lines of code and I've never made any single page app as integrated as that was. I mean it was amazing. I think I did master detail, detail, detail, I got down to like the, yeah, fourth level of inception, it was nuts. And it was really powerful. Someone made a quip on Twitter the other day that when you write single page apps with, I don't know, Knockout or even well Angular to some degree, you're using JavaScript. And when you're using Ember you're just pushing framework pieces around, you don't really write JavaScript. Well it's very declarative and I was actually at the Portland meet up last night and one of our presenters said something I thought which was really interesting, which is that it has a lot of the same properties of a functional language, right. Where you're just describing the data and the flow of that data and you don't really worry about the side effects very much. You just describe the system and you let the framework kind of figure it out for you, which could be very powerful I think. One of the things that I was kind of thinking as I was building these apps out was, at some point, you know, when do I get to do my own thing, right. But then I thought, you know, what's more important solving the business problem or, you know, or writing my own code? And I thought, well geez I have totally solved what I've needed to do here and I've had to work my way around a few things, but it was a pleasant experience. And what I want to do, though, is I want to transition I've said a couple of things that are a little opinionated, shall we say, yeah. And I certainly don't want to say those things in a vacuum and just think that I'm completely right. So, I don't want to go into a negative spot, but can we talk about a few things? Sure, alright you're always good about this. The first thing I want to talk about is the API and I know you're probably sighing, people bring it up a lot. I made the statement that Ember's API has changed significantly and that was kind of one of the trademarks of Ember. That you go back to it after a release and lots of stuff has changed. I also made the point that since you guys hit 1.0 that that is slowing down. The thing that I said was rather declarative and I will stand by it, but I want your opinion on it. I said that this is one of the number one reasons people use Angular is because of the shifting of the Ember API. Do you think that's true and also what do you think about the movement of the API now and into the future? Sure, so I totally agree with that statement, I think the reason that Angular is so popular right now whereas our growth in the last two years has been less, has been because Angular stuck to whatever the original API they designed was. And so there was a whole universe of documentation and examples and blog posts that people could rely on. Whereas for the first, you know, two and half years every time you'd Google for a documentation or answers to Ember you got out of date information. And that was incredibly frustrating and people really had to believe in our message to get through that pain, mm hmm. That being said, the tradeoff for early instability is that you get tremendous stability in the long term So since version 1.0 Ember has been released we have a very strong commitment to semantic versioning. So we haven't introduced any backwards incompatible changes since the 1.0 release came out. Good job, whoa, which is really awesome. And in fact we're so committed to this idea that we switched Ember's release model over to something very similar to Chrome, mm hmm, so every six weeks we cut a new stable release. And at the same time we cut a new beta release that has any potential new features for refactors in it. And that allows us to quickly determine, hey didn't we accidently break anything. And if we, then we add it a ____ test to make sure it doesn't happen again, mmm hmm. And so the delta between each release is very, very small and the app that I work on for my company we just stay on that release train and we have been upgrading very smoothly with no breaking changes from 1.0, well actually we were pre 1.0, but now we just upgraded to 1.5 I think is the latest release. So the most important thing is that we have built a foundation that we can keep iterating on for the next several years, easily. And we've got a migration strategy to adopt all of the features coming in the web platform. So things like ES6 modules, object.observes. If you look at Angular, Angular's doing Angular 2.0, ah huh, Angular 2.0 is a ground up rewrite of the framework, yep. And a lot of the things that they're doing the ground up rewrite are things that we already have, like a data framework or a robust router, ah huh. So I don't really know what's going to happen to the entire ecosystem of Angular 1.0 apps, but I do know that anyone adopting doing Ember today is going to be building on a stable foundation that's going to be around for years. Yep that's the thing that I found with Angular versus Ember and it's a point that I made here on the slides as we were going along. Everyone is always wanting to know, Angular or Ember, Angular or Ember. And one of the interesting things about Angular is this is the demo controller that I actually showed in New Zealand that, yeah, hey it is so easy to make a controller, look it's a simple function. And unless of course you want to have resource and you know services, well oh we have to now change everything and I remember you pinned me on this on stage, it was pretty funny. And then of course if you want to minify it then it means you got to change everything all over again, yep. And if you want to do nesting it's an entirely different thing too, right. And so I don't want to pick on Angular versus Ember, but what you just said actually matches the development process of Angular, right. If you want to go to Angular 2 you're going to have to change everything and I think that's a fair, a good point to make. Okay, I also said something else that was a little bit challenging, so I made the point, and I keep making this point throughout the course, that there are just some things with Ember that make no sense, on the face of it, let's just say that. Until you Google a little bit and then you find the reason why, you go oh well okay, that seems reasonable. For instance, here I was doing a simple example to show people how controllers work and let's do an action. And also let's put the renderedOn you know out on the page. And it wouldn't show up, in fact I tried to invoke it like someone might do in Angular or Backbone or, you know, even Knockout, right. Can't do that with handlebars, it doesn't want arbitrary code in the page, makes sense. So to get around it you have to flag it as a property. And I was looking at this and I thought, you know it makes sense once you understand it, but reading this is wonky. You know, and I feel like the Ember API was written by an engineer and that's a fairly strong statement and I don't mean it to be, it's bad. It's just something you have to get used to, what do you think about that? Well I think I disagree with that, so the first thing is that we made a very intentional choice to not allow arbitrary functions in the templates, mm hmm. And the reason for that, I think what you alluded to, is purely a performance one. So Angular, for example, if you invoke a function and use its result as a bound value in your template, literally anytime anything could possibly change, Angular has to reevaluate that function, mm hmm. Now it's great in demos, but if you're building larger apps you end up having functions, like in this case it's creating new date object, but you can imagine it doing something even more expensive, mm hmm. You start getting a number of those on the screen, literally every time the user like clicks the page or moves the mouse button. You potentially have to reevaluate all of those functions, mm hmm. So that's purely a performance thing. This is actually a principle that comes from computer science theory called the principle of universal access, mm hmm, and the basic idea is you can express everything in your app or everything in your UI, not as verbs, but as nouns, mm hmm. And I think the thing that you're experiencing is not, oh this was designed by an engineer, oh. Although I think that's a totally legitimate reaction, because I remember my feeling when I learned this when I was learning Sproutcore, mm hmm. It really asks you to completely flip your mental model of how you build these apps, mm hmm, right? So I would say that most programming models have you think in terms of verbs. So go get this value, recomputed this thing, set it in the DOM, these are all verbs. Ember wants you to still think about things in terms of nouns, what is the thing that you are trying to express? And then what's the minimum amount of computation that you need to do to express that? And the point is your template doesn't know whether renderedOn is a static value, whether you just set it, you know, statically or whether it's computed dynamically. That implementation detail is completely opaque to the template and that's a very important design decision, mm hmm. I will grant you that adding .property as the function _____ is, definitely feels a little bit weird. That's the thing I was wondering about, is, you know, I mused on this as I was writing out the app and I was thinking well, okay so what, how do I feel about this, is .property, especially with the bindings that you can put in. Is this any better than say masking this with something like computed property and then taking a callback let's say and have that be a built in Ember thing. Ah well you can do that actually and that's what many _____ script developers do. You can do Ember.computed, sure we cover that later on, but I don't know, I honestly don't know because then like we've been saying you need to understand what's going on and especially the caching and the observing of other properties and the changing. I think that makes perfect sense once you get to know it. So, I don't know, I guess I find myself in the middle a little bit, I am an API nut, meaning if my API feels weird I won't. Because you know without that, I have a couple of open-source projects that I run. And one that I'm working on now is called biggy and I'm trying to tackle the concept of storing a document in the database in a relational database, right. And the idea that you can have additional fields in there to describe it as, I was like how can I communicate this with the developer? And it won't release it until I get that API right, because to me that's the most important thing. Yeah, I think this is something we inherited from Sproutcore and I personally find it to be a very pleasant API, yeah, but you do have to kind of understand what it's expressing, absolutely. And, it is interesting in that it forces developers to understand it, you know what I mean, yeah. And I think that if we could auto-detect dependencies, for example, if we could do the thing that people would, they kind of expect to happen out-of-the-box, mm hmm. If we could do that we absolutely would, but we, there's a tension between creating the absolutely most intuitive API and giving people a foot gun. Like something that's going to make it really easy for them to blow their foot off, right. And it's tempting, especially because things like that demo really well, you can make some really awesome presentations that look great at conference talks, mm hmm, but we're just not willing to give people APIs that they have to scrape halfway through their app because it got to slow, mm hmm. And that's the steep learning curve, yep, right there is exactly what you just said, you have to learn this thing because it'll work for you. Okay, I'll buy that, I'll give you a pass on that. Well let's end on this, this is a summary slide that I showed right at the beginning at the course and basically wanted to answer the question, what is Ember? And your tagline is, it's a framework for building ambitious client side applications. And so I took that and kind of massaged it a little bit, just a client side JavaScript framework based loosely on MVC. And then I had a slide here that says, what is Ember, really? And because people want to know okay, enough with the raw raw what is it really? I called it an opinionated MVVM framework with a difficult learning curve, when mastered will make you incredibly efficient in writing complex code, complex apps. Does this sound like a reasonable summary or do you think this is a little bit too strong of a summary? Ah no I think it's roughly correct, I mean I think would quibble maybe a little bit with the difficulty of the learning curve. I think that the things that you need to learn to use Ember are the same things that you would need to learn if you were building an architected Backbone app or Angular app. We just ask you to learn a couple of those things up front, mm hmm, rather than encouraging you to kind of hack along in the jQuery spaghetti style that you might be used to. And then realize halfway through oh crap I have to go back and refactor my entire app. So in that sense I don't think we asking anyone to learn anything that other people don't it's just maybe the point where we ask them to learn is a little bit closer to the front, true. And you brought up a really good point too, when I say this has got a difficult learning curve I'm putting this up against Angular and something like Knockout. If you put it up against Backbone, I would say Ember is rather pleasant. I would say, I would also say it depends on how you've thought about building applications before because I know a lot of people that come from Coco backgrounds or even who have built large Backbone apps, mm hmm. They fine Ember, once they understand how it these objects, the roles of these objects in the system and how they work together, mm hmm, they actually find our model a programming model in our terminology far easier to understand than isolated scopes and translated directives. No you guys like to use that a lot, isolated scopes, oh my goodness that's pretty funny. But let's, okay we talked a lot about Angular, but let's talk a little bit about Backbone, because there is a prime difference when developing Backbone versus Ember. And for me I think that huge learning curve that I had with Backbone was to learn to program against an evented model. And evented programming just doesn't some naturally to people unless you've done client based programming, which a lot of web developers really haven't. So I think that's what makes Backbone so difficult for a lot of people. I've found this completely missing in Ember, I mean I know it's there, I know you can do observables and whatnot. Our observer is I should say, but Ember's programming style is not really event driven per se. In other words, I can, I have an action and when this action goes off this thing is called. It feels very, dare I say, procedural? And so talk to me about that. Yeah well I think because computed properties are such a core piece of Ember and because they are so powerful as a concept, you end up, like I said before, describing your app as data and the relationships between that data. And then when one thing changes over, all the way over here on this side of your app, something on the completely opposite side updates without you writing any code, right. Just because you described the relationship between this data. I actually like to think of it almost like an Excel spreadsheet, it's like imagine if you could use Excel to program awesome web apps. That's basically what Ember is right? You describe the rules and then once you've described the roles you don't do the work to keep all that information in synch, the framework does it for you, hmm good point. And I think, so the thing to keep in mind is that Backbone is 900 lines of code, mm hmm. I haven't checked recently but Ember's easily over 20,000 lines of code, yeah, so if 800 lines of code solves your problem as well as 20,000+, you should use Backbone. And I also think you shouldn't be using an abstraction if you don't understand what that abstraction is buying you, mm hmm. Well hey that's why people are here at Pluralsight right? That's right, yep. So I would just say that the experience is going to be much different, but Backbone is not a lot, it's 900 lines of code, it's something that once you understand what it's doing any developer could write over a weekend. So talk to me about Ember versus Knockout, because a lot people are going to look at this and say, well I like Knockout, why would I ever use Ember? Sure, yeah Knockout is a great tool and if it's making you happy then I definitely think you should keep using it, but the thing I would say is Knockout is just a data binding library, mm hmm. And if that's what you need, it does a phenomenal job of it, mm hmm, but it still causes a situation where your app, your web app, relies very heavily on the server for its UI, mm hmm. So, routing for example, like you click on a page, your app was feeling really fast a second ago, you click, boom, it responds. You click, boom, it responds. And then all of sudden you click a link that just happens to be a link that goes to the server instead of being in JavaScript, mm hmm, and now oh, oh my gosh it takes a second or two seconds. It creates this really inconsistent experience for the user, where they don't know when they click is going to be fast or is it going to be slow, mmm that's an interesting point. And that's really unpleasant. And the other thing I would say is just from a complexity point of view, right, which is that when you build an app that uses Knockout you have a different Knockout mini app per page, right, mm hmm. Like there's different apps per page, mm hmm. And the question is how do you share state between all of those different apps? Because remember that when you click a link the browser throws away everything in memory it loads the new page, and you have to reinitialize, mm hmm. So not only are you, you know, loading all the JavaScript and CSS reparsing it again, but now you've started from a blank slate, you got tabula rasa, mm hmm. You bring up a really interesting point which is, you know, you better be sure about the complexity on this page. Because if it grows into, well you know what I should really show this in a separate view, for instance, then you're kind of in trouble. I used it for a checkout page at tech pub, and what I wanted to do is test if someone's logged in and if they were, you know, show us certain things. That worked great, but I remember in the back of my mind I was thinking is this checkout page going to really stay this simple page, right. Where a credit card is sent off to PayPal. And I thought about it and thought about it and thought about it and was like, you know, yeah I'm not going to really open this thing up to any more functionality. That might not be the case if you're doing a GitHub browser, yeah, you know, yep, and you think well I might now want to show issues. Well what about submitting an issue to, what about this net? Well then you back yourself into some trouble, so I guess the point that we're both raising is, you know, for simple pages Knockout's wonderful, but beware of where that's going to scale in terms of complexity. Does that sound accurate? Yeah, absolutely.
-
Goodbye
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.