Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Angular 2 Cookbook
Angular 2 Cookbook

Angular 2 Cookbook: Discover over 70 recipes that provide the solutions you need to know to face every challenge in Angular 2 head on

Arrow left icon
Profile Icon Patrick Gillespie Profile Icon Matthew Frisbie
Arrow right icon
Free Trial
Full star icon Full star icon Full star icon Full star icon Half star icon 4.2 (24 Ratings)
Paperback Jan 2017 464 pages 1st Edition
eBook
S$12.99 S$59.99
Paperback
S$74.99
Subscription
Free Trial
Arrow left icon
Profile Icon Patrick Gillespie Profile Icon Matthew Frisbie
Arrow right icon
Free Trial
Full star icon Full star icon Full star icon Full star icon Half star icon 4.2 (24 Ratings)
Paperback Jan 2017 464 pages 1st Edition
eBook
S$12.99 S$59.99
Paperback
S$74.99
Subscription
Free Trial
eBook
S$12.99 S$59.99
Paperback
S$74.99
Subscription
Free Trial

What do you get with a Packt Subscription?

Free for first 7 days. $19.99 p/m after that. Cancel any time!
Product feature icon Unlimited ad-free access to the largest independent learning library in tech. Access this title and thousands more!
Product feature icon 50+ new titles added per month, including many first-to-market concepts and exclusive early access to books as they are being written.
Product feature icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Product feature icon Thousands of reference materials covering every tech concept you need to stay up to date.
Subscribe now
View plans & pricing
Table of content icon View table of contents Preview book icon Preview Book

Angular 2 Cookbook

Chapter 1. Strategies for Upgrading to Angular 2

This chapter will cover the following recipes:

  • Componentizing directives using the controllerAs encapsulation
  • Migrating an application to component directives
  • Implementing a basic component in AngularJS 1.5
  • Normalizing service types
  • Connecting Angular 1 and Angular 2 with UpgradeModule
  • Downgrading Angular 2 components to Angular 1 directives with downgradeComponent
  • Downgrading Angular 2 providers to Angular 1 services with downgradeInjectable

Introduction

The introduction of Angular 2 into the Angular ecosystem will surely be interpreted and handled differently for all developers. Some will stick to their existing Angular 1 codebases, some will start brand new Angular 2 codebases, and some will do a gradual or partial transition.

It is recommended that you become familiar with the behavior of Angular 2 components before you dive into these recipes. This will help you frame mental models as you adapt your existing applications to be more compliant with the Angular 2 style.

Componentizing directives using controllerAs encapsulation

One of the unusual conventions introduced in Angular 1 was the relationship between directives and the data they consumed. By default, directives used an inherited scope, which suited the needs of most developers just fine. While this was easy to use, it had the effect of introducing extra dependencies in the directives, and also the convention that directives often did not own the data they were consuming. Additionally, the data interpolated in the template was unclear in relation to where it was being assigned or owned.

Angular 2 utilizes components as the building blocks of the entire application. These components are class-based and are therefore in some ways at odds with the scope mechanisms of Angular 1. Transitioning to a controller-centric directive model is a large step towards compliance with the Angular 2 standards.

Note

The code, links, and a live example related to this recipe are available at http://ngcookbook.herokuapp.com/8194.

Getting ready

Suppose your application contains the following setup that involves the nested directives that share data using an isolate scope:

[index.html] 
 
<div ng-app="articleApp"> 
  <article></article> 
</div> 
[app.js] 
 
angular.module('articleApp', []) 
.directive('article', function() { 
  return { 
    controller: function($scope) { 
      $scope.articleData = { 
        person: {firstName: 'Jake'}, 
        title: 'Lesotho Yacht Club Membership Booms' 
     }; 
   }, 
    template: ` 
      <h1>{{articleData.title}}</h1> 
      <attribution author="articleData.person.firstName"> 
      </attribution> 
    ` 
 }; 
}) 
.directive('attribution', function() { 
  return { 
    scope: {author: '='}, 
    template: `<p>Written by: {{author}}</p>` 
 }; 
}); 

How to do it...

The goal is to refactor this setup so that templates can be explicit about where the data is coming from and so that the directives have ownership of this data:

[app.js] 
 
angular.module('articleApp', []) 
.directive('article', function() { 
   return { 
   controller: function() { 
      this.person = {firstName: 'Jake'}; 
      this.title = 'Lesotho Yacht Club Membership Booms'; 
   }, 
    controllerAs: 'articleCtrl', 
    template: ` 
      <h1>{{articleCtrl.title}}</h1> 
      <attribution></attribution> 
    ` 
 }; 
}) 
.directive('attribution', function() { 
   return { 
    template: `<p>Written by: {{articleCtrl.author}}</p>` 
 }; 
}); 

In this second implementation, anywhere you use the article data, you are certain of its origin. This is better, but the child directive is still referencing the parent controller, which isn't ideal since it is introducing an unneeded dependency. The attribution directive instance should be provided with the data, and it should instead interpolate from its own controller instance:

[app.js]  
 
angular.module('articleApp', []) 
.directive('article', function() { 
  return { 
    controller: function() { 
      this.person = {firstName: 'Jake'}; 
      this.title = 'Lesotho Yacht Club Membership Booms'; 
   }, 
    controllerAs: 'articleCtrl', 
    template: ` 
      <h1>{{articleCtrl.title}}</h1> 
      <attribution author="articleCtrl.person.firstName"> 
      </attribution> 
    ` 
 }; 
}) 
.directive('attribution', function() { 
  return { 
    controller: function() {}, 
    controllerAs: 'attributionCtrl', 
    bindToController: {author: '='}, 
    template: `<p>Written by: {{attributionCtrl.author}}</p>` 
 }; 
}); 

Much better! You provide the child directive with a stand-in controller and give it an alias in the attributionCtrl template. It is implicitly bound to the controller instance via bindToController in the same way you would accomplish a regular isolate scope; however, the binding is directly attributed to the controller object instead of the scope.

Now that you have introduced the notion of data ownership, suppose you want to modify your application data. What's more, you want different parts of your application to be able to modify it. A naïve implementation of this would be something as follows:

[app.js] 
 
angular.module('articleApp', []) 
.directive('attribution', function() { 
  return { 
    controller: function() { 
      this.capitalize = function() { 
        this.author = this.author.toUpperCase(); 
     } 
   }, 
    controllerAs: 'attributionCtrl', 
    bindToController: {author: '='}, 
    template: ` 
      <p ng-click="attributionCtrl.capitalize()"> 
        Written by: {{attributionCtrl.author}} 
      </p>` 
 }; 
}); 

The desired behavior is for you to click on the author, and it will become capitalized. However, in this implementation, the article controller's data is modified in the attribution controller, which does not own it. It is preferable for the controller that owns the data to perform the actual modification and instead supply an interface that an outside entity—here, the attribution directive—could use:

[app.js] 
 
angular.module('articleApp', []) 
.directive('article', function() { 
  return { 
    controller: function() { 
      this.person = {firstName: 'Jake'}; 
      this.title = 'Lesotho Yacht Club Membership Booms'; 
      this.capitalize = function() { 
        this.person.firstName =   
          this.person.firstName.toUpperCase(); 
     }; 
   }, 
    controllerAs: 'articleCtrl', 
    template: ` 
      <h1>{{articleCtrl.title}}</h1> 
      <attribution author="articleCtrl.person.firstName" 
                   upper-case-author="articleCtrl.capitalize()"> 
      </attribution> 
    ` 
 }; 
}) 
.directive('attribution', function() { 
  return { 
    controller: function() {}, 
    controllerAs: 'attributionCtrl', 
    bindToController: { 
      author: '=', 
      upperCaseAuthor: '&' 
   }, 
    template: ` 
      <p ng-click="attributionCtrl.upperCaseAuthor()"> 
        Written by: {{attributionCtrl.author}} 
      </p>` 
 }; 
});

Vastly superior! You are still able to namespace within the click binding, but the owning directive controller is providing a method to outside entities instead of just giving them direct data access.

How it works...

When a controller is specified in the directive definition object, one will be explicitly instantiated for each directive instance that is created. Thus, it is natural for this controller object to encapsulate the data that it owns and for it to be delegated the responsibility of passing its data to the members that require it.

The final implementation accomplishes several things:

  • Improved template namespacing: When you use the $scope properties that span multiple directives or nestings, you are creating a scenario where multiple entities can manipulate and read data without being able to concretely reason about where it is coming from or what is controlling it.
  • Improved testability: If you look at each of the directives in the final implementation, you'll find they are not too difficult to test. The attribution directive has no dependencies other than what are explicitly passed to it.
  • Encapsulation: Introducing the notion of data ownership in your application affords you a much more robust structure, better reusability, and additional insight and control involving pieces of your application interacting.
  • Angular 2 style: Angular 2 uses the @Input and @Output annotations on component definitions. Mirroring this style will make the process of transitioning to an application easier.

There's more...

You will notice that $scope has been made totally irrelevant in these examples. This is good as there is no notion of $scope in Angular 2, which means you are heading towards having an upgradeable application. This is not to say that $scope does not still have utility in an Angular 1 application, and surely, there are scenarios where this is unavoidable, like with $scope.$apply().

However, thinking about the application pieces in this component style will allow you to be more adequately prepared to adopt Angular 2 conventions.

See also

  • Migrating an application to component directives demonstrates how to refactor Angular 1 to a component style
  • Implementing a basic component in AngularJS 1.5 details how to write an Angular 1 component
  • Normalizing service types gives instruction on how to align your Angular 1 service types for Angular 2 compatibility

Migrating an application to component directives

In Angular 1, there are several built-in directives, including ngController and ngInclude, that developers tend to lean on when building applications. While not anti-patterns, using these features moves away from having a component-centric application.

All these directives are actually subsets of component functionality, and they can be entirely refactored out.

Note

The code, links, and a live example related to this recipe are available at http://ngcookbook.herokuapp.com/1008/.

Getting ready

Suppose your initial application is as follows:

[index.html] 
 
<div ng-app="articleApp"> 
  <ng-include src="'/press_header.html'"></ng-include> 
  <div ng-controller="articleCtrl as article"> 
    <h1>{{article.title}}</h1> 
    <p>Written by: {{article.author}}</p> 
  </div> 
  <script type="text/ng-template"  
          id="/press_header.html"> 
  <div ng-controller="headerCtrl as header"> 
    <strong> 
      Angular Chronicle - {{header.currentDate | date}} 
    </strong> 
   <hr /> 
  </div> 
  </script> 
</div> 
[app.js] 
 
angular.module('articleApp', []) 
.controller('articleCtrl', function() { 
  this.title = 'Food Fight Erupts During Diplomatic Luncheon'; 
  this.author = 'Jake'; 
}) 
.controller('headerCtrl', function() { 
   this.currentDate = new Date(); 
}); 

Note

Note that this example application contains a large number of very common Angular 1 patterns; you can see the ngController directives sprinkled throughout. Also, it uses an ngInclude directive to incorporate a header. Keep in mind that these directives are not inappropriate for a well-formed Angular 1 application. However, you can do better, and this involves refactoring to a component-driven design.

How to do it...

Component-driven patterns don't need to be frightening in appearance. In this example (and for essentially all Angular 1 applications), you can do a component refactor while leaving the existing template largely intact.

Begin with the ngInclude directive. Moving this to a component directive is simple—it becomes a directive with templateUrl set to the template path:

[index.html] 
 
<div ng-app="articleApp"> 
  <header></header> 
  <div ng-controller="articleCtrl as article"> 
    <h1>{{article.title}}</h1> 
    <p>Written by: {{article.author}}</p> 
  </div> 
  <script type="text/ng-template"  
          id="/press_header.html"> 
  <div ng-controller="headerCtrl as header"> 
   <strong> 
      Angular Chronicle - {{header.currentDate | date}} 
    </strong> 
   <hr /> 
  </div> 
  </script> 
</div> 
[app.js] 
 
angular.module('articleApp', []) 
.controller('articleCtrl', function() { 
  this.title = 'Food Fight Erupts During Diplomatic Luncheon'; 
  this.author = 'Jake'; 
}) 
.controller('headerCtrl', function() { 
   this.currentDate = new Date(); 
}) 
.directive('header', function() { 
   return { 
   templateUrl: '/press_header.html' 
 }; 
}); 

Next, you can also refactor ngController everywhere it appears. In this example, you find two extremely common appearances of ngController. The first is at the head of the press_header.html template, acting as the top-level controller for that template. Often, this results in needing a superfluous wrapper element just to house the ng-controller attribute. The second is ngController nested inside your primary application template, controlling some arbitrary portion of the DOM. Both of these can be refactored to component directives by reassigning ngController to a directive controller:

[index.html] 
 
<div ng-app="articleApp"> 
  <header></header> 
  <article></article> 
</div> 
[app.js] 
 
angular.module('articleApp', []) 
.directive('header', function() { 
  return { 
    controller: function() { 
      this.currentDate = new Date(); 
    }, 
    controllerAs: 'header', 
    template: ` 
      <strong> 
        Angular Chronicle - {{header.currentDate | date}} 
      </strong> 
      <hr /> 
    ` 
 }; 
}) 
.directive('article', function() { 
   return { 
   controller: function() { 
      this.title = 'Food Fight Erupts During Diplomatic Luncheon'; 
      this.author = 'Jake'; 
   }, 
    controllerAs: 'article', 
    template: `     
      <h1>{{article.title}}</h1> 
      <p>Written by: {{article.author}}</p> 
    ` 
 }; 
}); 

Tip

Note that templates here are included in the directive for visual congruity. For large applications, it is preferred that you use templateUrl and locate the template markup in its own file.

How it works...

Generally speaking, an application can be represented by a hierarchy of nested MVC components. ngInclude and ngController act as subsets of a component functionality, and so it makes sense that you are able to expand them into full component directives.

In the preceding example, the ultimate application structure is comprised of only components. Each component is delegated its own template, controller, and model (by virtue of the controller object itself). Sticklers will dispute whether or not Angular belongs to true MVC style, but in the context of component refactoring, this is irrelevant. Here, you have defined a structure that is completely modular, reusable, testable, abstractable, and easily maintainable. This is the style of Angular 2, and the value of this should be immediately apparent.

There's more...

An alert developer will notice that no attention is paid to scope inheritance. This is a difficult problem to approach, mostly because many of the patterns in Angular 1 are designed for a mishmash between a scope and controllerAs. Angular 2 is built around strict input and output between nested components; however, in Angular 1, scope is inherited by default, and nested directives, by default, have access to their encompassing controller objects.

Thus, to truly emulate an Angular 2 style, one must configure their application to explicitly pass data and methods to children, similar to the controllerAs encapsulation recipe. However, this does not preclude direct data access to ancestral component directive controllers; it merely wags a finger at it since it adds additional dependencies.

See also

  • Componentizing directives using controllerAs encapsulation shows you a superior method of organizing Angular 1 directives
  • Implementing a basic component in AngularJS 1.5 details how to write an Angular 1 component
  • Normalizing service types gives instruction on how to align your Angular 1 service types for Angular 2 compatibility

Implementing a basic component in AngularJS 1.5

The 1.5 release of AngularJS introduced a new tool: the component. While it isn't exactly similar to the concept of the Angular 2 component, it does allow you to build directive-style pieces in an explicitly componentized fashion.

Note

The code, links, and a live example related to this recipe are available at http://ngcookbook.herokuapp.com/7756/.

Getting ready

Suppose your application had a directive defined as follows:

[index.html] 
 
<div ng-app="articleApp"> 
  <article></article> 
</div> 
[app.js] 
 
angular.module('articleApp', []) 
.directive('article', function() { 
  return { 
    controller: function() { 
      this.person = {firstName: 'Jake'}; 
      this.title = 'Police Bust Illegal Snail Racing Ring'; 
      this.capitalize = function() { 
        this.person.firstName =   
          this.person.firstName.toUpperCase(); 
     }; 
   }, 
    controllerAs: 'articleCtrl', 
    template: ` 
      <h1>{{articleCtrl.title}}</h1> 
      <attribution author="articleCtrl.person.firstName" 
                   upper-case-author="articleCtrl.capitalize()"> 
      </attribution> 
    ` 
 }; 
}) 
.directive('attribution', function() { 
  return { 
    controller: function() {}, 
    controllerAs: 'attributionCtrl', 
    bindToController: { 
      author: '=', 
      upperCaseAuthor: '&' 
   }, 
    template: ` 
      <p ng-click="attributionCtrl.upperCaseAuthor()"> 
        Written by: {{attributionCtrl.author}} 
      </p>` 
 }; 
}); 

How to do it...

Since this application is already organized around the controllerAs encapsulation, you can migrate it to use the component() definition introduced in the Angular 1.5 release.

Components accept an object definition similar to a directive, but the object does not demand to be returned by a function—an object literal is all that is needed. Components utilize the bindings property in this object definition object in the same way that bindToController works for directives. With this, you can easily introduce components in this application instead of directives:

[index.html] 
 
<div ng-app="articleApp"> 
  <article></article> 
</div> 
[app.js] 
 
angular.module('articleApp', []) 
.component('article', { 
  controller: function() { 
    this.person = {firstName: 'Jake'}; 
    this.title = ' Police Bust Illegal Snail Racing Ring '; 
    this.capitalize = function() { 
      this.person.firstName =   
        this.person.firstName.toUpperCase(); 
   }; 
 }, 
  controllerAs: 'articleCtrl', 
  template: ` 
    <h1>{{articleCtrl.title}}</h1> 
    <attribution author="articleCtrl.person.firstName" 
                 upper-case-author="articleCtrl.capitalize()"> 
   </attribution>` 
}) 
.component('attribution', { 
  controller: function() {}, 
  controllerAs: 'attributionCtrl', 
  bindings: { 
    author: '=', 
    upperCaseAuthor: '&' 
 }, 
  template: ` 
    <p ng-click="attributionCtrl.upperCaseAuthor()"> 
      Written by: {{attributionCtrl.author}} 
    </p> 
  ` 
}); 

