The two topics I have been writing about in the last year are "Angular 2" and "How to Become a Productive Programmer". Many people care about one but not the other. And that is why I've decided to have two separate resources dedicated to each topic.
In-depth Articles About Angular 2
Reflections on Productivity and Self-Improvement for Programmers
Angular Router: Empty Paths, Componentless Routes, and Redirects
At the core of the Angular router lies a powerful URL matching engine, which transforms URLs and converts them into router states.
In this article I will show how to use three of the engine's features: empty-path routes, componentless routes, and redirects. And how using them together, we can implement advanced patterns in just a few lines of code.
When navigating to '/team/11/user/bob', the router will instantiate the team component with the user component in it. And when navigating to '/team/11/users', the router will show the list of all users. This configuration works. But a more common way of doing this would be to render the list when navigating to /team/11. That's what empty-path routes are for.
In this example, when navigating to '/team/11/user/jim', the router will instantiate the wrapper component with UserComponent in it.
Matching Strategies and Redirects
To understand the second feature, let's step back to think about how the router does matching. The router takes an array of routes and a URL, and tries to create a RouterState.
Let’s imagine we have this configuration, and we are navigating to '/team/11/user/jim'.
The router will go through the array of routes, which in this case there is only one, and it will check if the URL starts with the route's path. Here it will check that '/team/11/user/jim' starts with 'team/:id'. Because it matches, the router will carry on matching by taking what is left in the URL and the matched route's children. If the taken path through the configuration does not "consume" the whole url, the router backtracks to try an alternative path.
The default matching strategy is called 'prefix' because a match is successful when a route's path is the prefix of what is left in the URL. We can set the strategy explicitly, as follows:
The router supports a second matching strategy--full, which checks that the path is "equal" to what is left in the URL. This is mostly important for redirects. To see why, let's look at the this example:
Most likely your intent is to render the Teams component when navigating to '/teams', and redirect to '/teams' when navigating to '/'. But since the default matching strategy is 'prefix', and an empty string is a prefix of any URL, the router will apply the redirect even when we are navigating to '/teams'. Now, if we change the matching strategy to 'full', the router will apply the redirect only when navigating to '/'.
If the 'redirectTo' value starts with a '/', then it is an absolute redirect. The next example shows the difference between relative and absolute redirects.
When navigating to '/team/11/legacy/user/jim', the router will apply the second redirect and will change the URL to '/team/11/user/jim'. In other words, the part of the URL corresponding to the matched path will be replaced. But navigating to '/team/11/org/eng' will replace the whole URL with '/org/eng'.
Componentless Routes
It is often useful to share parameters between sibling components. In this example we have two components--TeamListComponent and TeamDetailsComponent--that we want to put next to each other, and both of them require the team id parameter. TeamListComponent uses the id to highlight the selected team, and TeamDetailsComponent uses it to show the information about the team.
One way to model that would be to create a bogus parent component, which both TeamListComponent and TeamDetailsComponent can get the id parameter from, i.e. we can model this solution with the following configuration:
This solution has two problems. First, we need to create the team parent component, which serves no real purpose. Second, TeamListComponent and TeamDetailsComponent have to access the id parameter through the parent, which makes them less reusable. Because this use case is so common, the router supports a feature called componentless routes.
Componentless routes "consume" URL segments without instantiating components.
Let's change the 'team/:id' route to make it Componentless.
Why is this better? First of all, we've gotten rid of the bogus component. Second, since there is no component associated with the '/team/:id' route, the router will merge its params, data, and resolve into the children. As a result, TeamListComponent and TeamDetailsComponent can access the id parameter directly, without going through the parent route.
Composing Empty-Path Routes, Componentless Routes, and Redirects
What is really exciting about all these features is that they compose very nicely. And we can use them together to implement advanced patterns in just a few lines of code.
Let me give you an example. We've learned that we can use empty-path routes to instantiate components without consuming any URL segments, and we can use Componentless routes to consume URL segments without instantiating components. What about combining them?
Here we've defined a route that neither consumes any URL segments nor creates any components, but used merely for running guards and fetching data that will be used by boths TeamsComponent and OrgsComponent.
Next example, although contrived, shows how we can use the three features to implement interesting URL transformations.
With this configuration in place, the navigation to 'team/11' will result in 'team/11/(list/default//aux:details).
Learn More
"Angular Router. The Complete Authoritative Reference" is the best resource on the Angular 2 Router.
Follow @victorsavkin !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs'); to read more about Angular router.
We all build user interfaces using some form of reactive programming. A new todo was added? We need to render it on the screen. Someone changed the todo's title? We need to update the text element in the DOM. And there are dozens of libraries out there that help us do that. They are similar in some ways and different in others.
In this article I will introduce four independent dimensions of reactive programming: events and state, deriving and executing, reified and transparent, self observation and external observation. And I will explain how using these dimensions, as well as the vocabulary that comes with them, we can make our discussions about different libraries and approaches more objective and concise.
The examples in the article are written using both Angular 2 and RxJS. By including examples of each, it will become clear that even when you compare a batteries-included framework with a reusable library, we can still talk about them using the same terminology. The conversation can still be meaningful.
Managing state transitions is one of the hardest parts of building applications. This is especially true on the web, where you also need to ensure that the state is reflected in the URL. In addition, we often want to split applications into multiple bundles and load them on demand. Doing this transparently isn't trivial.
The Angular router is designed to solve these problems. Using the router, you can declaratively specify application state, manage state transitions while taking care of the URL, and load components on demand. In this article I will discuss the API of the router, as well as the mental model and the design principles behind it.
Let's get started.
Outline
What Do Routers Do?
URL Parsing and Serialization
Accessing URL Tree
Route Recognition
Powerful Matching Syntax
Component Instantiation
Using Params
QueryParams and Fragment
Using Snapshots
Navigation
Imperative Navigation
RouterLink
More on Syntax
Navigation is URL Based, not Route Based
E2E Example
Summary
What Do Routers Do?
Before we jump into the specifics of the Angular Router, let's talk about what routers do in general.
As you might know, an Angular 2 application is a tree of components. Some of these components are reusable UI components (e.g., list, table), and some are application components. The router cares about application components, or, to be more specific, about their arrangements. Let's call such component arrangements router states. A router state defines what is visible on the screen.
The router configuration defines all the potential router states an application can be in. Let's look at an example.
An outlet is the location where a component will be placed. If a node has multiple children of the same color, i.e., of the same outlet type, only one of them can be active at a time. Consequently, the team and summary components cannot be displayed together.
A router state is a subtree of the configuration tree. For instance, the example below has the summary component activated.
The router's primary job is to manage navigations between states, which includes updating the component tree. A navigation is essentially the act of transitioning from one activated subtree to another. Say we perform a navigation, and this is the result:
Because the summary is no longer active, the router will remove it. Instead, it will instantiate the details component and display it inside the team component, with the chat component visible on the side.
That's it. The router simply allows us to express all the potential states in which our application can be, and provides a mechanism for navigating from one state to another.
So now we've learned what routers do in general. It's time to talk about the Angular router.
The Angular router takes a URL, parses it into a URL tree, recognizes router states, instantiates all the needed components, and, finally, manages navigation. Let's look at each one of these operations in detail.
URL Parsing and Serialization
The URL bar provides a huge advantage for web applications over native applications. It allows you to reference states, bookmark them, and share them with your friends. In a well-behaved web application, any application state transition results in a URL change, and any URL change results in a state transition. In other words, a URL is a serialized router state.
The first thing the router does is parse the URL string into a URL tree. The router does not need to know anything about your application or its components to do that. In other words, the parse operation is application-independent. To get a feel of how this works, let's look at a few examples.
Let's start with a simple URL.
/team/3/details // is parsed into the following URL tree: new UrlSegment(paths: [], children: // this is root segment { primary: new UrlSegment(paths: [ new UrlPathWithParams(path: 'team', parameters: {}), new UrlPathWithParams(path: '3', parameters: {}), new UrlPathWithParams(path: 'details', parameters: {}) ], children: {}) }
)
As you can see, a URL tree consists of URL segments. And each URL segment contains its paths, and children.
Now look at this example, where the first path fragment has the extra parameter set to true.
/team;extra=true/3 // is parsed into the following URL tree: new UrlSegment(paths: [], children: { primary: new UrlSegment(paths: [ new UrlPathWithParams(path: 'team', parameters: {extra: 'true'}) new UrlPathWithParams(path: '3', parameters: {}) ], children: {}) }
)
Finally, let's see the result when the team segment has two children instead of one.
/team/3(aux:/chat;open=true) // is parsed into the following URL tree: new UrlSegment(paths: [], children: { primary: new UrlSegment(paths: [ new UrlPathWithParams(path: 'team', parameters: {}) new UrlPathWithParams(path: '3', parameters: {}) ], children: {}), aux: new UrlSegment(paths: [ new UrlPathWithParams(chat: 'team', parameters: {open: 'true'}) ], children: {}) }
)
As you can see, the router uses parenthesis to serialize segments with multiple children, the colon syntax to specify the outlet, and the ';parameter=value' syntax (e.g., open=true) to specify route specific parameters.
There are a couple of reasons why the router use the URL tree instead of just using the URL string. First, the URL tree is a richer data structure, with more affordances to facilitate common operations. Second, it enables us to provide our own custom URL serialization strategy.
You can get the current URL from the router service.
class TeamCmp { constructor(router: Router) { const url: string = router.url; // we can parse it into UrlTree const tree: UrlTree = router.parseUrl(url); } }
Storing URLs
URL trees and URL segments are immutable, so it is safe to store them. This is particularly handy for implementing interesting navigation and undo patterns.
Router State Recognition
Since the parsing of the URL does not require any knowledge about the application, the produced URL tree does not represent the logical structure of the application. A router state does. To create it, the router uses the provided configuration and matches it against a URL tree. Again, let's look at a few examples.
/team/3/details new UrlSegment(paths: [], children: { primary: new UrlSegment(paths: [ new UrlPathWithParams(path: 'team', parameters: {}), new UrlPathWithParams(path: '3', parameters: {}), new UrlPathWithParams(path: 'details', parameters: {}) ], children: {}) }
)
First, the router will parse the URL string into a URL tree. Then, it will go through the items in the configuration, one by one, trying to match them against the parsed URL tree. In this particular case, the first entry is a match. Then, it will take the children items and will carry on matching.
If it is impossible to match the whole URL, the navigation will fail. But if it works, a router state representing the future state of the application will be constructed.
For this particular example it will look as follows:
/team/3/details // will create the following activated routes: new ActivatedRoute(component: TeamCmp, url: [ new UrlPathWithParams(path: 'team', parameters: {}), new UrlPathWithParams(path: '3', parameters: {}) ]) new ActivatedRoute(component: DetailsCmp, url: [ new UrlPathWithParams(path: 'details', parameters: {}) ]) RouterState: ActivatedRoute(component: RootCmp) -> ActivatedRoute(component: TeamCmp) -> ActivatedRoute(component: DetailsCmp)
A router state consists of activated routes. And each activated route is associated with a single component. Also, note that we always have an activated route associated with the root component of the application.
Let's look at one more example.
/team/3(aux:/chat;open=true) // will create the following activated routes: new ActivatedRoute(component: TeamCmp, outlet: primary, url: [ new UrlPathWithParams(path: 'team', parameters: {}), new UrlPathWithParams(path: '3', parameters: {}) ]) new ActivatedRoute(component: ChatCmp, outlet: 'aux', url: [ new UrlPathWithParams(path: 'chat', parameters: {open: 'true'}) ]) RouterState ActivatedRoute(component: RootCmp, outlet: primary) -> ActivatedRoute(component: TeamCmp, outlet: primary) -> ActivatedRoute(component: ChatCmp, outlet: aux)
Here we have two siblings with different outlet names, TeamCmp and ChatCmp, which are activated at the same time.
Powerful Matching Syntax
The syntax for matching is very powerful. It supports wildcards (e.g., '**'), and positional parameters (e.g., /team/:id).
Component Instantiation
At this point, we have a router state. The router can now match this state by instantiating each component, assembling a component tree, and placing each component into the appropriate router outlet.
To understand how it works, let's look at the following example.
First, the router will instantiate TeamCmp and place it into the primary outlet of the root component. Then, it will place a new instance of ChatCmp into the 'aux' outlet. Finally, it will instantiate a new instance of DetailsCmp and place it in the primary outlet of the team component.
Using Params
Often components rely on the state captured in the URL. For instance, the team component probably need to access the id parameter. We can get the parameters by injecting the ActivatedRoute object.
@Component({ selector: 'team', template: ` Team Id: {{id | async}} primary: { <router-outlet></router-outlet> } ` }) class TeamCmp { id: Observable<string>; constructor(r: ActivatedRoute) { //r.params is an observable this.id = r.params.map(r => r.id); } }
If we navigate from /team/3/details to /team/4/details, the params observable will emit a new map of parameters. So the team component will display Team Id: 4.
Now, say the details component also needs to react to the team id changes. This is how we can do it:
As you can see the router exposes route and query parameters as observables, which is convenient most of the time. But not always. Sometimes what we want is a snapshot of the state that we can examine at once.
So at this point the router has parsed the URL, recognized the router state, and instantiated the components. Next, we need to be able to navigate from this router state to another one. There are two ways to accomplish this: by calling router.navigate or by using the RouterLink directive.
Imperative Navigation
To navigate imperatively, inject the Router service and call navigate.
This code works, but it has a problem--the navigation is absolute. This makes this component harder to reuse and test. To fix it, we just need to pass an activated route to the navigate method, as follows:
This directive will also update the href attribute when applied to an <a> link element, so it is SEO friendly and the right-click, open-in-new-browser-tab behavior we expect from regular links will work.
More on Syntax
Just to get a feel of what can be passed in to navigate and routerLink, let's look at the following examples.
// absolute navigation this.router.navigate(['/team', this.teamId, 'details']); // you can collapse static parts into a single element this.router.navigate(['/team/3/details']); // also set query params and fragment this.router.navigate(['/team/3/details'], {queryParams: newParams, fragment: 'fragment'}); // e.g., /team/3;extra=true/details this.router.navigate(['/team/3', {extra: true}, 'details']); // relative navigation to /team/3/details this.router.navigate(['./details'], {relativeTo: this.route}); // relative navigation to /team/3 this.router.navigate(['../', this.teamId], {relativeTo: this.route});
Navigation is URL Based, not Route Based
Navigation is URL based, and not route based. Meaning that '../' skips one segment in the URL tree. To see why this is the case, let's look at the following link:
</a><a this.chatid>Chat</a>
Let's say the link navigates into the ChatCmp and DetailsCmp components, and we would like to load these components lazily, only when the user clicks on the link. At the same time, the href attribute of the link should be set on page load.
The route configuration of ChatCmp and DetailsCmp is not available on page load. This means that we cannot know if '../chat', this.chatId, 'details' are one, two, or three route segments. The only thing we know is that these are three separate URL segments.
This was one of the design constraints when building the router. It should allow deep linking into lazily loaded routes without knowing anything about them. And the links should have the href attribute set. That is why all the navigation in the router is URL based, not route based.
E2E Example
We've looked at all the four core operations of the Angular router. Now, let's look at all of them in action.
Say this is the router configuration of our application.
When the browser is loading /team/3/details, the router will do the following:
First, it will parse this url string into a URL tree.
new UrlSegment(paths: [], children: // this is root segment { primary: new UrlSegment(paths: [ new UrlPathWithParams(path: 'team', parameters: {}), new UrlPathWithParams(path: '3', parameters: {}), new UrlPathWithParams(path: 'details', parameters: {}) ], children: {}) }
)
Second, it will use this URL tree to construct a new router state.
Third, the router will instantiate the team and details components.
Now, let's say the team component has the following link in its template:
<a>Team 4</a>
And say the user triggers a navigation by clicking on the link.
The router will take this array ['../', 4, 'details'] and will construct a new URL tree.
new UrlSegment(paths: [], children: // this is root segment { primary: new UrlSegment(paths: [ new UrlPathWithParams(path: 'team', parameters: {}), new UrlPathWithParams(path: '4', parameters: {}), new UrlPathWithParams(path: 'details', parameters: {}) ], children: {}) }
Finally, it will find that the team and details components are already in place. So it will reuse them and just push a new set of parameters into the params observable of the team component.
Once this is done, the router will take the new URL tree, serialize it into a string, and update the location property.
Summary
We learned quite a few things today. First, we learned what routers are for. They allow us to express all the potential states in which our application can be, and give us a mechanism for navigating from one state to another. We also learned about the four core operations of the Angular router: URL parsing, state recognition, component instantiation, and navigation. Finally, we looked at an e2e example showing the router in action.
We haven't touched on the following important topics: navigation guards, monitoring, debugging, error handling, and lazy loading. All of these topics will be covered in future blog posts.
You can play with a real application built with the Angular router here. Also, you can enable tracing, as follows: bootstrap(RootCmp, provideRouter(config, {enableTracing: true})). This will tell the router to log all the events to the console, which is useful for troubleshooting.
Learn More
"Angular Router. The Complete Authoritative Reference" is the best resource on the Angular 2 Router.
Follow me on twitter to get the latest news and announcements about the Angular router.
As programmers we want to do work that pushes our cognitive abilities to their limits and puts us in the state of flow. We enjoy the feeling of joy we get after solving a hard problem. But many of us rarely experience that as we are often too distracted and consumed by mundane tasks.
In this article I will talk about how dividing my work into two categories, deep and shallow, and changing my daily routine to respect that, increased the amount of fulfilling work I do and made me more productive.
Managing application state is a hard problem. You need to coordinate between multiple backends, web workers, and UI components. Patterns like Redux and Flux are designed to address this problem by making this coordination more explicit. In this article, I will show how we can implement a similar pattern in just a few lines of code using RxJS. Then I will show how we can use this pattern to implement a simple Angular 2 application.
Using mutable objects for modeling application state makes tracking changes hard and incurs a sizable performance cost. Switching to immutable objects solves these problems, but brings new ones. This is because immutability and encapsulation are often at odds. Can we combine the benefits of immutability and local state? In this article I will explore how it can be done in Angular 2.
Let's say we want the component to look up and display the zip code of the address. And to make it more interesting, this lookup is expensive, so we want to do it only when the address changes.
In Angular 2, components can subscribe to the onChanges lifecycle hook, which will be called when any of the inputs change. We can put the logic of calculating the zip code into this hook.
@Component({ selector: 'person', template: `{{person.name}} lives at {{address.street}}, {{address.city}} (zipCode)` }) class DisplayPerson { @Input() person: {name:string}; @Input() address: {city:string, street:string}; zipCode: string; // this is not an input, this is local state of this component constructor(private zipCodes: ZipCodes) {} onChanges(inputChanges) { if (inputChanges.address) { // this means that the address object was replaced this.zipCode = this.zipCodes(this.address); } } }
If the address object is mutable, some other component can update the street property without creating a new address object. If this happens, the onChanges hook won't be called, and a wrong zip code will be displayed. There are, of course, ways to work around it. But the problem remains -- we have to be deeply aware of how and when the address object can change.
What does this component depend upon? Well, it depends on the person and address objects. But if these objects are mutable, this does not tell as much. Since they can be updated by any component or service at any time, the DisplayPerson component can be affected by any other component or service. That is why, by default, Angular does not make any assumptions about what a component depends upon. So it has be conservative and check DisplayPerson's template on every browser event. Since the framework has to do it for every component, it can become a performance problem.
Two Problems with Mutable Objects
To sum up, these are the problems with mutable domain objects:
They make tracking changes hard, both for the developer and the framework.
They force Angular to be conservative by default, which negatively affects performance.
Using Immutable Objects
Modeling application state using immutable objects solves these problems. If both person and address are immutable, we can tell a lot more about when the DisplayPerson component can change. The component can change if and only if any of its inputs changes. And we can communicate it to Angular by setting the change detection strategy to OnPush.
@Component({ selector: 'person', template: `{{person.name}} lives at {{address.street}}, {{address.city}} (zipCode)`, changeDetection: ChangeDetectionStrategy.OnPush // ⇐=== }) class DisplayPerson { @Input() person: {name:string}; @Input() address: {city:string, street:string}; zipCode: string; constructor(private zipCodes:ZipCodes) {} onChanges(inputChanges) { if (inputChanges.address) { // this means that the address was replaced this.zipCode = this.zipCodes(this.address); } } }
Using this change-detection strategy restricts when Angular has to check for updates from "any time something might change" to "only when this component's inputs have changed". As a result, the framework can be a lot more efficient about detecting changes in DisplayPerson. If no inputs change, no need to check the component's template. Perfect! Immutable objects are awesome!
How to Use Immutable Objects in JS
Primitives types like strings and numbers are immutable. You can freeze JavaScript objects and array to make them immutable, but a better option would be to use libraries like Immutable.js or Mori. They provide efficient implementation of immutable collections and records and are easy to use.
Immutability vs Encapsulation
As usual, there are trade-offs.
Modeling application state using exclusively immutable objects requires pushing the state management out of the component tree. Think about it. Since address is immutable, we cannot update its street property in place. Instead, we have to create a new address object. Say this address is stored in some PersonRecord object. Since that object is immutable too, we will have to create a new PersonRecord object. Therefore, the whole application state will have to be updated if we change the street property of the address.
There are many ways to organize the application state, but for simplicity let's assume it is stored in a single data structure. Although it may look scary at first, this setup actually provides a lot of benefits: we can serialize the state of the whole application, inspect it, etc.
So we decided to remove all local mutable state from our components. But since the state is there for a reason, we cannot just remove it -- we need to move it somewhere. The only place to move it to is the application state object. This means that some state that previously would have been encapsulated, becomes public. Let's look at an example illustrating this.
Say we have a typeahead component that we can use like this:
<typeahead>
And this is a sketch of the component's class:
@Component({selector: 'typeahead', templateUrl: 'typeahead.html'}) class Typeahead { @Input() options: string[]; @Input() initOption: string; @Output() change = new EventEmitter(); }
This is just the public interface of this component. In practice, the typeahead component will have a lot more going on. For example, it might store some scrolling position, which can only be updated by events triggered in typeahead.html:
The scrolling position is internal to typeahead. No client of the component has to know the property is even there.
Completely disallowing local mutable state will mean that the scrolling position will have to be stored in the application state object. Suddenly, the client now has to know that the scrolling position is there. We need to pass it in and out. This isn't ideal from the dev ergonomics standpoint. But what is even more important, it makes the component less reusable.
In summary, removing mutable state simplifies tracking changes in the application and makes the application more performant. But at the same time it breaks the encapsulation of components, and their implementation details start to leak out.
Getting the Best of Both Worlds
Instead of abolishing mutable state all together, let's just scope it to a component.
Application state that is passed around is modeled using immutable objects.
Components can have local state that can only be updated when their inputs change or an event is emitted in their templates.
So we allow mutable state, but in a very scoped form. Let's look at how this setup compare to the previous one.
Tracking Changes
Since inputs are immutable, an input has to be replaced to affect a component. And we can track changes to local state properties just by looking at the component's onChanges hook and event handlers. Nothing outside the component can change them in any way. This is a good result.
Performance
This setup works for us, but it also works for Angular. The framework will check OnPush components only when their inputs change or components' templates emit events.
Encapsulation
And we did not break the component's encapsulation. In the typeahead example, the scrolling position is private to the component. So the client using it does not need to know that the property event exists.
But we lost something too. The application state is no longer stored in our place. As a result, the precise control over all mutations is no longer there. An this, for instance, can have a negative impact on tooling.
Be Pragmatic
We often contrast the "everything-is-mutable" with "everything-is-a-pure-function" styles of programmings. In this article I showed that both of these have pros and cons, and that there are pragmatic options in between the two extremes that you can consider.
Summary
Using mutable objects for modeling application state has a negative impact on performance.
It also makes tracking changes really hard.
Switching to immutable objects for modeling application state solves these problems.
Keeping scoped mutable state in components preserves most of the benefits of using immutable objects without breaking encapsulation.
To enable that use ChangeDetectionStrategy.OnPush.
Combining Static and Runtime Type Checking for Better Dev Productivity
Dynamically-typed languages enable interesting development workflows. The ease with which you can experiment and innovate in, for instance JavaScript, is extraordinary. But there is a catch. When your application and team grow, the inability to define contracts in the code makes the application fragile and refactorings expensive. Every time you undertake one of those, you see errors like "undefined is not a function". This is a sign of a wrong object being passed in as an argument somewhere in the codebase. It is also a sign that you will have a painful debugging session. So dynamically-typed languages provide better dev experience in the small, but have problems in the large.
This is what systems like TypeScript aim to solve. In TypeScript, you can use types to annotate static parts of the code. This enables advanced editing, refactoring, and early error detection, which improves the dev experience in the large, without losing what is great about JavaScript -- you can still experiment as easily as you could before.
TypeScript's optional type system can get you very far. Painful debugging sessions are rare in a reasonably-typed TypeScript codebase. And this is fantastic!
There are certain things, however, that just cannot be checked statically.
Say you make an http call to the backend. TypeScript cannot statically verify that the fetched data is of the right type. So you just have to tell the type checker to trust you.
interface Movie { title: string; releaseDate: string; } @Component({...}) class MoviesCmp { movies: Movie[]; constructor(http: Http) { http.get("/movies.json").map(resp => resp.json()).subscribe(json:any => this.movies = json // type checker cannot verify this ); } }
Interacting With Untyped Libraries
Another example where the type checker is helpless is when your application has to interact with untyped third-party libraries. Once again, the only thing TypeScript can do is to trust you.
interface Movie { title: string; releaseDate: string; } function bestMatch(movies, query) { // some funky code we do not control } @Component({...}) class MoviesCmp { movies: Movie[]; findBestMatch(query: string): Movie { return bestMatch(this.movies, query); // type checker cannot know if we are passing right arguments to bestMatch // nor does it know if the function returns a Movie } }
Reflective Code
Finally, if you have some funky meta-programming code, where you reflectively transform or generate objects, most likely the type checker won't be able to figure out the types.
function convertStringsToDates(obj) { const res = {}; for (let k in obj) { if (looksLikeDate(obj[k])) { res[k] = parseDate(obj[k]); } else { res[k]= obj[k]; } } return res; } final normalizedMovie = convertStringsToDates({title: "Star Wars: Episode VII", releaseDate: "Dec 18, 2015"}); // type checker does not know the type of normalizedMovie
Unhelpful Exceptions
In a reasonably-typed application, these limitations are why you can still see "undefined is not a function". Or even worse, you may not get any error. Instead, the application suddenly starts behaving weirdly due to a value somewhere being undefined. This is not good dev experience. So let's see how we can fix it.
Separate Your Application and External Code
First, you need to separate our application code from the code talking to the backend or untyped-libraries, like this:
There are good reasons to do this. First, the backend code can be written by another team, and, as a result, can be changed without you even knowing it. But even if you know about the coming change, the repository object becomes the only place you need to patch. So you change different parts of the code base independently.
Once you separate your application code from the interactions with external systems, this is what the architecture of your application will look like:
Note, by doing this you also separated typed and untyped code. This means that an incorrect object can only get through one of these repositories. To prevent this you need to verify that the objects returned by these repositories and facades are what you expect.
Adding Runtime Checks
To show how you can do it, I built a small library for adding runtime type checks. You can find it on github and on npm. And this is how it can be used:
The CheckReturn decorator wraps the parseMovies method and verifies that the parsed value is an array of objects with title and releaseDate. If any of these fields is missing, it will throw.
The Runtime Type Checks library provides a few decorators for checking parameters and return values. It can do instanceof type checks out of the box, but can be customized to do structural checks, as shown above. I won't go into details in this blog post. But if you are interested, read more information here.
Back to our example. By decorating MoviesRepo with runtime checks, you change the architecture of the application to look like this:
You protected all external boundaries of your application, and since your application is reasonably-typed, you should not receive "undefined is not a function" or other cryptic messages.
Instead, when the backend API changes, the application will throw RuntimeTypeCheck: Expected Array of {title:string, releaseDate:string}, got [{title: "Star Wars: Episode VII", usReleaseDate: "Dec 18, 2015"}]. The releaseDate field is missing., with the stack trace pointing to the exact place where the mismatch was detected. This exception tells you the cause of the error and the place where it can be fixed.
Additional Questions
Does it affect performance?
Runtime-checks are primarily a development time feature. They give the developer more confidence that their code works. And when the code breaks, the developer gets helpful error messages. Having said that, the checks are performed only at the application or library boundaries, which hopefully are not crossed very often. So you can keep the checks running in production for better error reporting.
Does it affect testing?
As with everything, there are trade-offs. Runtime checks are great for acceptance tests or some dev mode in which you run your application. But unit tests are a different story. Runtime type checks make isolated testing and mocking a lot trickier. So if you rely on those in your unit tests, you probably should disable the checks in your unit tests.
Can we simplify structural checks?
Nominal checks (instanceof checks) are automatically handled for us by the library, but structural checks are not. So you have to specify a function that will do the checks (e.g., arrayOf(objectLike(...))). To simplify such check you can either use some library for defining structural types or use compile-type tools generating checks based on interfaces declarations.
Isn't this arrangement similar to gradual typing?
It is similar, but not the same. Gradual type systems ensure that everything inside the boundary is sound and cannot throw "undefined is not a function". The soundness allows the compiler to trust type annotations and do optimizations based on them. This is not the case in Flow or TypeScript.
Summary
Dynamically-typed languages, like JavaScript, provide great dev experience in the small. You can experiment with new approaches and build interesting solutions in days or even hours! But there is a problem.
How do you scale it to large code bases and teams?
This is what systems like TypeScript solve. They improve dev experience in the large without losing what is great about JavaScript. But they can only go so far because certain things cannot be checked at compile-time.
Runtime type checks at the application boundary in combination with static-type checking of the application itself provides the best dev experience.
As programmers, we believe new frameworks and languages can make us a lot more productive. So we read books, watch online courses, and go to hackathons. Of course, these things matter. But I believe the way you do your work, the process itself, can have a great impact on your productivity than any framework or language.
In this article I will show how common scenarios of using dependency injection in Angular 1 can be implemented in Angular 2.
Code Samples in TypeScript
I wrote the code samples in TypeScript because I am a big fan of the language. This does not mean you have to use it when building applications in Angular 2. The framework works great with ES5 and ES6.
Let's Start with a Simple Component
Let's start by implementing a simple login component.
// Angular 2: Basic Implementation of Login Component @Component({ selector: 'login', template: ` <form #f="form" (submit)="onSubmit(f.value)"> Text <input type="text" ngControl="login"> Password <input type="password" ngControl="password"> <button>Submit</button> </form> ` }) class Login { onSubmit(formValue: { login: string, password: string }) { const service = new LoginService(); service.login(formValue); } }
Experienced developers know that coupling of the login component to the login service is problematic. It is hard to test this component in isolation. And it also makes it less reusable. If we have two applications, with two login services, we will not be able to reuse our login component.
We can remedy it by monkey patching the system loader, so we can replace the login service, but this is not the right solution. We have a fundamental problem with our design, which DI can help us fix.
Using Dependency Injection
We can solve our problem by injecting an instance of LoginService into the constructor instead of creating it directly.
As with Angular 1, we need to tell the framework how to create the service. In Angular 2, the directive and component decorators are the place where you configure dependency injection providers. In this example, the App component makes LoginService available for itself and all its descendants, including the login component. An instance of the login service will be created next to the App component. So if multiple children depend on it, all of them will get the same instance.
We separated the two concerns: the login component now depends on some abstract login service, and the app component creates a concrete implementation of the service. As a result, the login component no longer cares what implementation of the login service it will get. This means that we can test our component in isolation. And we can use it in multiple applications.
Note, that Angular 1 relies on strings to configure dependency injection. Angular 2 by default uses type annotations, but there is a way to fall back on strings when more flexibility is required.
Using Different Login Service
We can configure our application to use another implementation of the login service.
// Angular 2: Using SomeOtherLoginService @Component({ selector: 'app', template: `<login></login>`, directives: [Login], providers: [provide(LoginService, {useClass: SomeOtherLoginService})] }) class SomeOtherApp {}
Configuring Login Service
One of the great things about dependency injection is that we do not have to worry about the dependencies of our dependencies. The login component depends on the login service, but it does not need to know what the service itself depends upon.
Let's say the service requires some configuration object. In Angular 1, it can be done as follows:
Angular 2 does a much better job here. It uses the same dependency injection mechanism to inject the element into the component's constructor.
// Angular 2: Injecting Element @Component({ selector: 'needs-element', template: 'some template' }) class NeedsElement { constructor(el: ElementRef) { el.nativeElement // the DOM element } }
Injecting Other Directives
It is also quite common to have multiple directives working together. For instance, if you have an implementation of tabs and panes, the tab component will need to know about the pane components.
We use the require property to get access to the tab controller. Then, we use scope to get access to the pane controller. And, finally, we use the link function to connect the two. This is quite a bit of work for such a simple scenario.
And, once again, Angular 2 does a much better job here.
But we can do even better than that! Instead of panes registering themselves with the closest tab, the tab component can get the list of panes using the ContentChildren decorator.
Query takes care of many of the problems developers face when implementing this in Angular 1:
The panes are always in order.
The QueryList will notify the tab component about changes.
There is no need for Pane to know about Tab. The Pane component is easier to test and reuse.
Single API
Angular 1 has several APIs for injecting dependencies into directives. Who hasn't been confused by the difference between factory, service, provider, constant and value? Some objects are injected by position (e.g., element), some by name (e.g., LoginService). Some dependencies are always provided (e.g., element in link), some have to be configured using require, and some are configured using parameter names.
Angular 2 provides a single API for injecting services, directives, and elements. All of them get injected into the component's constructor. As a result, there is a lot less API to learn. And your components are much easier to test.
But how does it work? How does it know what element to inject when a component asks for one? The way it works is as follows:
The framework builds a tree of injectors that matches the DOM.
Since there is an injector for every DOM element, the framework can provide contextual or local information, such as an element, attributes, or nearby directives.
This is how the dependency resolution algorithm works.
// this is pseudocode. var inj = this; while (inj) { if (inj.has(requestedDependency)) { return inj.get(requestedDependency); } else { inj = inj.parent; } } throw new NoProviderError(requestedDependency);
So if Pane depends on Tab, Angular will start by checking if the pane element happens to have an instance of Tab. If it does not, it will check the parent's element. It will repeat the process until either it finds an instance of Tab or it reaches the root injector.
You can point to any element on the page, and by using ngProbe get its injector. You can also see an element's injector when an exception is thrown.
I know that it may seem a little bit complicated, but the truth is Angular 1 already has a similar mechanism. You can inject nearby directives using require. But this mechanism is undeveloped in Angular 1, and that is why we cannot not fully take advantage of it.
Angular 2 takes this mechanism to its logical conclusion. And turns out we do not need the other mechanisms any more.
Advanced Examples
So far, we have looked at examples that worked in both Angular 1 and Angular 2. Now, I want to show you a few advanced examples that just cannot be expressed in Angular 1.
Optional Dependencies
To mark a dependency as optional, use the Optional decorator.
class Login { constructor(@Optional() service: LoginService) {} }
Controlling Visibility
You can be more specific where you want to get dependencies from. For instance, you can ask for another directive on the same element.
class CustomInputComponent { constructor(@Self() f: FormatterDirective) {} }
Or you can ask for a directive in the same view.
class CustomInputComponent { constructor(@Host() f: CustomForm) {} }
You can read more about it here.
Providing Two Implementations of the Same Service
Since Angular 1 has only one injector object, you cannot have two implementations of LoginService in the same app. In Angular 2, where every element has an injector, this is not a problem.
The services and directives created under SubApp1 will use CustomPaymentService1, and the ones created under SubApp2 will use CustomPaymentService2, even though all of them declare a dependency on PaymentService.
View Providers
The following makes LoginService available for injection in both the content children (a.k.a. light dom children) of App and its view.
In my previous blog post I talked about the three core parts of Angular 2: components, dependency injection, and property bindings. In this blog post I will talk about Angular 2 template syntax.
Angular 2 separates updating the application model and reflecting the state of the model in the view into two distinct phases. The developer is responsible for updating the application model. Angular, by means of change detection, is responsible for reflecting the state of the model in the view. The framework does it automatically on every VM turn.
Event bindings, which can be added using the () syntax, can be used to capture a browser event execute some function on a component. So they trigger the first phase.
Property bindings, which can be added using the [] syntax, should be used only for reflecting the state of the model in the view.
Better Support for Functional Programming in Angular 2
In this blog post I will talk about the changes coming in Angular 2 that will improve its support for functional programming.
Angular 2 is still in active development, so all the examples are WIP. Please focus on the capabilities and ideas, and not the API. The API may change.
Why Functional Programming
Imagine an application being a nested structure of components. And it is written in the way that
A component depends only on its bindings and its direct children.
A component directly affects only its direct children.
For example, the highlighted Todo component should not be able to affect any other component except for the input elements.
If we write our application in this way, we get the property:
We can effectively reason about every single component without knowing where it is used. In other words, we can reason about a component just by looking at its class/function and its template.
We want to limit the number of things we need to look at to make a change. And we want to make changes predictable and controlled. This is the goal, and functional programming is just the means of achieving it.
Data-Oriented Programming
Using a mutable domain model goes against our goal. Say we have a mutable domain model that backs up my component. And the component updates the model. Some other component, somewhere else, that happened to point it, will be updated.
Instead we should model our domain using dumb data structures, such as record-like objects and arrays, and transform them using map, filter, and reduce.
Since Angular does not use KVO, and uses dirty-checking instead, it does not require us to wrap our data into model classes. So we could always use arrays and simple objects as our model.
Here, for example, todos is an array of simple objects. Note, we do not mutate any of the objects. And we can do it because the framework does not force us to build a mutable domain model.
We can already write this kind of code in Angular today. What we want is to make it smoother, and take advantage of it performance wise.
Using Persistent Data Structures
For example, we want to improve the support of persistent data structures.
First, we want to support even most exotic collections. And, of course, we do not want to hardcode this support into the framework. Instead, we are making the new change detection system pluggable. So we will be able to pass any collection to ng-repeat or other directives, and it will work. The directives will not have to have special logic to handle them.
<todo template=”ng-repeat: #t in: todos” [todo]=”t”></todo> // what is todos? array? immutable list? does not matter.
Second, we can also take advantage of the fact that these collections are immutable. For example, we can replace an expensive structural check to see if anything changed with a simple reference check, which is much faster. And it will be completely transparent to the developer.
Controlling Change Detection
Functional programming limits the number of ways things change, and it makes the changes more explicit. So because our application written in a functional way, we will have stronger guarantees, even though the JavaScript language itself does not provide those guarantees.
For example, we may know that a component will not change until we receive some event. Now, we will be able to use this knowledge and disable the dirty-checking of that component and its children altogether.
A few examples where this feature can come handy: * If a component depends on only observable objects, we can disable the dirty-checking of the component until one of the observables fires an event. * If we use the FLUX pattern, we can disable the dirty-checking of the component until the store fires an event.
Our applications are not just projections of immutable data onto the DOM. We need to handle the user input. And the primary way of doing it today is NgModel.
NgModel is a convenient way of dealing with the input, but it has a few drawbacks:
It does not work with immutable objects.
It does not allow us to work with the form as a whole.
For instance, if in the example below todo is immutable, which we would prefer, NgModel just will not work.
We want to be able to reason about every component in isolation. And we want changes to be predictable and controlled. Functional programming is a common way of achieving it.
Data-oriented programming is a big part of it. Although Angular has always supported it, there are ways to make it even better. For example, by improving support of persistent data structures.
When using functional programming we have stronger guarantees about how things change. We will be able to use this knowledge to control change detection.
The current way of dealing with dealing with the user input, although convenient, promotes mutability. A more elegant way is to treat forms as values, and streams of values.
Types have a bad reputation for making code harder to read, adding unnecessary ceremony, and in general complicating things. In this blog post I'd like to show that a type system done right can help make code more readable and toolable without constraining the expressiveness of the language.
You can find the updated version of this blog post here: (http://victorsavkin.com/post/123555572351/writing-angular-2-in-typescript)
Documentation for Humans and Machines
First, I want to show how type annotations can be used as documentation for both humans and machines.
Let's look at this function jQuery.ajax() and pretend that we are not familiar with it. What kind of information can we get from its signature?
jQuery.ajax(url, settings)
The only thing we can tell for sure is that the function takes two arguments. We can guess the types. Maybe the first one is a string and the second one is a configuration object. But it is just a guess and we might be wrong. We have no idea what options go into the settings object (neither their names nor their types) or what this function returns.
There is no way we can call this function without checking the source code or the documentation. Checking the source code is not a good option -- the point of having functions and classes is to be able to use them without knowing how they are implemented. In other words, we should rely on their interfaces, not on their implementation.The only option we have here is to check the documentation. And there is nothing wrong with that. Except that it takes time.
The settings argument is optional. We can see all the options that can be passed into the function, and not only their names, but also their types.
The function returns a JQueryXHR object, and we can see its properties and functions.
This bring us to my first point.
Types serve as documentation for the programmer.
In addition, this information enables advanced autocompletion, navigation, and refactoring capabilities. Having such tools is almost a requirement for large projects. Without them programmers are afraid to change the code, which makes large-scale refactorings almost impossible. As Anders Hejlsberg puts it, you can build a large application in a dynamically-typed language, but you cannot maintain it. Without advanced tools a large code base is always in a semi-read-only state.
Types enable advanced tools.
Types Provide a Conceptual Framework for the Programmer
A good design is all about well-defined interfaces. And it is much easier to express the idea of an interface in a language that supports them.
For instance, imagine a book-selling application where a purchase can be made by either a registered user through the UI or by an external system through some sort of an API.
As you can see, both classes play the role of a purchaser. Despite being extremely important for the application, the notion of a purchaser is not clearly expressed in the code. There is no file named purchaser.js. And as a result, it is possible for someone modifying the code to miss the fact that this role even exists.
function processPurchase(purchaser, details){ } class User { } class ExternalSystem { }
It is hard, just by looking at the code, tell what objects can play the role of a purchaser, and what methods this role has. We do not know for sure and we will not get much help from our tools. We have to infer this information manually, which is slow and error-prone.
Now, compare it with a version where we explicitly define the Purchaser interface.
interface Purchaser { id: int; bankAccount: Account; } class User implements Purchaser {} class ExternalSystem implements Purchaser {}
The typed version clearly states that we have the Purchaser interface, and the User and ExternalSystem classes implement it. This brings us to my next point.
Types are useful for defining interfaces/protocols/roles. They provide a conceptual framework for the programmer.
It is important to realize that types do not force us to introduce extra abstractions. The Purchaser abstraction is present in the JavaScript version of the code, but it is not explicitly defined.
I've spent most of my career programming in Ruby and JavaScript, and I find these languages extremely powerful and flexible. But after working on sizeable apps written in these languages, I came to the conclusion that the lack of interfaces pushes developers toward building tightly coupled systems.
In a statically typed language boundaries between subsystems are defined using interfaces. Since Ruby and JavaScript lack interfaces, boundaries are not well expressed in code. Not being able to clearly see the boundaries, developers start depending on concrete types instead of abstract interfaces. And it leads to tight coupling. It is possible to build large decoupled systems in these languages, but it requires a lot of discipline.
Statically Typed Languages?
It may seem that I am advocating statically typed languages, but I am not. The arguments against a mandatory static type system are still sound.
It complicates the language. Writing a mock library in JavaScript is an exercise that almost everyone can undertake. Doing similar stuff in Java is far more difficult.
It constrains the expressiveness of the language.
It requires type annotations even when they are not desirable (e.g., in a domain specific language).
What we want is something that combines the power of a dynamically typed language with the benefits of having explicit interfaces and type annotations.
Optional Type Systems
An optional type system does not require using type annotations. Type annotations are used to help you communicate your intent and get better tooling.
Optional type systems are forgiving. You do not have to satisfy the type checker. Quite the opposite, the type checker is there to help you find typos, search, and refactor. You do not completely rewrite your program to make the types work. If you are prototyping something, and duck typing works, just do not use types. Use them only when they add value.
It would not be completely fair to say that optional typing is only good stuff without any drawbacks. It still adds some complexity to the language (nothing compared to a mandatory static type system though).Also, to fully see the benefits of such a system you need more sophisticated tools than a plain text editor. But because of the reasons I outlined in this blog post, I think it is worth paying this price.
Typed JavaScript
I wrote the original version of this blog post some time ago, and called it "I Wish Ruby Had Interfaces". Back then I was a Rails developer. And I was extremely frustrated by working on a very large Rails code base, where some refactorings were taking months to finish. There was no hope Ruby would get an optional type system, so it was just a rant.
This time it is different. The JavaScript community is embracing the idea of optional types. TypeScript has become a robust tool used in production at many companies. AtScript, and Flow are new projects in this area. I am really excited to see what typed JavaScript community will look like in a few years.
Angular 1.x has two APIs for injecting dependencies into a directive. These APIs are not interchangeable, so depending on what you are injecting, you need to use one or the other. Angular 2 unifies the two APIs, making the code easier to understand and test.
Angular 1.x
Let's start with a simple example: a directive autocompleting the user input.
<input name="title" autocomplete="movie-title">
The autocomplete directive takes the user input, and using the autocompleter service object, gets matching movie titles from the backend.
Note, we have to use two mechanisms to inject the autocompleter and the element.
The autocompleter service is injected into the directive factory function, and it is done by name.
The element and the attributes are injected into the link function. This is done by position, which forces us to pass in scope, even though we may not need it.
Angular 2
Now, contrast it with the Angular 2 way of defining the same directive.
function Autocomplete(autocompleter, el, attrs) { //... } Autocomplete.annotations = [new Decorator({selector: '[autocomplete]'})]; Autocomplete.parameters = [[Autocompleter], [NgElement], [NgAttributes]];
In Angular 2 the autocompleter, the element, and the attributes are injected into the constructor by name.
Hierarchical Injectors
How is it possible? Should not every instance of the directive get its own element? It should. To enable that the framework builds a tree of injectors that matches the DOM.
Injector Matching Div | |__Injector Matching Movie Title | |__Injector Matching Actor Name
Since there is an injector for every DOM element, the framework can provide element-specific information, such as an element, attributes, or a shadow root.
Summary
In Angular 2 there is a single way to inject dependencies into a directive, regardless of what you inject: user-defined services, framework services, elements, attributes, or other directives.