Книга: Angular 2 Cookbook
Назад: Componentizing directives using controllerAs encapsulation
Дальше: Implementing a basic component in AngularJS 1.5

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 .

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
Назад: Componentizing directives using controllerAs encapsulation
Дальше: Implementing a basic component in AngularJS 1.5

thank you
Flame
cant read the code since it is all on a single line. Also this comments section is russian
Rakuneque
DATA COLLECTION AND ANALYSIS Two reviewers extracted data and assessed methodological quality independently lasix torsemide conversion Many others were in that space already