How it works...

Notice that since your controller-centric data organization matches what a component definition expects, no template modifications are necessary. Components, by default, will utilize an isolate scope. What's more, they will not have access to the alias of the surrounding controller objects, something that cannot be said for component-style directives. This encapsulation is an important offering of the new component feature, as it has direct parity to how components operate in Angular 2.

There's more...

Since you have now entirely isolated each individual component, there is only a single controller object to deal with in each template. Thus, Angular 1.5 automatically provides a convenient alias for the component's controller object, namely—$ctrl. This is provided whether or not a controllerAs alias is specified. Therefore, a further refactoring yields the following:

[index.html] 
 
<div ng-app="articleApp"> 
  <article></article> 
</div> 
[app.js] 
 
angular.module('articleApp', []) 
.component('article', { 
  controller: function() { 
    this.person = {firstName: 'Jake'}; 
    this.title = 'Police Bust Illegal Snail Racing Ring'; 
    this.capitalize = function() { 
      this.person.firstName =   
        this.person.firstName.toUpperCase(); 
   }; 
 }, 
  template: ` 
    <h1>{{$ctrl.title}}</h1> 
    <attribution author="$ctrl.person.firstName" 
                 upper-case-author="$ctrl.capitalize()"> 
   </attribution> 
  ` 
}) 
.component('attribution', { 
  controller: function() {}, 
  bindings: { 
    author: '=', 
    upperCaseAuthor: '&' 
 }, 
  template: ` 
    <p ng-click="$ctrl.upperCaseAuthor()"> 
      Written by: {{$ctrl.author}} 
    </p> 
  ` 
}); 

See also

  • Componentizing directives using controllerAs encapsulation shows you a superior method of organizing Angular 1 directives
  • Migrating an application to component directives demonstrates how to refactor Angular 1 to a component style
  • Normalizing service types gives instruction on how to align your Angular 1 service types for Angular 2 compatibility

Normalizing service types

Angular 1 developers will be quite familiar with the factory/service/provider trifecta. In many ways, this has gone largely unaltered in Angular 2 conceptually. However, in the interest of upgrading an existing application, there is one thing that should be done to make the migration as easy as possible: eliminate factories and replace them with services.

Note

The code, links, and a live example related to this recipe are available at http://ngcookbook.herokuapp.com/5637/.

Getting ready

Suppose you had a simple application as follows:

[index.html] 
 
<div ng-app="articleApp"> 
  <article></article> 
</div> 
[app.js] 
 
angular.module('articleApp', []) 
.factory('ArticleData', function() { 
  var title = 'Incumbent Senator Ousted by Stalk of Broccoli'; 
  return { 
    getTitle: function() { 
      return title; 
   }, 
    author: 'Jake' 
 }; 
}) 
.component('article', { 
  controller: function(ArticleData) { 
    this.title = ArticleData.getTitle(); 
    this.author = ArticleData.author; 
 }, 
  template: ` 
    <h1>{{$ctrl.title}}</h1> 
    <p>Written by: {{$ctrl.author}}</p> 
  ` 
}); 

How to do it...

Angular 2 is class-based, and it includes its service types as well. The example here does not have a service type that is compatible with a class structure. So it must be converted. Thankfully, this is quite easy to do:

[index.html] 
 
<div ng-app="articleApp"> 
  <article></article> 
</div> 
[app.js] 
 
angular.module('articleApp', []) 
.service('ArticleData', function() { 
   var title = 'Incumbent Senator Ousted by Stalk of Broccoli'; 
   this.getTitle = function() { 
   return title; 
 }; 
  this.author = 'Jake'; 
}) 
.component('article', { 
  controller: function(ArticleData) { 
    this.title = ArticleData.getTitle(); 
    this.author = ArticleData.author; 
 }, 
  template: ` 
    <h1>{{$ctrl.title}}</h1> 
    <p>Written by: {{$ctrl.author}}</p> 
  ` 
}); 

How it works...

You still want to keep the notion of title private, but you also want to maintain the API that the injected service type is providing. Services are defined by a function that acts as a constructor, and an instance created from this constructor is what is ultimately injected. Here, you are simply moving getTitle() and author to be defined on the this keyword, which thereby makes it a property on all instances. Note that the use in the component and template does not change in any way, and it shouldn't.

There's more...

The simplest to implement service types, Angular 1 factories were often used first by many developers, including myself. Some developers might take offense at the following claim, but I don't think there was ever a good reason for both factories and services to exist. Both have a high degree of redundancy, and if you dig down into the Angular source code, you will see that they essentially converge to the same methods.

Why services over factories then? The new world of JavaScript, ES6, and TypeScript is being built around classes. They are a far more elegant way of expressing and organizing logic. Angular 1 services are an implementation of prototype-based classes, which when used correctly function in essentially the same way as formal ES6/TypeScript classes. If you stop here, you will have modified your services to be more extensible and comprehensible. If you intend to upgrade, you will find that Angular 1 services will cleanly upgrade to Angular 2 services.

See also

  • Componentizing directives using controllerAs encapsulation shows you a superior method for organizing Angular 1 directives
  • Migrating an application to component directives demonstrates how to refactor Angular 1 to a component style
  • Implementing a basic component in AngularJS 1.5 details how to write an Angular 1 component

Connecting Angular 1 and Angular 2 with UpgradeModule

Angular 2 comes with the ability to connect it to an existing Angular 1 application. This is obviously advantageous since this will allow you to utilize existing components and services in Angular 1 in tandem with Angular 2's components and services. UpgradeModule is the tool that is supported by Angular teams to accomplish such a feat.

Note

The code, links, and a live example in relation to this recipe are available at http://ngcookbook.herokuapp.com/4137/.

Getting ready

Suppose you had a very simple Angular 1 application as follows:

[index.html] 
 
<!DOCTYPE html> 
<html> 
  <head> 
    <!-- Angular 1 scripts --> 
    <script src="angular.js"></script> 
  </head> 
  <body> 
    <div ng-app="hybridApp" 
         ng-init="val='Angular 1 bootstrapped successfully!'"> 
      {{val}}   
    </div> 
  </body> 
</html> 

This application interpolates a value set in an Angular expression so you can visually confirm that the application has bootstrapped and is working.

How to do it...

Begin by declaring the top-level angular module inside its own file. Instead of using a script tag to fetch the angular module, require Angular 1, import it, and create the root Angular 1 module:

[ng1.module.ts] 
 
import 'angular' 
 
export const Ng1AppModule = angular.module('Ng1AppModule', []); 

Angular 2 ships with an upgrade module out of the box, which is provided inside upgrade.js. The two frameworks can be connected with UpgradeModule.

Note

This recipe utilizes SystemJS and TypeScript, the specifications for which lie inside a very complicated config file. This is discussed in a later chapter, so don't worry about the specifics. For now, you are free to assume the following:

  • SystemJS is configured to compile TypeScript (.ts) files on the fly
  • SystemJS is able to resolve the import and export statements in TypeScript files
  • SystemJS is able to resolve Angular 1 and 2 library imports

Angular 2 requires a top-level module definition as part of its base configuration:

[app/ng2.module.ts] 
 
import {NgModule} from '@angular/core'; 
import {BrowserModule} from '@angular/platform-browser'; 
import {UpgradeModule} from '@angular/upgrade/static';  
import {RootComponent} from './root.component'; 
 
@NgModule({ 
  imports: [ 
    BrowserModule, 
    UpgradeModule, 
  ], 
  bootstrap: [ 
    RootComponent 
  ], 
  declarations: [ 
    RootComponent 
  ] 
}) 
export class Ng2AppModule { 
  constructor(public upgrade: UpgradeModule){} 
} 
export class AppModule {} 

Tip

The reason why this module definition exists this way isn't critical for understanding this recipe. Angular 2 modules are covered in Chapter 7, Services, Dependency Injection, and NgModule.

Create the root component of the Angular 2 application:

[app/root.component.ts] 
import {Component} from '@angular/core'; 
 
@Component({ 
  selector: 'root', 
  template: ` 
    <p>Angular 2 bootstrapped successfully!</p> 
  ` 
}) 
export class RootComponent {} 

Since Angular 2 will often bootstrap from a top-level file, create this file as main.ts and bootstrap the Angular 2 module:

[main.ts] 
import {platformBrowserDynamic}  
  from '@angular/platform-browser-dynamic'; 
 
import {Ng1AppModule} from './app/ng1.module'; 
import {Ng2AppModule} from './app/ng2.module'; 
 
platformBrowserDynamic() 
  .bootstrapModule(Ng2AppModule); 

Connecting Angular 1 to Angular 2

Don't use an ng-app to bootstrap the Angular 1 application; instead, do this after you bootstrap Angular 2:

[main.ts] 
 
import {platformBrowserDynamic}  
  from '@angular/platform-browser-dynamic'; 
 
import {Ng1AppModule} from './app/ng1.module'; 
import {Ng2AppModule} from './app/ng2.module'; 
 
platformBrowserDynamic() 
  .bootstrapModule(Ng2AppModule) 
  .then(ref => { 
    ref.instance.upgrade 
      .bootstrap(document.body, [Ng1AppModule.name]); 
  }); 

With this, you'll be able to remove Angular 1's JS script, the ng-app directive, and add in the root element of the Angular 2 app:

[index.html] 
 
<!DOCTYPE html> 
<html> 
  <head> 
    <!-- Angular 2 scripts --> 
    <script src="zone.js "></script> 
    <script src="reflect-metadata.js"></script> 
    <script src="system.js"></script> 
    <script src="system-config.js"></script> 
  </head> 
  <body> 
    <div ng-init="val='Angular 1 bootstrapped successfully!'"> 
      {{val}}   
    </div> 
    <root></root> 
  </body> 
</html> 

Note

The new scripts listed here are dependencies of an Angular 2 application, but understanding what they're doing isn't critical for understanding this recipe. This is explained later in the book.

With all this, you should see your Angular 1 application template compile and the Angular 2 component render properly again. This means that you are successfully running Angular 1 and Angular 2 frameworks side by side.

How it works...

Make no mistake, when you use UpgradeModule, you create an Angular 1 and Angular 2 app on the same page and connect them together. This adapter instance will allow you to connect pieces from each framework and use them in harmony.

More specifically, this creates an Angular 1 application at the top level and allows you to uses pieces of an Angular 2 application inside it.

There's more...

While useful for experimentation and upgrading purposes, this should not be a solution that any application should rely on in a production context. You have effectively doubled the framework payload size and introduced additional complexity in an existing application. Although Angular 2 is a far more performant framework, do not expect to have the same pristine results with the UpgradeModule cross-pollination.

That said, as you will see in subsequent recipes, you can now use Angular 2 components in an Angular 1 application using the adapter translation methods.

See also

  • Downgrading Angular 2 components to Angular 1 directives with downgradeComponent demonstrates how to use an Angular 2 component inside an Angular 1 application
  • Downgrade Angular 2 providers to Angular 1 services with downgradeInjectable, which demonstrates how to use an Angular 2 service inside an Angular 1 application

Downgrading Angular 2 components to Angular 1 directives with downgradeComponent

If you have followed the steps in Connecting Angular 1 and Angular 2 with UpgradeModule, you should now have a hybrid application that is capable of sharing different elements with the opposing framework.

Tip

If you are unfamiliar with Angular 2 components, it is recommended that you go through the components chapter before you proceed.

This recipe will allow you to fully utilize Angular 2 components inside an Angular 1 template.

Note

The code, links, and a live example in relation to this recipe are available at http://ngcookbook.herokuapp.com/1499/.

Getting ready

Suppose you had the following Angular 2 component that you wanted to use in an Angular 1 application:

[app/article.component.ts] 
 
import {Component, Input} from '@angular/core'; 
 
@Component({ 
  selector: 'ng2-article', 
  template: ` 
    <h1>{{title}}</h1> 
    <p>Written by: {{author}}</p> 
  ` 
}) 
export class ArticleComponent { 
  @Input() author:string 
  title:string = 'Unicycle Jousting Recognized as Olympic Sport'; 
} 

