Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Angular 2 Tutorial in JavaScript/ES5 - Part 4 and 5

5.00/5 (2 votes)
5 Mar 2016MIT7 min read 22.5K   559  
This post presents a JavaScript (ECMAScript 5) version of the Angular 2 tutorial Tour of Heroes. Learn how to implement the TypeScript examples in JavaScript/ES5. This article covers part 4 and 5 of the original tutorial.

Written for Angular 2 (Version 2.0.0-beta7). May work with other versions.

Introduction

This is the second artilce of this series. In the first post we created a small Angular 2 Application with two components. In this article we will add services with dependency injection and routing / navigation.

You can find the first article here: Angular 2 Tutorial in JavaScript/ES5 - Part 1 to 3

Using the code

Part 4 - Dependency Injection

The most important aspect in part 4 will be to pass a service in our AppComponent using dependency injection.

Create the Service to Inject

Create a service

For the HeroService we create a separate JavaScript file named hero.service.js. We register the object as app.HeroService and start with an constructor. The code snippet below already contains the public getHeroes() function.

JavaScript
// HeroService stub (js)
(function (app) {
    app.HeroService = ng.core
        .Class({
            constructor: function () {
                this.getHeroes = function() {
                    return HEROES;
                };
            }
        });
})(window.app || (window.app = {}));
Injectable Decoration

In TypeScript, the HeroService gets decorated with Injectable().

JavaScript
// HeroService class (ts)
@Injectable()
    export class HeroService {
}

In the transpiled Javascript code we find a corresponding statement. But it seems to have no influence on the class's behavior if we skipped it.

Mock array

The HeroService's getHeroes() function returns the mock data HEROES. Following the tutorial, we move this array from app.js to its own file mock-heroes.js.

Register Scripts in index.html

Don't forget to register the new files hero.service.js and mock-heroes.js in index.html. The script registration now should look like this.

HTML
<!-- script registration in index.html -->
<script src='app/mock-heroes.js'></script>
<script src='app/hero.service.js'></script>
<script src='app/hero-detail.js'></script>
<script src='app/app.js'></script>
<script src='app/main.js'></script>

Dependency Injection

In the next step we inject the HeroService in AppComponent. To resolve the dependencies correctly in Angular 2, we have to perform two steps:

Register the Provider

We register the provider at the component. This is almost the same for TypeScript and JavaScript. All we have to do change, is to specify, how to resolve the app.HeroService from our Angular app directly in the providers property:

JavaScript
// Provider registration (js)
app.AppComponent = ng.core
    .Component({
        // ...
        providers: [ app.HeroService ]
    })
Inject the Service in the Constructor

Here comes the trickiest part. In TypeScript we can inject the provided instance in the constructor as arguments by providing the following details:

  • access modifier
  • variable name
  • variable type

In our example, it looks like this:

JavaScript
// AppComponent constructor (ts)
constructor(private _heroService: HeroService) { }

The JavaScript version of this code is not as obvious on the first look. Instead of a single function we define the constructor as an array. This array contains the type of every provided instance we want to inject and as last element the constructor function. Then we can pass arguments to constructor, which get resolved as provided dependency types by order.

JavaScript
//AppComponent constructor (js)
constructor: [
    app.HeroService,
    function (heroService) {
        this.heroService = heroService;
    }
]

Note, that there is no relation between provided types and argument names. If we injected the type app.HeroService as a constructor argument named foo, Angular would be totally finde.

Use the Injected Service

If heroService was a public property, we had to add it to the object (e.g. this.heroService = heroService);). But as we keep the object private, we will work with the constructor argument heroService in the following getHeroes() function.

JavaScript
// getHeroes function in AppComponent (js)
this.getHeroes = function() {
    this.heroes = heroService.getHeroes();
};

Now the application will start again. As you can see in the screen shot to the right, the heroes from the service will be listed again in the AppComponent.

Using Angular Life Cycle Hooks

It is a common use case to execute a function in our component, when it passes a certain "state" (e.g. being initialized or being destroyed). Therefore angular provides several life cycle hooks [^]. In this case we want to use the ngOnInit hook to call getHeroes().

Deriving from OnInit

In TypeScript we would implement the interface OnInit that provides a function ngOnInit(), which will be called by Angular during the corresponding life cycle event. In the strict sense this is some sort of inheritance. So we will solve it in JavaScript by providing the correct function on our object's prototype:

