Angular 2 is around the corner and there are mixed opinions about it. Some people can’t wait for it and other people are not any happy with it. Why is that? People are afraid to change, thinking that they wasted their time learning something that is now going to change in a radical way.
Angular is a great framework, but it is 6 years old, and that for a framework is too much. The web evolved a lot since Angular inception in 2009 and the Angular team can’t simply implement those new stuff without breaking half of the actual implementation.
So if they have to break Angular to be able to make it “modern”, it is better to write it from scratch. How so? Angular itself has other problems like the complex syntax for directives, 5 types of services that confuses new users, writing stuff in the wrong scope…
They decided to kill 2 birds with one stone. A new simplified Angular with all the new stuff like shadow dom and also none of the Angular 1 problems.
So my friend, don’t worry, Angular 2 will rock.
Module loader
In Angular until recently, we always had a problem with module loaders. What should I use? Maybe the classic way of endless script tags?
Require.js
? Browserify / webpack?
… Angular 2 uses the standard System.js
which is a universal module loader that loads ES6 modules, AMD, Common JS…
Now with this module loader, we just need to import the file where we bootstrap our app, and we are good to go:
Now inside our
main
file, we just need to:
Thanks to our module loader, we now see where this
bootstrap
and App
comes from and also, we don’t need to create a <script>
tag for every javascript file in our application. Ah, this also means, no more ng-app
.
There are more advantages that we will see soon.
Components
In Angular 2, we don’t have controllers nor scope anymore. So… what do we use to create a page like
login
or home
? We use a Component
. What’s a component
? In Angular 2 we have two types of directives. The ones that adds behavior to a DOM element and the ones that has an embedded view (template). The first one are called directives
and the second one are called components
.
Ok, so a
component
is a directive that has a template and that is what we use to represent a page in Angular 2. A component
is simply a class:
What makes this class a
component
? In Angular 2 we have annotations. Annotations are a way to add metadata to a class. So there is an annotation called Component
which we can use to say that a particular class is a Component
:
So here we are saying: Hey MyComponent, you’re now a component which is going to respond to the “my-component” selector. Meaning that we can now do:
Yay, now our component is usable in our html using the
selector
name we gave to it. And please notice that we didn’t use myComponent
as the selector. That is useful.
Ok, but a
Component
needs a template. There is another annotation called View
which is used for the template:
So now
MyComponent
is a component that responds to my-component
selector and outputs a template with a Hello, World
.
We can have more than 1
View
annotation in a component. We could define a template for desktop, one for tablets, one for mobile, one for tv…
Also, since using a
View
annotation was mandatory, they decided to merge all the View
properties into Component
. So in the cases that you only have one view, you can do:
We can also use data binding:
We used the class constructor to set a message. No more scopes.
And that is how we create our pages. When we use the new router, we just need to pass a component to it instead of the old template+controller.
Directives
So how do we create a directive in Angular 2?
As we did with
Component
, we use an annotation to define our directive.
In Angular 1 we had the DDO (directive definition objects) which we used to develop our directive. The problem with the
DDO
is that it is a bit confusing with stuff like:- transclusion: What does that word even mean?
- controller vs link: When to use link and when to use a controller?
- compile: What should I do in there?
- scope: scope: false, scope: true, scope: {}. Which one to use and why?
Directives in Angular 2 are much simpler and straightforward. Let’s create a simple
tooltip
(it will just log into the console instead of showing a popup).
First, what do we need to actually use a directive? a selector. In Angular 1 we had the
restrict
property, in Angular 2 is way more flexible. We can use:- foo: that will restrict for an element.
- [foo]: that will restrict for an attribute.
- .class: that will restrict for a class.
- input[type=text]: that will apply this directive only in
<input type="text">
There are more ways to define a selector than those 4.
Alright, we just need it to be an attribute, so:
A tooltip needs a text do display, right? Makes sense to just use the
tooltip
attribute to pass the text, something like:
In Angular 1 we could use an isolated scope or maybe grab the
tooltip
attribute in the link
function and assign it to the scope.
In Angular 2, we have a two different things. We have
inputs
and outputs
. In both cases, they are annotations as well. The input annotation is used for what we give to the directive, for example:
Here we say that we can use an attribute called
foo
and what we pass to it will be assigned to thatfoo
variable. If the attribute name and the variable names doesn’t match, we can pass a parameter to the input annotation. Let’s see how can we use it in our tooltip:
Here, we are saying that we want the
tooltip
attribute to be mapped to this.text
.
Lastly, it needs to trigger on
mouseover
. Alright, so a .on
call on the element like we used to do? No. We just need to set a listener for the directive:
I think it is pretty clear, isn’t it? On mouse over, we call the
show
function. And that is it. That is our first directive. We will explain why the parens around mouseover
in a bit.
They require you to define some properties, but they are much easier to understand than the counterpart in Angular 1:
Alright, you know this syntax pretty well, but explain to a novice what
link
is, why we have a scope
property, what is that weird @
…
Back to Angular 2, we can use this directive in our preview example like:
Try it. Does it work? No.
There is one of the biggest features in Angular 2 for me. No directive will run in our template if we don’t specify it explicitly. How?
The
Component
(and View
) annotation has an array called directives
where we list all the directives we want to use in our template. Here we imported the class Tooltip
and we listed it on directives
.
Wait a second… does that mean that if I use 10 directives on my template, I need to list all of them? Yes. How is that cool? We won’t have more collisions. Let me put an example: In Angular 1 we have two implementations of
Twitter Bootstrap
, ui-bootstrap
and AngularStrap
. They both have their issues. Imagine you use ui-bootstrap
on a daily basis but then find that the tooltip
is not enough for our purposes and then we discover that AngularStrap
has a better tooltip
. You pull that library in and you use its tooltip.
Wait a second… They both have a directive called
tooltip
. Which one is going to be used in our template? Both and there is no way in hell you can avoid that.
Now in Angular 2, imagining that we have a port of both libraries, we could do something like:
Isn’t it much better? No collision at all.
Both
ui-bootstrap
and AngularStrap
fixed this issue long ago by prefixing their directives, but in Angular 2 even when that is still recommended to prefix your directives, the end users won’t have to deal with your bad decisions.
That being said, all core directives are accessible by default.
$apply() what’s that?
Dollar apply, well, I would like to apply for some dollars. Jokes aside. How many times did you forgot to use
$apply()
? Checking our code for hours to then realize that we forgot to use $apply()
and our bindings weren’t updating.
Not anymore. Imagine this component:
Here we are using the built-in
setTimeout
to change our message. Does it Work?
Of course it does. No more fear when mixing Angular with “non angular” stuff.
We get this feature thanks to Zone.js (Thanks to my good friend Wesley Cho for pointing that out).
Properties
This is complex subject and I hope I can explain it properly.
When we write our html, for example:
The browser will parse the input element and create a node object. Then it will start parsing each
attribute
to create a property
inside that object. So we will end with a node object for the input with the type and value as properties (among others).
If we write on that input again, the
value property
will be updated but the original value attribute
will not. That attribute was used just to initialize the node object.
If you used the
<img>
tag in the past, you found this issue:
The browser parse it, and will try to fetch the image called
{{foo}}
and then when the angular runs, it will interpolate that {{foo}}
so the browser will be able to fetch the image this time. To fix that, the angular team created ng-src
. The browser doesn’t know what it is, so it is angular the one who will create the src
property with the correct value.
We have this same issue with other directives like
ng-class
, ng-show
, ng-hide
, ng-bind
, etc. Again, we have a bunch of directives that will prevent the browser from creating “broken” properties by creating the properties themselves with the correct values.
In Angular 2, we can write to those properties directly, that way, we won’t need to create all those directives. How can we do that? For example:
Using the
[]
we can write to those properties directly. We want define the src
based on this.myImage
? we write that value directly to the property (which is exactly what ngSrc
does). We need to hide a div conditionally? There is a hidden
property in HTML 5 we can use for that. All of those, without any extra directive.
An attribute only accept strings, but a property accepts complex models. This mean that in the past:
We used to do this to pass the content of
something
on the scope to the foo attribute. Now that we can write directly to properties, we can do:
No more interpolation because we are now writing to the property directly. Remember our
tooltip
directive?:
It has a
tooltip
input. That means that we can pass a dynamic text to it like:
That won’t pass
foo
as the text, it will pass the content of this.foo
as the text. And the best part, we didn’t need to modify our directive.
Now, we won’t be confused anymore of when to use interpolation or not.
Events
With events we have another problem. Check this code:
What is that doing? Is it calling
user.name()
from the scope
to assign a value to the select
property based on a name
parameter? Or is it a callback function that will be executed from inside our directive? We don’t know and there is no way we can know that without checking myDirective
source.
Angular 2 introduces a new syntax for this events, also called statements:
Thanks to that, we know that it is an event and not a property. If it were a property, we would have:
Also, thanks to this, we can get rid of unneeded directives like
ng-click
, ng-blur
, ng-change
etc. We can have something like:
That will use the
click
event of the DOM, no more wrappers around that. As an extra, if that doSomething
doesn’t exist, angular will throw an error.
For this one you will need to check the console to see that the second
<p>
will trigger an error.
Also, you will now understand this code from the tooltip:
It is an event after all ;)
References
Imagine we want to focus an input by clicking somewhere, something like:
How can we reference this input? In Angular 1 we could create a directive for the input that would $watch for changes in a variable to focus the input. Sounds complicated. In Angular 2 we can create a reference to a particular node which will be local to the template. For example:
With
#user
, we are simply creating a reference to the input, so now we can do stuff like {{user.value}}
to see its value or even {{user.type}}
to see the type of the input. In short, we now have a reference to the node object in the template. Thanks to that we can simply do:
Now upon click, we call the
focus()
method on the node so it will grab the focus.
Isn’t this wonderful? We are avoiding the need of creating extra directives for something as simple as this.
Wait a second, that example is nice, that for sure, but what is that
[(ngModel)]
syntax? Let see it step by step:
Here we are using the new
ng-model
. If we remember from an early point, with [foo]
we set some property in our directive, and with (foo) we can fire some event (for example, send a value to the parent). With this example, we are setting name
to be the value of the ng-model
using [ngModel]="name"
. Then we are creating an event to update the name in the parent with (ngModelChange)="name=$event"
. That is good but verbose. Angular 2 let us mix them both like [(ngModel)]="name"
so we are actually doing two-way databinding!.Services
Let’s create a service to fetch users at Github. What should we use? A Provider? Service? Factory? Value? Constant? I am just kidding. No more service vs factory or value vs constant (to this day, I don’t understand why it is called a constant if it is not constant).
A service in Angular 2 is simply a…
Class! You guessed it correctly.
Angular 2 has a service for http, but for the sake of showing how to use a third party library, we will use the new library that comes with ES6 called
fetch
.
We simply use
fetch
to grab those users and send the json
response back. That is really simple.
How can we use it? If the service is in a different file, for example one called ‘github’, we have to import it:
Then, we tell our component that we want to inject that:
Then in our constructor we simply inject it:
The syntax used in here, is just a syntactic sugar for the injector and it uses TypeScript typing to achieve that. It is something like: “Hey, we are injecting the class
GithubNames
, create a instance for me and call it github
”.
Alright, now we have
GithubNames
injected and saved in our class.
We just need to use it. For example, let’s create a function that will populate the
users
:
That will call our service and assign the
users
to this.users
. Again, no more $apply
even whenfetch
is not part of angular.
Let’s iterate through those users:
Uh, that is our new
ng-repeat
and that #user
sounds like the reference we created before. Thanks to that, we can reference user
inside the <li>
.
The
*
for ng-for
is another syntactic sugar. No need to dig in that for this article.Overpowered outlets
In Angular 1 we have
ngView
and uiView
to mount our views and they are cool, but now with Angular 2, we can get the new router router-outlet
and extend it, yes, literally:
No more weird $decorators.
Ok, but what’s the point, what can we do in here? We can create a new outlet that will prevent unregistered users to access certain parts of our applications:
So in this case, we created a new directive that will extend the
RouterOutlet
one. The interesting part in here is the canActivate
method, in there we grab the current url and if it is not the /login
one and we are not authenticated, we won’t allow the user to go there and we mount the Login
component instead.
Thanks to that, we have a much prettier way to manage authenticated users other than listening to route changes events.
I grabbed this example from Auth0
Note: I am not entirely sure if this example works with the latest angular 2 version.
Conclusions
Angular 2 is the right step forward. It is way simpler than Angular 1. No more controllers, no more scope inheritance that drives us insane, the directives API is much easier to understand than the actual
DDO
. No more $apply, and the best of all things, thanks to [properties] and (events) we removed like 30 directives that are not needed anymore and apart from that, we simplified the way of consuming directives.
On the other hand, there is no more 5 “different” types of services, so we just need to create a plain ES6/TS class and write methods on it.
If you had issues understanding the properties and events, please watch Misko video at ng-conf: https://www.youtube.com/watch?v=-dMBcqwvYA0
So, do you agree with me? What do you think? Leave a comment.
No comments:
Post a Comment