Tips & Takeaways
- Routing - good old routing in Angular 2 is everything you would expect from Angular 1's UI-Router (like route parameters and nested routes).
One key thing to remember is that you can define empty string as a route path for the base route (the base route is just the url of your site with no "/something" after it):
{path: '', component:MainComponent},
{path: '**', component:Error404Component},
- Lifecycle hooks - Lifecycle hooks for components are a great new feature for Angular 2 (and something introduced in AngularJS at 1.5 as there was a push to replace ng-controller with components. The big lifecycle hooks to start using are ngOnInit for initialization logic, ngOnDestroy for cleaning up anything that might cause memory leaks (*hint hint* web sockets / observables), and ngOnChanges for running code when one your @Input's value changes.
- In Angular, it's an anti-pattern to put initialization logic in the constructor. The constructor should only be used for dependency injection, and the initialization logic should be in the ngOnInit lifecycle hook.
- Lukas emphasized this next thing briefly, and it was something that I personally loved moving from Angular 1 to Angular 2: encapsulated CSS styles. Once you work with it for a while you're like " it's nbd bro, really", but honestly encapsulated css styles is way better than not encapsulated css styles (which is what we had in AngularJS and in general with vanilla JavaScript). I have worked on angularJS projects where styles were "bleeding" outside of the HTML elements that they were intended to be styling and influenced other elements thereby overriding the styles that we didn't want to be overridden. Needless to say, it was a huge mess, and all across the world it has caused web developers quite a few painful and difficult debugging sessions. In Angular 2 we use the @Component metadata property "styleUrls" where we can enter a path or paths for css (or scss/ less) files, and in this way we can specify for each component that we only want these css files to affect the component instead of all css files affecting it. In general this allows you to make shorter, more generic css class names, and I'd still recommend using BEM naming system with the "double undies" and "double dashes".
- Directives in Angular 2 are basically components with no template. They are just a class with metadata and have a selector metadata property, but where a component's selector property allows it to be used as an HTML element, the directive's selector allows it to be used as a custom attribute. I could go on a rant for hours about how I think the name "Directive" in Angular should be completely throw out and just be replaced with, "Custom Attribute" or "Angular Attribute". Sorry Misko, but the word "directive" intuitively conveys nothing to me. If you look up directive in the dictionary you should see that the meaning is something like, "serving to direct; directing" or "an authoritative instruction or direction; specific order". I know it originated in AngularJS, but how the heck this got chosen to mean custom component thing with or without a template... I have no idea. Anyway , you can use this @Directive metadata on a TypeScript class to create custom attributes. Using this power well can be tricky, and I personally admire people who can make good use of directives.
- A service is meant to take the burden of business logic out of the components and into a separate, encapsulated place. Services can provide utility methods or store data as properties, and they embody the singleton software design patten. With dependency injection we can easily get a reference to some service in a component, directive, or another service. In general, always use the @Injectable() metadata for the service classes (and don't forget the parentheses because the Injectable decorator is a function!).
- Interfaces !== Inheritance
The bread and butter of component-driven architecture is composition, and interfaces complement composition extremely well. As a general rule of thumb, inheritance is an evil ogre of doom that will cause heaps of bugs in your programs (ok, it's not that bad, but if you are using inheritance in Angular 2 I'd bet there is a cleaner, or at least as clean, way to do it without using inheritance). The key thing to realize is that even though the syntax for implementing an interface and extending a subclass may be similar, the way they are used are very different, and in Angular 2 you should prefer composition and implementing interfaces over extending subclasses. - Lexical scoping in Es5 JavaScript means madness for us programmers, and we found ourselves using things like .bind to awkwardly pass our parent scope into callback functions to change the meaning of this. In TypeScript this trouble is pretty much completely avoided simply by using fat arrows ( => ) instead of the function keyword. Idiomatic Angular 2 code makes heavy use of the this keyword, but you can pretty much always assume that this is referring to that class instance of the component, service, pipe or directive that you are working in.
- NgModule was an addition very late in the development of Angular 2, and as something that changed right before the framework left beta it really frustrated a lot of people. The key to thing remember is that your components go in the declarations array, other libraries and modules go in the imports array, services go in the providers array, and the main component that kicks off your app goes in the bootstrap array. Another fun thing to notice is that a module is just a TypeScript class with some metadata just like the other Angular tools we have to use. Here's an example:
@NgModule({ declarations: [ AppComponent, SomeOtherComponent ], imports: [ SomeExternalLibrary SomeOtherModuleFromAnotherTeam ], providers: [ SomeService, SomePipe ], bootstrap: [AppComponent ] }) export class AppModule { }
- One person in the class asked, "Is it ok to put something like {{someService.serviceproperty}} right in my template? Lukas cited the Law of Demeter and said that doing something like this breaks that law. I agree with him here in that the template shouldn't be "reaching into" other services. Instead, the template should only know about it's corresponding component TypeScript class, and any service properties should be saved into local fields of the component.
- The Angular CLI supports SASS and Less out of the box, which is pretty flipping nice and convenient. I recommend using scss instead of css because... why not? This will only remind you to take advantage of those extra features for writing even cleaner code that you get from preprocessors (well hopefully, haha).
- If you want to upgrade the Angular CLI version for your project, usually the easiest way is to just use the Angular CLI to scaffold out a new project, pick up your old src or app directory, and drop it in overwriting the one in your new project (something I like to call brute force upgrading).
- This is a mnemonic acronym for remembering the steps that go into creating new Angular components, services, etc. that I'm pretty sure Lukas came up with: CIDER. It stands for:
- Class
- Import
- Decorate
- Enhance
- Repeat
- Professor Frisbee's functional programming is definitely something worth googling and checking out.
- Avoid doing "import star" syntax which looks something like this:
import * as firebase from 'firebase';
- In your templates instead of property binding like this:
<modal-header [show-close]="true">
<modal-header bind-show-close="true">
<button type="button" class="btn btn-default" (click)="cancelClicked()">Cancel</button>
<button type="button" class="btn btn-default" On-click="cancelClicked()">Cancel</button>
- There is an HTML color named papayawhip.
- Lukas's dumb components had absolutely no functions in them! That totally blew my mind, and I really want to start copying that. It's almost laughable how amazing to me it was to see this. Most people agree that dumb components shouldn't have "business logic" in them, but Lukas really took the idea to the next level by making dumb components that were purely just metadata-annotated classes with @Inputs and @Outputs. He even called this, "the epitome of what a component should be". You might be thinking, "yeah, but don't you at least have to have click handler functions that then fire the @Output EventEmitter up to the parent component"? Well, that flow is exactly what happens, but Lukas had a cleverly simplified way of doing it where he just put the emitting of the event right in the template. It looked something like this:
<button type="button" (click)=”selected.emit(item)”>Do Stuff</button>
- You can make local template variables with the hashtag sign ( # ). For example:
<form novalidate #formRef=”ngForm”> <button type=”submit" [disabled]=”!formRef.valid”>
- You can use the "safe navigation operator" by putting a "?." before object properties in your template if the properties may not exist. For example:
<h1>Hero’s name is {{nullHero?.firstName}}</h1>
- Property binding is better than string interpolation for "src" attributes:
<video [src]=”widget.videoSrc”></video>
- In Angular 2 you pretty much have to embrace the idea of "Component-Driven Architecture" which of course is way better than Angular 1's controller-driven architecture.
- In general, try not to go more than 20 lines per function.
- The Angular module system makes all components in declarations available throughout all your templates.
- You need to import and inject, for example, a service into a component, because you have to declare it at a framework level so Angular knows about it and also at a language level so your bundler knows about it.
- You should invent a "domain specific language" naming convention for variables and methods. In his examples Lukas loves using "widgets" and says the example app is for an imaginary widget producing company. I do think this advice makes sense. Definitely don't give stuff (properties / methods) random names like "b" or "tony", and don't name them overly generic things either; name them things that are relevant to the business domain in which your app is operating.
- The #1 book Lukas recommends for someone asking how to be a great Angular developer: Clean Code. This is interesting because that particular book has nothing to do with Angular. And this feeds into another thing that Lukas said: "If you want to be a great Angular developer, just focus on becoming a great developer".
- In Angular 2 you have the option of template-driven forms and reactive forms. Template-driven are very similar to Angular 1 forms. Reactive forms are cool too though, and they give you a nice syntax for binding everything to your TypeScript class.
- Using an ngModel on a <form> element will “under the hood” create a FormControl on the element.
- When I asked him what he would say to some smart aleck kid who says, "I don't need container /presentational and smart/dumb components. It's not that much code. I can just have smart presentational components!" Lukas replied that sometimes it is ok to do that. You want to get it working fast, with as little code as possible. But then, to make things more maintainable in the long run, it's a good idea to break things up and have container / presentation components. I'm really glad he gave that answer because it's realistic in that when you're hacking stuff together from just a blank white canvas, it's fine to just throw stuff in the component to get it working. However, if you want truly elegant, "clean code" with straightforward unit tests then you refactor it down to smaller blocks that compose. You encapsulate things and make everything very focused and specific. Lukas also threw out this great quote which I think deserves its own bullet:
- "Make it work, make it right, make it fast" - Kent Beck
- Lukas then noted that, "Some people get so caught up in making it right that they are never able to actually make it work". You don't want to let "making it right" slow you down and get in the way, but once you have working code and you understand what the "right way" is, go ahead and refactor it to what's right!
- Lukas recommends NOT using JSONP because it can expose your data to some security risks (he didn't really get into the details but strongly recommended not using it in your production apps).
- The Angular HTTP service is a somewhat more low-level api but at the same time time gives you more options and control. For example, you may often use RequestOptions with a "headers" property to set Content-Type on your requests.
- Two event sources that give really rich examples of observables: user inputs (especially move muse move / keyboard events) and webockets.
- You can convert HTML Dom events to observables like this:
Observable.fromEvent(this.getNativeElement(this.btn), ‘click’)
- If you are building a typeahead that calls out to some backend api, use ".debounceTime" to have it "wait" for a split second to make sure the user has finished typing before you sent out another request to the backend.
- Although he did't talk about it during the lectures, I was talking to Lukas after most students had left, and he was really excited about using Apollo with GraphQL. The "better than yours" statement for GraphQl was this: suppose you have a huge list of objects and you want to select 10 of them. In standard relational and even NoSql (firebase as well) you can pretty much only pull either one object or the whole list and so getting those 10 objects might require 10 independent network requests. Well, GraphQL pretty much lets you get as many points of data as you want all in one go (as least that's how I understood it). I admittedly haven't used GrahpQL much, and I'm still kicking myself for missing Lukas's Wednesday night meetup where he talked about it, but I definitely want to check it out at some point.
- It was interesting that after the talks on the second day a guy came up to ask Lukas for advice. He was basically like, "yeah, I'm 16. I'm a sophomore in high school. What should I do?". I think he and his parents just thought the normal thing to do would be go to college, but Lukas surprised me by basically saying, "why go to college? They aren't going to teach you any of this web development stuff. You might as well just spend six months on these videos courses and then go get a 80k+ job in NYC". Although I didn't make a very convincing case at the time, I was trying to play devil's advocate and say that college is way more than just the information you take away from it. College is a whole experience. It's the transition from you being a kid living with your parents to being on your own as an adult. It's an opportunity for you to live in a dorm, an apartment building of 100 of people your age that you can befriend and hang out with any time. It's a place where you can get in-person training on calculus, philosophy, economics, writing, computer science theory, logic, and all these other things that I took classes about. I think Lukas is not realizing how little these kids really know about development. They haven't read the hundred of books he has. They don't know how to use the command line, or git, or what unit tests are. Sure, someone could pick it up and hack something together, but companies don't want someone that learned how to program 6 months ago. They want someone who really know's what's good and can hit the ground running. And the thing is that computer programming is extremely difficult; or rather maybe I should say it takes an extreme amount of experience and thoughtful practice to become good at it. It's easy for me to say that I could have just gone and be a web developer instead of going to college, but the thing is I didn't even know I wanted to be a web developer at the time. In fact, my freshman and sophomore year I was taking actuarial exams. I can specifically remember being totally lost trying to work with AWS ec2 servers and hating it, posting all my issues on the Electrotank forums, and paying literally $100 (which is a LOT for a broke college student) a month for a few months because I had no idea what I was doing. I went to a big university and loved it. Lukas was not as much of the "work hard, play hard" type as me, and when I brought up the fact that skipping college would mean you miss out on partying with friends and screwing sorority girls he would just shrug and say, "I guess, if that's important to you". Either way, if the kid decides to really get into web development and Angular 2, he'll probably be just fine.
Unasked Questions
- One thing that I wish I had asked him about was how you can easily load certain components only when the user navigates to a specific route as opposed to loading all the things up front when the page starts up.
- How run Angular 2 Protractor tests on my CI server? I mean I'm pretty sure the right answer is just to hook it up to something like Sauce Labs, but I really wish there was an example project using Travis CI that I could look at as an example if how to set everything up.