JavaScript
//OnInit implementation in AppComponent (js)
app.HeroListComponent.prototype.ngOnInit = 
    function () {
        this.getHeroes();
    };

Other life cycle hooks, like ngOnChanges or ngOnDestroy can be used in the same way.

Using Promises

The last steps in part 4 cover how to process async results with promises. As this is a commonly used practices, you should take a close look at the tutorial. Here only comes the changed function for the heroService:

JavaScript
//heroService (js)
this.getHeroes = function () {
    return new Promise(resolve =>
        setTimeout(() => resolve(HEROES), 2000)
    );
};

Results

After completing these steps, the application will load the heroes from a service. To simulate a remote server, the response is 2 seconds delayed.

Part 5 - Routing and Navigation

Tutorial part 5 is all about routing and navigating in our app. Therefore we use the Angular 2 default router from ng.router package.

Add a push URL Base

To enable client side navigation in the web browser, we use so called push states. Have a look at the angular documentation [^] and the mdn documentation [^] about this topic. In our application we implement a <base href="/"> element in our index.html.

HTML
<!-- index.html -->
<head>
    <base href="/">

If you get the following exception in an Angular 2 application, it is very likely that you are missing this line.

EXCEPTION: Error during instantiation of LocationStrategy! (RouterLink -> Router -> Location -> LocationStrategy).

Configure the Router

Router Provider

In Angular 2 we need one component to host the router. This will be our AppComponent. To tell our application how to resolve a router instance, we reference the router provider ng.router.ROUTER_PROVIDERS.

JavaScript
// AppComponent (js)
.Component({
    // ...
    providers: [ng.router.ROUTER_PROVIDERS, app.HeroService]
})
Configure a Route

Next we can configure our first route to a component by specifying a path, a name and the component's type. The first two properties are strings, the latter is, well..., the component's type as object. By adding the property useAsDefault we tell the router which route to resolve if none was selected. To add the route to a route configuration, we pass it as argument to ng.router.RouterConfig(). Usually we configure multiple routes at once. Therefore RouterConfig() takes an array as argument. Then we decorate our AppComponent with this instance.

JavaScript
// AppComponent (js)
app.AppComponent = __decorate([
    ng.router.RouteConfig([ {
            path: '/heroes',
            name: 'Heroes',
            component: app.HeroesComponent,
            useAsDefault: true
        }])
    ],
    app.AppComponent);

The __decorate() function in the code example is what we get from transpiled TypeScript code. Not the most readable part but it works.

Use the Router

We want to call a router link to navigate through the router's states.

Register the Router Directive

To use the router directives in HTML, we first reference the router directives.

JavaScript
// AppComponent (js)
.Component({
    // ...
    directives: [ng.router.ROUTER_DIRECTIVES],
    providers: [ng.router.ROUTER_PROVIDERS, app.HeroService]
})
Router Links and Router Outlet

Having this implement in JavaScript, we can simply add a routerLink and a <router-outlet /> in HTML:

HTML
<!-- app.html -->
<a [routerLink]="['Heroes']">Heroes</a>
<router-outlet>Heroes</router-outlet>

Results

When we start the app now, we see that the router navigated to our heroes component already. Note how the browser URL changed. Yet the heroes route is the only route we have and there is nothing to navigate to. We will change this in the following part.

Completing the Tutorial

Adding the router and configuring the routes was the toughest tasks of this part. Now we add a new component named dashboard and set up the routes between dashboard, hero list and hero detail view. When you stick with the original TypeScript tutorial, implementing this in JavaScript should not be that hard. You can find my JavaScript solution for part 5 attached to this post.

Conclusion

With a little effort, it is possible to set up this application using only JavaScript / ECMAScript 5. For a first approach to Angular 2, there is no need to deep-dive into TypeScript, altough some TypeScript knwoledge is necessary to understand the documentation and hints. If I had known these steps in advance, I'd saved myself quite some time of research, debugging and trying.

How were your first experiences with Angular 2, especially in combination with JavaScript/ES5? Did you try different appraoches? Could you figure out a simpler solution for certain aspects or did I miss something? Please let me know in the comments below.

History

  • 5th March, 2016: Initial version

License

The original tutorial, in particular the source code files, are published under an MIT-style license that can be found in the LICENSE file at http://angular.io/license. This tutorial and the source code modifications are published under MIT license, that can be found at https://opensource.org/licenses/MIT.

License

This article, along with any associated source code and files, is licensed under The MIT License