Begin by completing the Connecting Angular 1 and Angular 2 with UpgradeModule recipe.

How to do it...

Angular 1 has no comprehension of how to utilize Angular 2 components. The existing Angular 2 framework will dutifully render it if given the opportunity, but the definition itself must be connected to the Angular 1 framework so that it may be requested when needed.

Begin by adding the component declarations to the module definition; this is used to link the two frameworks:

[app/app.module.ts] 
 
import {NgModule} from '@angular/core'; 
import {BrowserModule} from '@angular/platform-browser'; 
import {UpgradeModule} from '@angular/upgrade/static'; 
import {RootComponent} from './root.component'; 
import {ArticleComponent} from './article.component'; 
 
@NgModule({ 
  imports: [ 
    BrowserModule, 
    UpgradeModule, 
  ], 
  declarations: [ 
    RootComponent, 
    ArticleComponent 
  ], 
  bootstrap: [ 
    RootComponent 
  ] 
}) 
export class Ng2AppModule { 
  constructor(public upgrade: UpgradeModule){} 
} 

This connects the component declaration to the Angular 2 context, but Angular 1 still has no concept of how to interface with it. For this, you'll need to use downgradeComponent() to define the Angular 2 component as an Angular 1 directive. Give the Angular 1 directive a different HTML tag to render inside so you can be certain that it's Angular 1 doing the rendering and not Angular 2:

[main.ts] 
 
import {Component, Input} from '@angular/core'; 
import {downgradeComponent} from '@angular/upgrade/static'; 
import {Ng1AppModule} from './ng1.module'; 
 
@Component({ 
  selector: 'ng2-article', 
  template: ` 
    <h1>{{title}}</h1> 
    <p>Written by: {{author}}</p> 
  ` 
}) 
export class ArticleComponent { 
  @Input() author:string 
  title:string = 'Unicycle Jousting Recognized as Olympic Sport'; 
} 
 
Ng1AppModule.directive( 
  'ng1Article', 
  downgradeComponent({component: ArticleComponent})); 

Finally, since this component has an input, you'll need to pass this value via a binding attribute. Even though the component is still being declared as an Angular 1 directive, you'll use the Angular 2 binding syntax:

[index.html] 
 
<!DOCTYPE html> 
<html> 
  <head> 
    <!-- Angular 2 scripts --> 
    <script src="zone.js "></script> 
    <script src="reflect-metadata.js"></script> 
    <script src="system.js"></script> 
    <script src="system-config.js"></script> 
  </head> 
  <body> 
    <div ng-init="authorName='Jake Hsu'"> 
      <ng1-article [author]="authorName"></ng1-article> 
    </div> 
    <root></root> 
  </body> 
</html> 

The input and output must be explicitly declared at the time of conversion:

[app/article.component.ts] 
 
import {Component, Input} from '@angular/core'; 
import {downgradeComponent} from '@angular/upgrade/static'; 
import {Ng1AppModule} from './ng1.module'; 
 
@Component({ 
  selector: 'ng2-article', 
  template: ` 
    <h1>{{title}}</h1> 
    <p>Written by: {{author}}</p> 
  ` 
}) 
export class ArticleComponent { 
  @Input() author:string 
  title:string = 'Unicycle Jousting Recognized as Olympic Sport'; 
} 
 
Ng1AppModule.directive( 
  'ng1Article', 
  downgradeComponent({ 
    component: ArticleComponent,  
    inputs: ['author'] 
  })); 

These are all the steps required. If done properly, you should see the component render along with the author's name being interpolated inside the Angular 2 component through Angular 1's ng-init definition.

How it works...

You are giving Angular 1 the ability to direct Angular 2 to a certain element in the DOM and say, "I need you to render here." Angular 2 still controls the component view and operation, and in every sense, the main thing we really care about is a full Angular 2 component adapted for use in an Angular 1 template.

Tip

downgradeComponent() takes an object specifying the component as an argument and returns the function that Angular 1 is expecting for the directive definition.

See also

  • Connecting Angular 1 and Angular 2 with UpgradeModule shows you how to run Angular 1 and 2 frameworks together
  • Downgrade Angular 2 providers to Angular 1 services with downgradeInjectable demonstrates how to use an Angular 2 service inside an Angular 1 application

Downgrade Angular 2 providers to Angular 1 services with downgradeInjectable

If you have followed the steps in Connecting Angular 1 and Angular 2 with UpgradeModule, you should now have a hybrid application that is capable of sharing different elements with the opposing framework. If you are unfamiliar with Angular 2 providers, it is recommended that you go through the dependency injection chapter before you proceed.

Like with templated components, interchangeability is also offered to service types. It is possible to define a service type in Angular 2 and then inject it into an Angular 1 context.

Note

The code, links, and a live example in relation to this recipe are available at http://ngcookbook.herokuapp.com/2824/.

Getting ready

Begin with the code written in Connecting Angular 1 and Angular 2 with UpgradeModule.

How to do it...

First, define the service you would like to inject into an Angular 1 component:

[app/article.service.ts] 
 
import {Injectable} from '@angular/core'; 
 
@Injectable() 
export class ArticleService { 
  article:Object = { 
    title: 'Research Shows Moon Not Actually Made of Cheese', 
    author: 'Jake Hsu' 
  }; 
} 

Next, define the Angular 1 component that should inject it:

[app/article.component.ts] 
 
export const ng1Article = { 
  template: ` 
    <h1>{{article.title}}</h1> 
    <p>{{article.author}}</p> 
  `, 
  controller: (ArticleService, $scope) => { 
    $scope.article = ArticleService.article; 
  } 
}; 

ArticleService won't be injected yet though, since Angular 1 has no idea that this service exists. Doing this is very simple, however. First, you'll list the service provider in the Angular 2 module definition as you normally would:

[app/ng2.module.ts] 
 
import {NgModule} from '@angular/core'; 
import {BrowserModule} from '@angular/platform-browser'; 
import {UpgradeModule} from '@angular/upgrade/static'; 
import {RootComponent} from './root.component'; 
import {ArticleService} from './article.service'; 
 
