Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save JimmyLv/91344af1e22b1d46b5eb to your computer and use it in GitHub Desktop.
Save JimmyLv/91344af1e22b1d46b5eb to your computer and use it in GitHub Desktop.
Migrating from 1.4 to 1.5: Introduction of New Features by Examples

Migrating from 1.4 to 1.5: Introduction of New Features by Examples

  • angular.component(): Introducing "components", a special sort of directive that are easy to configure and promote best practices, bring Angular 1 applications closer to Angular 2's style of architecture. Angular 1.5 takes a big step towards preparing developers for a smoother transition to Angular 2 in the future.
  • Multi-slot transclusion: Enabling the design of more powerful and complex UI elements with a much simpler configuration and reduced boilerplate.

angular.component()

There is nothing component() can do but directive() can not do.

<my-component first-name="'Alan'" last-name="'Rickman'"></my-component>

myModule.component('myComponent', {
  template: '<h1>Hello {{ $ctrl.getFullName() }}</h1>',
  bindings: { 
    firstName: '<', 
    lastName: '<' 
  },
  controller: function() {
    this.getFullName = function() {
      return this.firstName + ' ' + this.lastName;
    };
  }
});

myModule.directive('myComponent', {
  restrict: 'E',
  template: '<h1>Hello {{ ctrl.getFullName() }}</h1>',
  scope: {},
  bindToController: { 
    firstName: '<', 
    lastName: '<' 
  },
  controller: function() {
    this.getFullName = function() {
      return this.firstName + ' ' + this.lastName;
    };
  },
  controllerAs: 'ctrl'
});

Why bindToController when using controllerAs in directive? That is reason what if name should be two-way bound?

<div ng-controller="MainCtrl as main">
    <my-component 
        first-name="'Alan'" 
        last-name="'Rickman'" 
        name="main.name">
    </my-component>
</div>

myModule.controller('MainCtrl', function () {
  this.name = 'JimmyLv';
});

myModule.directive('myComponent', {
  restrict: 'E',
  template: '<h1>Hello {{ ctrl.getFullName() }} to {{ ctrl.name }}</h1>',
  scope: {
    firstName: '<', 
    lastName: '<',
    name: '='
  },
  controller: function($scope) {
    this.getFullName = function() {
      return this.firstName + ' ' + this.lastName;
    };
      this.name = 'Pascal';
      $scope.$watch('name', function (newValue) {
        this.name = newValue;
      }.bind(this));
  },
  controllerAs: 'ctrl'
});

scope

In AngularJS, a child scope normally prototypically inherits from its parent scope.

  • scope: { ... } -- this creates an "isolate" scope that does not prototypically inherit. (defalut in .component(), scope is always isolate)
  • scope:true -- then prototypical inheritance will be used for that directive. all properties will be inherited in directive
  • scope:false -- shared scope with parent scope. (default in .directive())

component tree

Ideally, the whole application should be a tree of components that implement clearly defined inputs and outputs, and minimize two-way data binding. That way, it's easier to predict when data changes and what the state of a component is.

  • Components only control their own View and Data: Components should never modify any data or DOM that is out of their own scope. Normally, in Angular it is possible to modify data anywhere in the application through scope inheritance and watches. This is practical, but can also lead to problems when it is not clear which part of the application is responsible for modifying the data. That is why component directives use an isolate scope, so a whole class of scope manipulation is not possible.

  • Components have a well-defined public API - Inputs and Outputs: However, scope isolation only goes so far, because Angular uses two-way binding. So if you pass an object to a component like this - bindings: {item: '='}, and modify one of its properties, the change will be reflected in the parent component. For components however, only the component that owns the data should modify it, to make it easy to reason about what data is changed, and when.

route

myMod.component('home', {
  template: '<h1>Home</h1><p>Hello, {{ $ctrl.user.name }} !</p>',
  controller: function() {
    this.user = {name: 'world'};
  }
});

$routeProvider.when('/', {
    template: '<home></home>'
  });

var myMod = angular.module('myMod', ['ngRoute']);
myMod.component('home', {
  template: '<h1>Home</h1><p>Hello, {{ $ctrl.user.name }} !</p>',
  bindings: {
    user: '<'
  }
});
myMod.config(function($routeProvider) {
  $routeProvider.when('/', {
    template: '<home user="$resolve.user"></home>',
    resolve: {
      user: function($http) { return $http.get('...'); }
    }
  });
});

Multiple Transclusion

myMod.directive('pane', function(){
    return {
      restrict: 'E',
      transclude: {
        'title': '?paneTitle',
        'body': 'paneBody',
        'footer': '?paneFooter'
      },
      templateUrl: 'pane.html'
    };
})
.controller('ExampleController', ['$scope', function($scope) {
  $scope.title = 'Lorem Ipsum';
  $scope.link = "https://google.com";
  $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
}]);

<div>
    <div ng-transclude="title">Fallback Title</div>
    <p ng-transclude="body"></p>
    <div ng-transclude="footer">Fallback Footer</div>
</div>

<div ng-controller="ExampleController as ctrl">
    <input ng-model="ctrl.title">
    <textarea ng-model="ctrl.text"></textarea>
    <pane>
        <pane-title>
            <a ng-href="{{ ctrl.link }}">{{ ctrl.title }}</a>
        </pane-title>
        <pane-body>{{ ctrl.text }}</pane-body>
    </pane>
</div>

using component directive, but lose template functionality (na yi tuo!)

myMod.component('pane', function(){
    return {
      templateUrl: 'pane.html'
      bindings: {
        'title': '<paneTitle',
        'body': '<paneBody',
        'footer': '<?paneFooter'
      }
    };
})

<div>
    <div>{{ $ctrl.title}}</div>
    <p>{{ $ctrl.body}}</p>
    <div>{{ $ctrl.footer}}</div>
</div>

<div ng-controller="ExampleController as ctrl">
  <input ng-model="ctrl.title">
  <textarea ng-model="ctrl.text"></textarea>
  <pane pane-title="ctrl.title"
        pane-body="ctrl.text">
  </pane>
</div>

Manual in action

Refactoring Angular Apps to Component Style

Understanding Components

Angular Releases Version 1.5 -- InfoQ

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment