Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases now! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
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
$29.99 $43.99
Book Jan 2017 464 pages 1st Edition
eBook
$29.99 $43.99
Print
$54.99
Subscription
Free Trial
Renews at $19.99p/m
Arrow left icon
Profile Icon Patrick Gillespie Profile Icon Matthew Frisbie
Arrow right icon
$29.99 $43.99
Book Jan 2017 464 pages 1st Edition
eBook
$29.99 $43.99
Print
$54.99
Subscription
Free Trial
Renews at $19.99p/m
eBook
$29.99 $43.99
Print
$54.99
Subscription
Free Trial
Renews at $19.99p/m

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
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.

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 Edition
Language : English
ISBN-13 : 9781785881923
Vendor :
Google

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want

Product Details

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

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 $5 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 $5 each
Feature tick icon Exclusive print discounts

Frequently bought together

Stars icon
Total $ 29.99 43.99 14.00 saved
Angular 2 Cookbook
$29.99 $43.99
=
Book stack Total $ 29.99 43.99 14.00 saved Stars icon

Table of Contents

18 Chapters
Angular 2 Cookbook Chevron down icon Chevron up icon
Credits Chevron down icon Chevron up icon
About the Author Chevron down icon Chevron up icon
About the Reviewer Chevron down icon Chevron up icon
www.PacktPub.com Chevron down icon Chevron up icon
Customer Feedback Chevron down icon Chevron up icon
Dedication Chevron down icon Chevron up icon
Preface Chevron down icon Chevron up icon
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
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

How do I buy and download an eBook? Chevron down icon Chevron up icon

Where there is an eBook version of a title available, you can buy it from the book details for that title. Add either the standalone eBook or the eBook and print book bundle to your shopping cart. Your eBook will show in your cart as a product on its own. After completing checkout and payment in the normal way, you will receive your receipt on the screen containing a link to a personalised PDF download file. This link will remain active for 30 days. You can download backup copies of the file by logging in to your account at any time.

If you already have Adobe reader installed, then clicking on the link will download and open the PDF file directly. If you don't, then save the PDF file on your machine and download the Reader to view it.

Please Note: Packt eBooks are non-returnable and non-refundable.

Packt eBook and Licensing When you buy an eBook from Packt Publishing, completing your purchase means you accept the terms of our licence agreement. Please read the full text of the agreement. In it we have tried to balance the need for the ebook to be usable for you the reader with our needs to protect the rights of us as Publishers and of our authors. In summary, the agreement says:

  • You may make copies of your eBook for your own use onto any machine
  • You may not pass copies of the eBook on to anyone else
How can I make a purchase on your website? Chevron down icon Chevron up icon

If you want to purchase a video course, eBook or Bundle (Print+eBook) please follow below steps:

  1. Register on our website using your email address and the password.
  2. Search for the title by name or ISBN using the search option.
  3. Select the title you want to purchase.
  4. Choose the format you wish to purchase the title in; if you order the Print Book, you get a free eBook copy of the same title. 
  5. Proceed with the checkout process (payment to be made using Credit Card, Debit Cart, or PayPal)
Where can I access support around an eBook? Chevron down icon Chevron up icon
  • If you experience a problem with using or installing Adobe Reader, the contact Adobe directly.
  • To view the errata for the book, see www.packtpub.com/support and view the pages for the title you have.
  • To view your account details or to download a new copy of the book go to www.packtpub.com/account
  • To contact us directly if a problem is not resolved, use www.packtpub.com/contact-us
What eBook formats do Packt support? Chevron down icon Chevron up icon

Our eBooks are currently available in a variety of formats such as PDF and ePubs. In the future, this may well change with trends and development in technology, but please note that our PDFs are not Adobe eBook Reader format, which has greater restrictions on security.

You will need to use Adobe Reader v9 or later in order to read Packt's PDF eBooks.

What are the benefits of eBooks? Chevron down icon Chevron up icon
  • You can get the information you need immediately
  • You can easily take them with you on a laptop
  • You can download them an unlimited number of times
  • You can print them out
  • They are copy-paste enabled
  • They are searchable
  • There is no password protection
  • They are lower price than print
  • They save resources and space
What is an eBook? Chevron down icon Chevron up icon

Packt eBooks are a complete electronic version of the print edition, available in PDF and ePub formats. Every piece of content down to the page numbering is the same. Because we save the costs of printing and shipping the book to you, we are able to offer eBooks at a lower cost than print editions.

When you have purchased an eBook, simply login to your account and click on the link in Your Download Area. We recommend you saving the file to your hard drive before opening it.

For optimal viewing of our eBooks, we recommend you download and install the free Adobe Reader version 9.