@NgModule({ 
  imports: [ 
    BrowserModule, 
    UpgradeModule, 
  ], 
  declarations: [ 
    RootComponent 
  ], 
  providers: [ 
    ArticleService 
  ], 
  bootstrap: [ 
    RootComponent 
  ] 
}) 
export class Ng2AppModule { 
  constructor(public upgrade: UpgradeModule){} 
} 

Still, Angular 1 does not understand how to use the service.

In the same way you convert an Angular 2 component definition into an Angular 1 directive, convert an Angular 2 service into an Angular 1 factory. Use downgradeInjectable and add the Angular 1 component and the converted service to the Angular 1 module definition:

[app/ng1.module.ts] 
 
import 'angular'; 
import {ng1Article} from './article.component'; 
import {ArticleService} from './article.service'; 
import {downgradeInjectable} from '@angular/upgrade/static'; 
 
export const Ng1AppModule = angular.module('Ng1AppModule', []) 
  .component('ng1Article', ng1Article) 
  .factory('ArticleService', downgradeInjectable(ArticleService)); 

That's all! You should be able to see the Angular 1 component render with the data passed from the Angular 2 service.

See also

  • Connecting Angular 1 and Angular 2 with UpgradeModule shows you how to run Angular 1 and 2 frameworks together
  • Downgrading Angular 2 components to Angular 1 directives with downgradeComponent demonstrates how to use an Angular 2 component inside an Angular 1 application
Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • A first-rate reference guide with a clear structure and intuitive index that gives you as a developer exactly the information you want in exactly the way you want it
  • Covers no legacy material from the outdated Angular release candidates; it is up-to-date with the latest release of Angular 2.4
  • All the code in the book is explicitly written out, and every piece of code shown is a step towards building a simple working example

Description

Angular 2 introduces an entirely new way to build applications. It wholly embraces all the newest concepts that are built into the next generation of browsers, and it cuts away all the fat and bloat from Angular 1. This book plunges directly into the heart of all the most important Angular 2 concepts for you to conquer. In addition to covering all the Angular 2 fundamentals, such as components, forms, and services, it demonstrates how the framework embraces a range of new web technologies such as ES6 and TypeScript syntax, Promises, Observables, and Web Workers, among many others. This book covers all the most complicated Angular concepts and at the same time introduces the best practices with which to wield these powerful tools. It also covers in detail all the concepts you'll need to get you building applications faster. Oft-neglected topics such as testing and performance optimization are widely covered as well. A developer that reads through all the content in this book will have a broad and deep understanding of all the major topics in the Angular 2 universe.

Who is this book for?

This book is for developers who are competent with JavaScript and are looking to dive headfirst into the TypeScript edition of Angular 2. This book is also geared towards developers with experience in Angular 1 who are looking to make the transition.

What you will learn

  • Understand how to best move an Angular 1 application to Angular 2
  • Build a solid foundational understanding of the core elements of Angular 2 such as components, forms, and services
  • Gain an ability to wield complex topics such as Observables and Promises
  • Properly implement applications utilizing advanced topics such as dependency injection
  • Know how to maximize the performance of Angular 2 applications
  • Understand the best ways to take an Angular 2 application from TypeScript in a code editor to a fully function application served on your site
  • Get to know the best practices when organizing and testing a large Angular 2 application

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Jan 20, 2017
Length: 464 pages
Edition : 1st
Language : English
ISBN-13 : 9781785881923
Vendor :
Google
Languages :
Tools :

What do you get with a Packt Subscription?

Free for first 7 days. $19.99 p/m after that. Cancel any time!
Product feature icon Unlimited ad-free access to the largest independent learning library in tech. Access this title and thousands more!
Product feature icon 50+ new titles added per month, including many first-to-market concepts and exclusive early access to books as they are being written.
Product feature icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Product feature icon Thousands of reference materials covering every tech concept you need to stay up to date.
Subscribe now
View plans & pricing

Product Details

Publication date : Jan 20, 2017
Length: 464 pages
Edition : 1st
Language : English
ISBN-13 : 9781785881923
Vendor :
Google
Languages :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
$199.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just S$6 each
Feature tick icon Exclusive print discounts
$279.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just S$6 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total S$ 134.98
Angular Services
S$59.99
Angular 2 Cookbook
S$74.99
Total S$ 134.98 Stars icon
Banner background image

Table of Contents

10 Chapters
1. Strategies for Upgrading to Angular 2 Chevron down icon Chevron up icon
2. Conquering Components and Directives Chevron down icon Chevron up icon
3. Building Template-Driven and Reactive Forms Chevron down icon Chevron up icon
4. Mastering Promises Chevron down icon Chevron up icon
5. ReactiveX Observables Chevron down icon Chevron up icon
6. The Component Router Chevron down icon Chevron up icon
7. Services, Dependency Injection, and NgModule Chevron down icon Chevron up icon
8. Application Organization and Management Chevron down icon Chevron up icon
9. Angular 2 Testing Chevron down icon Chevron up icon
10. Performance and Advanced Concepts Chevron down icon Chevron up icon

Customer reviews

Top Reviews
Rating distribution
Full star icon Full star icon Full star icon Full star icon Half star icon 4.2
(24 Ratings)
5 star 45.8%
4 star 33.3%
3 star 16.7%
2 star 0%
1 star 4.2%
Filter icon Filter
Top Reviews

Filter reviews by




