Moving to Components – Replacing ng-controller with a Component module.component('mainComponent', { template: ` Items `, controller: MainComponentController }) – Items



Moving to Components – Replacing ng-controller with a Component module.component('mainComponent', { template: ` Items `, controller: MainComponentController }) – Items

0 0


moving-to-components

A short presentation about moving to Angular 1.X component syntax

On Github yanivefraim / moving-to-components

Moving to Components

Yaniv Efraim

Wix.com

Stop Using

  • NG-CONTROLLER
  • NG-INCLUDE
  • **Directives

** directives can be used for DOM manipulations

Why not ng-controller?

  • Scope Inheritance - shared mutable state
  • Poor view organization - several controllers for same html
  • Preparing for Angular 2.X

What should we use instead?

Replace:

<div ng-controller="myController"></div>
					

With:

<my-component></my-component>
					

What should we use instead?

//Instead:
<div ng-include="my-template.html"></div>

//Use:
<my-component></my-component>

module.component('myComponent', {
  templateUrl: 'my-template.html';
  ...
})
					

What should we use instead?

Instead of Directives, use:

angular.module('myModule')
.directive('myComponent', () => {
  return {
    template: `<div>{{myControllerAs.data}}</div>`
    controller: MyController,
    controllerAs: 'myControllerAs'
    bindToController: {
      input: '=',
      output: '&'
    },
    scope: {},
    link: (scope, element, attrs, ngModelController) => {}
  }
});
angular.module('myModule')
.component('myComponent', {
  template: `<div>{{$ctrl.data}}</div>`
  controller: MyController,
  bindings: {
    input: '=',
    output: '&'
  }
});
					

Map Routes to Components

angular.module('myModule')
  .config(($routeProvider) => {
    $routeProvider
      .when('/my-app', {
        template: `<my-app-component saved-games="$resolve.savedGames">
                  </my-app-component>`,
        resolve: {
          savedGames: function (gameServerApi: GameServerApi) {
            return gameServerApi.getSavedGames();
          }
        }
      })
					

Advantages of Angular 1.5 Component syntax

  • Simpler configuration than plain directives
  • Promote sane defaults and best practices
  • Optimized for component-based architecture
  • Writing component directives will make it easier to upgrade to Angular 2

Component Architecture

What Is A Component

An atomic piece of UI that is composable and reusable.

What do we want from our component?

  • Well-defined public API - inputs and outputs
  • Immutable - does not change data which it doesn't own
  • Isolated - no side effects / external references

Unidirectional data-flow

Let's Take This Piece Of HTML

<div ng-controller="MainCtrl as mainCtrl">
  <h1>Items</h1>
  <ul>
    <li ng-repeat="item in mainCtrl.items">
      {{item.text}}
      <button ng-click="mainCtrl.deleteItem(item)">
        Delete
      </button>
    </li>
  </ul>
</div>

And Its Controller

function MainController() {
  var ctrl = this;
  ctrl.items = [{title: 'title 1', text: 'item 1'}, {...}];
  ctrl.deleteItem = function(item) {
    var idx = ctrl.items.indexOf(item);
    if (idx >= 0) {
      ctrl.items.splice(idx, 1);
    }
  };
}

And Create an 'item-list' Component

<div ng-controller="MainCtrl as mainCtrl">
  <h1>Items</h1>
  <!-- <ul>
    <li ng-repeat="item in mainCtrl.items">
      { {item.text}}
      <button ng-click="mainCtrl.deleteItem(item)">
        Delete
      </button>
    </li>
  </ul> -->
  <item-list items="mainCtrl.items"></item-list>
</div>

item-list Component

module.component('itemList', {
    controller: function() {
      var ctrl = this;
      ctrl.deleteItem = function(item) {
        var idx = ctrl.items.indexOf(item);
        if (idx >= 0) {
          ctrl.items.splice(idx, 1);
        }
      };
    },
    bindings: {
      items: '='
    },
    templateUrl: 'item-list.html'
  };
);

Cool! But Wait - What Is Wrong Here?

(our component is mutating data it doesn't own!)

ctrl.deleteItem = function(item) {
  var idx = ctrl.items.indexOf(item);
  if (idx >= 0) {
    ctrl.items.splice(idx, 1);
  }
};

Adding An Output Event

module.component('itemList', {
    scope: {},
    controller: function() {
      $ctrl.deleteItem = function(item) {
        $ctrl.onDelete({item: item});
      };
    },
    controllerAs: 'itemListCtrl',
    bindings: {
      items: '=',
      onDelete: '&'
    },
    templateUrl: 'item-list.html'
  };
);

Registering An Output Event

<div ng-controller="MainCtrl as mainCtrl">
  <h1>Items</h1>
  <item-list items="mainCtrl.items" on-delete="mainCtrl.onDelete(item)"></item-list>
</div>

We Now Have A Well Defined Component

module.component('itemList', {
    scope: {},
    controller: function() {
      $ctrl.deleteItem = function(item) {
        $ctrl.onDelete({item: item});
      };
    },
    controllerAs: 'itemListCtrl',
    bindings: {
      items: '=', //input
      onDelete: '&' //output
    },
    templateUrl: 'item-list.html'
  };
);
<-- Logic
<-- Interface
<-- View

Replacing ng-controller with a Component

module.component('mainComponent', {
  template: `
  <div>
    <h1>Items</h1>
    <item-list items="$ctrl.dataService.items" on-delete="$ctrl.onDelete(item)"></item-list>
  </div>
`,
  controller: MainComponentController
})

Recap - item-list Component

  • Our component now has a well defined API
  • No shared mutable state
  • No side effects / external references

Thank You

Moving to Components Yaniv Efraim Wix.com