Building Awesome Web Apps with Angular 2 Angular 2 Building Blocks 1
This course has been updated! We now recommend you take the Angular 9 Fundamentals course.
Transcript from the "Angular 2 Building Blocks 1" Lesson
>> Lukas Ruebbelke: So the main building blocks of Angular 2. So there are some additional build blocks. But these are the most important ones. Modules, routes, components and services. And do let me tell you why I chose those. So this is The Angular 1.x Big Picture. When you simplify it, this is what generally, an angular app looks like.
[00:00:24] Which at the time, when I did this first diagram, Angular one was super confusing. People were like, so it's just eight pieces? Like okay, that makes sense. So you have your module, config, routes. Then you have controller, scope and view. And then if you need to extend your controller.
[00:00:39] Then you do that into a service. And if you need to extend your view, then that becomes a directive. Well, this is the Angular 2 big picture as I see it. So you have your module, your routes, your component and then your service. And so, let's step through these.
>> Lukas Ruebbelke: The module. So we have module support. Initially at a language level, but now at a framework level. So Angular 2 uses ES6 modules for, essentially the core mechanism for composition. So when we create something we export it, so that we can import it somewhere else. So this is ES6 modules, and the goal is to keep these modules fine-grained and self-documenting, clean code all the way.
>> Lukas Ruebbelke: And so what we have here is a ItemsComponent, and you can see here that we're exporting it. But then you can see that for ItemsComponent, that we also can import other things and make that available. And so using ES6 module syntax, this gives us the ability to organize things at a language-level.
[00:02:06] And then at RC five, we got essentially module support at the framework-level. So coming from Angular one, this is not a new concept. But we found that with, for instance ahead of time compilation in different things, we needed to be able to compile or know the entirety of the application, at compiled-time and not run-time.
[00:02:30] And so this is why we introduced NgModule. And at first, I was a little bummed out. But then I began to realize, this actually cuts down on a lot of boilerplate. So you can actually import a dependency into your module, and then it's available to every essentially, component or dependency within that module.
[00:02:48] So this is the organizational mechanism at the framework level. And there's a few properties. These are the four that I think are the most important. So when you create a component, that essentially exist in the view, then you put that under declarations. Then within your imports, you can import the list of modules or sub-modules that, that module needs.
[00:03:17] Providers. So this is where you import your services. And then, bootstrap. So this defines the component that should be bootstrapped. So what this looks like is declaration. So what goes in here? Well, we have an AppComponent, ItemsComponent, ItemsListComponent, etc. So you can know that because it's in the declarations, that this is a component that's going to live in the DOM at some point.
[00:03:45] Then we have our imports. So we have our BrowserModule that essentially contains Angular core, everything we need to run in the browser. FormsModule, HttpModule and then we have the Ng2RestAppRoutingModule. That's a mouthful. Thank you Angular CLI for generating that for us. But this essentially is a sub-module that contains all the routes for our application.
[00:04:12] And then providers, ItemsService and then we're bootstrapping the AppComponent. If you create a module or sub-module that has dependencies or things in it, and then you import it into another module, then that becomes available to that module. So as you could see here, that is we're importing essentially the FormsModule.
[00:04:32] Once we import the FormsModule, then all of the functionality of forms then is available to this main AppModule.
>> Lukas Ruebbelke: And then once we have our module in place, so previous to RC five, we would actually just bootstrap to our top-level component. But now instead of bootstrapping to a component, we just bootstrap to a module.
[00:04:59] So this is one of the pieces that you just kind of have to know. But essentially we're saying platformBrowserDynamic(), so this is really just the browser. We're saying, platformBrowserDynamic().bootstrapModule this AppModule. And then what that does is, it'll bootstrap the module. But then it will look for the app component and then basically try to spin up the application from there.
[00:05:28] Let me ask you, hop into the code real quick. I thought I had one more slide. But I'll just show you real quick, so if we go into our index.html and let's go into our app component.
>> Lukas Ruebbelke: You can see here within our component, we have a selector of app-root.
[00:05:52] If we go into our index.html, you notice that that exists as app-root. So the selector, this is how this component begins to live within essentially, your DOM. So every component, if it's going to essentially be instantiated, it's either via really the most common way is by actually adding it to a template.
[00:06:15] You can essentially load these in via routes but generally, at least especially for the top-level of the component, you need to actually add that to your index.html. So be sure to take your top-level module, look for the top-level component, and from there make sure that you've added it to the DOM.
[00:06:37] Now the upside is, so let's actually just look here. So, bootstrap: [AppComponent]. So it's bootstrap your module, which then bootstraps your top-level component, which then lives in your index.html. And so we'll get more into this when we get into components, but I just wanted to call out that when you bootstrap something, you need to have that top-level module selector in your index.html.
[00:07:05] So routes.
>> Lukas Ruebbelke: So routes gives us the ability to essentially take a certain state and say, for this state I want to take this component and dynamically load it into the page. So for instance, hopping back into the browser, you can see here that the URL is essentially a serialization or a representation of the state that we're in.
[00:07:35] Up here we have widgets, but then when I click Items, now I'm essentially saying load the state for the items route. And what you have is a router outlet, that's essentially taking when you're on Items, it's just dumping the items component into the router outlet. And when you have widgets, it's taking the widgets component and putting it into the router outlet.
[00:07:59] So you essentially have this dynamic piece in your code. That would just dynamically load components into it depending on your router state. So if we go back here, essentially we define a route table that is of a type route that just, in the simplest form, has a path and a component.
[00:08:21] And then from there, when you hit upon that path, it will just take that component and load it into the router-outlet component, which is just something that's is in the DOM, it's that if you're at this path, take this component and load it in here. And then we can just navigate to routes using routerLink, or router.navigate if you wanna do it imperatively.
[00:08:45] This is a gotcha here that the router uses history.pushState, so you need to set a base-ref in your HTML so that it can effectively handle that. Cuz prior to Angular 2 you could do HTML 5 mode, but by default, it was essentially hashtag whatever your route was. And so now, you'll notice that if I go back here, that on widget or items, you'll notice that it's just forward slash and there's not a hashtag in there.
[00:09:16] And so you can set that up if you want. There are cases where you need to do that. But as a whole, it's HTML5 mode by default.
>> Lukas Ruebbelke: And so what we have here is our routes. Which is essentially just an array of route objects. And we have a path and a component.
[00:09:44] So if we go to the items path, then the items component gets loaded into the router outlet. And then what we're doing at the bottom is, essentially within our imports, we're calling router module for route and passing our routes in. And what that does is, it returns fully configured routes, essentially service, that we can then pass along to our application.
[00:10:09] So we'll get into more of that today. And components. So components are the atomic building block of Angular 2 applications. And so when I say component, if you'll remember on the Angular 1 big picture, we had essentially a component or a controller, scope and a template. Well this is essentially, we've combined that together into a component.
[00:10:34] So component has a template and a class. And so a component class is just an ES6 class. So you'll notice when I say that Angular 2 is, essentially because it's standard-based, we're letting really ES6 the browser do a lot of the work. So our components are just ES6 classes.
[00:10:57] Properties and methods of the component class, then those are available to the template. Dependencies are injected in the constructor. And then we have life-cycle hooks, so that when certain events happen to our component we can tap into those and leverage them. And so what this looks like is, we have an ES6 class called items component.
[00:11:24] And we're implementing the UnInit interface, which you can see here at the bottom. It's ngUnInit(), so it's saying when all the data bindings have been initialized, then go ahead and call this method. Items and selectedItem, because those are properties on the class, we can them bind to them in the template, and we are injecting ItemsService into our constructor so now, you can see with an ngUnInit() then we can call this.itemsService and it's available for us.
[00:11:55] Now on the flip side. The template, surprise, tells Angular how to render a component. And so this includes, really data bindings, but it can also contain other components and directives. And so, when we get into component-driven architecture, we're gonna see a lot of this, is you really want small fine-grained components that contain other small fine-grained components, so not only can you bind the things, but you can contain sub-components, and that's actually encouraged to do so.
[00:12:30] Angular 2 leverages native DOM events and properties. So what this does again, is it reduces the need for custom directives. You no longer need like ng-mouseup, ng-mousedown, cuz you can bind directly to that DOM event, and handle it that way. This is actually one of my favorite features, I have a bonus module at the end that I may talk about briefly.
[00:12:55] But Angular 2 by default leverages shadow DOM. And so what this allows you to do is, essentially encapsulate styles to your component. So if anybody has ever done components that get shared across an organization, that you have something that's beautiful and somebody does something totally irresponsible so it's just I think all buttons should be 200 pixels wide.
[00:13:20] And they set some top-level super generic button with 200 pix or something, and it just blows your component out. And that's the problem with CSS is the C stands for cascading and not for component. But using shadow DOM, or by default emulate a shadow DOM, we can actually encapsulate those styles to the component itself.
[00:13:45] So that's super super cool. And if we have any UI designers or developers who are doing component libraries, they should just be like, yes! Because this is going to save a lot of heartaches, a lot of tears and the fact that when I give this to somebody, they're not gonna have the ability to mess it up, or it becomes a lot harder.
[00:14:09] And so what we have here, in ItemsListComponent, we're actually using template URL to point to the template. But one thing I wanna call out is, you can also use template and do your templates inline. And so this is generally by default, where I start. The idea is that if your components are fine-grained enough, the template should comfortably live within the metadata.
[00:14:40] So for instance, if you have a template that's 200 lines long of HTML, you can kinda stop and say, this doesn't feel right, maybe I'm doing too much and I need to break this into smaller sub-components. So really kind of the general rule of thumb, is that your templates should be small enough to go in-line to the component.
[00:15:03] Now, this is not something for me personally that I'm willing to make into a religious war. I'm not going to belittle you, or get upset, or offended. There are cases, for instance if you're doing forms or somebody else is doing the HTML markup, to do it as an external template.
[00:15:21] But as a whole I recommend, start here and then when it becomes cumbersome stop and ask why. And there may be a case to break out or not. Yes.
>> Speaker 2: Mike has a question from the room, is the native DOM used where it's available? Excuse me, native shadow DOM used where it's available?
>> Lukas Ruebbelke: So you can turn that on. You can turn on, essentially you have view encapsulation, you can turn it to native. So by default it's emulated. And so Angular is gonna basically emulate shadow DOM. But unless you are developing in a vacuum on one browser, I think that if you go to native shadow DOM, you are asking for trouble.
[00:16:09] Just because the browser support is so poor for that right now. So it's not by default, it's essentially emulated, which does work. But you can essentially set that on the component is view encapsulation.native. Good question.
>> Speaker 2: Follow-up, well not a follow-up, another question.
>> Lukas Ruebbelke: Yes.
>> Speaker 2: Annie is asking would UnInit run before or after the constructor?
>> Lukas Ruebbelke: That is a fantastic question. So UnInit runs after the constructor. And so I'm getting ahead of my slides a bit, but what you wanna actually do is keep essentially initialization logic out of your constructor. Because one, it makes it harder to test, but more importantly is you may have a dependency further upstream that depends on an asynchronous call.
[00:16:57] So your constructor is going to run right away. But that incoming data may not be there, when the constructor runs. And so you will throw an error. So what you wanna do is actually move your initialization logic into ngUnInit, cuz then you know that, hey all of my bindings that essentially I depend on have been initialized, now I can safely go ahead and depend on the knowledge, that this particular property is available.
[00:17:25] And so that was a mistake I made when I started learning Angular 2, is I would basically bind to something, essentially passing something into my child-component, and I would just start doing things in the constructor. It's like, this thing, it's not here yet. It's en route from the server.
[00:17:42] And so ngUnInit runs after the constructor, and that's really where all of your initialization logic should live.