Kevin White Apr 24, 2017
Full star icon Full star icon Full star icon Full star icon Full star icon 5
I'm writing as a software engineer veteran, very comprehensive, excellent pointers on various Angular2 techniques. Professionals will want to seek additional information, such as best practices concerning authentication/authorisation (check out: OpenIddict), but this book covers a lot nonetheless. Novices will probably get lost in some of the technical detail, but perfect for intermediate/advanced and professionals. You should be familiar with basic OOP concepts, such as encapsulation, to get the most from this book. Being an Angular2 novice myself, I started with another excellent book "ASP.NET Core and Angular2". Great introduction but light on detail. The latter book does cross the server/client-side boundary though.
Amazon Verified review Amazon
Dhananjaya Oct 03, 2017
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Excellant book
Amazon Verified review Amazon
Michael Feb 15, 2017
Full star icon Full star icon Full star icon Full star icon Full star icon 5
To put it simply, this has become my main go-to Angular book. Due to my job, I am typically forced to be a glass-pane back learner, which means I am confronted with specific issues that require specific solutions, and a solution needs to be thoroughly understood and implemented very quickly. I usually don't have the option to start with the basics and move up. The "cookbook" format of this book is perfect for that approach. However, the way this book is structured, you can also read it front-to-back, and build concepts in the traditional fashion. It's the best of both worlds.If you've read many technical programming books, then you know that the author's style is usually prioritized at the bottom of the list. Not so with this book. Its simply a pleasure. The author is very clear, grammatically correct (rare in this genre), and simply fun to read. Add to that the interactivity the author implements via on-line code samples for hands-on learning, and you have the perfect tool that covers all the bases for all types of learners.And finally, and most important for Angular 2 books... IT'S CURRENT!!! How cool is that???I can't recommend it enough.
Amazon Verified review Amazon
Thomas Q Brady Apr 28, 2017
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Had to learn Angular (2/4) as quickly as possible for work, having dabbled in AngularJS (1.x).This book dives in with examples pretty quickly, so you might want to start with some more basic "getting started" kinds of content first, but you'll quickly come back to this book as your reference for how to actually get things done in Angular. The "getting started" type content introduces you to the vocabulary, syntax, etc., which you'll need to be able to understand the recipes in this book, but this book picks up at the "great, but how do I build anything other than this example to do list?" and remains useful for quite some time as a reference.
Amazon Verified review Amazon
Zebulah Fadade Jun 03, 2017
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Really nice, step-by-step explanation of how (and) why things are done in Angular 2. Answered a lot of my questions.Warning: If you purchased the book before mid-June 2017, you might have received a hoaxed version of the book (identified by lack of page numbers and chapter headers, and generally poor formatting). This problem has apparently been fixed now. You should be able to return the hoaxed book version to Amazon.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is included in a Packt subscription? Chevron down icon Chevron up icon

A subscription provides you with full access to view all Packt and licnesed content online, this includes exclusive access to Early Access titles. Depending on the tier chosen you can also earn credits and discounts to use for owning content

How can I cancel my subscription? Chevron down icon Chevron up icon

To cancel your subscription with us simply go to the account page - found in the top right of the page or at https://subscription.packtpub.com/my-account/subscription - From here you will see the ‘cancel subscription’ button in the grey box with your subscription information in.

What are credits? Chevron down icon Chevron up icon

Credits can be earned from reading 40 section of any title within the payment cycle - a month starting from the day of subscription payment. You also earn a Credit every month if you subscribe to our annual or 18 month plans. Credits can be used to buy books DRM free, the same way that you would pay for a book. Your credits can be found in the subscription homepage - subscription.packtpub.com - clicking on ‘the my’ library dropdown and selecting ‘credits’.

What happens if an Early Access Course is cancelled? Chevron down icon Chevron up icon

Projects are rarely cancelled, but sometimes it's unavoidable. If an Early Access course is cancelled or excessively delayed, you can exchange your purchase for another course. For further details, please contact us here.

Where can I send feedback about an Early Access title? Chevron down icon Chevron up icon

If you have any feedback about the product you're reading, or Early Access in general, then please fill out a contact form here and we'll make sure the feedback gets to the right team. 

Can I download the code files for Early Access titles? Chevron down icon Chevron up icon

We try to ensure that all books in Early Access have code available to use, download, and fork on GitHub. This helps us be more agile in the development of the book, and helps keep the often changing code base of new versions and new technologies as up to date as possible. Unfortunately, however, there will be rare cases when it is not possible for us to have downloadable code samples available until publication.

When we publish the book, the code files will also be available to download from the Packt website.

How accurate is the publication date? Chevron down icon Chevron up icon

The publication date is as accurate as we can be at any point in the project. Unfortunately, delays can happen. Often those delays are out of our control, such as changes to the technology code base or delays in the tech release. We do our best to give you an accurate estimate of the publication date at any given time, and as more chapters are delivered, the more accurate the delivery date will become.

How will I know when new chapters are ready? Chevron down icon Chevron up icon

We'll let you know every time there has been an update to a course that you've bought in Early Access. You'll get an email to let you know there has been a new chapter, or a change to a previous chapter. The new chapters are automatically added to your account, so you can also check back there any time you're ready and download or read them online.

I am a Packt subscriber, do I get Early Access? Chevron down icon Chevron up icon

Yes, all Early Access content is fully available through your subscription. You will need to have a paid for or active trial subscription in order to access all titles.

How is Early Access delivered? Chevron down icon Chevron up icon

Early Access is currently only available as a PDF or through our online reader. As we make changes or add new chapters, the files in your Packt account will be updated so you can download them again or view them online immediately.

How do I buy Early Access content? Chevron down icon Chevron up icon

Early Access is a way of us getting our content to you quicker, but the method of buying the Early Access course is still the same. Just find the course you want to buy, go through the check-out steps, and you’ll get a confirmation email from us with information and a link to the relevant Early Access courses.

What is Early Access? Chevron down icon Chevron up icon

Keeping up to date with the latest technology is difficult; new versions, new frameworks, new techniques. This feature gives you a head-start to our content, as it's being created. With Early Access you'll receive each chapter as it's written, and get regular updates throughout the product's development, as well as the final course as soon as it's ready.We created Early Access as a means of giving you the information you need, as soon as it's available. As we go through the process of developing a course, 99% of it can be ready but we can't publish until that last 1% falls in to place. Early Access helps to unlock the potential of our content early, to help you start your learning when you need it most. You not only get access to every chapter as it's delivered, edited, and updated, but you'll also get the finalized, DRM-free product to download in any format you want when it's published. As a member of Packt, you'll also be eligible for our exclusive offers, including a free course every day, and discounts on new and popular titles.