[( Web Components )]

 

J n J

Specs

  • Custom elements
  • Shadow dom
  • Templates
  • Html imports

 

Libraries: POLYMER, X-TAG, BOSONIC, SKATEJS

Example

<template id="template">
  <style>
    ...
  </style>
  <div id="container">
    ...
  </div>  
</template>

<script>
  // element registration
  var MyComponent = document.registerElement('my-component', {
    prototype: Object.create(HTMLElement.prototype, {
      createdCallback: {
        value: function() {
          var root = this.createShadowRoot();
          var template = document.querySelector('#template');
          var clone = document.importNode(template.content, true);
          root.appendChild(clone);
        }
      }
    })
  });
</script>
<head>
  ...
  <link rel="import" href="my-component.html">
</head>
<body>
  <my-component> ... </my-component>
  ...
</body>

Component  definition

my-component.html

Component  usage

index.html

Example v2

<dom-module id="element-name">
  <template>
    <style>
      /* CSS rules for your element */
    </style>

    <!-- local DOM for your element -->

    <div>{{greeting}}</div> <!-- data bindings in local DOM -->
  </template>

  <script>
    // element registration
    Polymer({
      is: "element-name",

      // add properties and methods on the element's prototype
      properties: {
        // declare properties for the element's public API
        greeting: {
          type: String,
          value: "Hello!"
        }
      }
    });
  </script>
</dom-module>

Example v3

import {ViewEncapsulation} from '@angular/core';

@Component({
  selector: 'zippy',
  templateUrl: 'zippy.html',
  styles: [`
    .zippy {
      background: green;
    }
  `],
  encapsulation: ViewEncapsulation.None
})
class Zippy {
  @Input() title: string;
}

<div class="zippy">
  <div (click)="toggle()" class="zippy__title">  
  </div>
  <div [hidden]="!visible" class="zippy__content">
    <ng-content></ng-content>
  </div>
</div>

Angular 2


Encapsulations:

  • Native
  • Emulated (default)
  • None       

 

Component based architecture

  • Components only control their own View and Data
  • Components have a well-defined public API - Inputs and Outputs
  • Components have a well-defined lifecycle
  • An application is a tree of components

 

Components in Angular 1.5

.component('counter', {
  bindings: {
    count: '='
  },
  templateUrl: 'counter.html',
  controller: function () {
    function increment() {
      this.count++;
    }
    function decrement() {
      this.count--;
    }
    this.increment = increment;
    this.decrement = decrement;
  }
});

Angular 1.5 vs 1.4

// Angular 1.5
.component('counter', {
  bindings: {
    count: '='
  },
  templateUrl: 'counter.html',
  controller: function () {
    function increment() {
      this.count++;
    }
    function decrement() {
      this.count--;
    }
    this.increment = increment;
    this.decrement = decrement;
  }
});
// Angular 1.4
.directive('counter', function counter() {
  return {
    scope: {},
    bindToController: {
      count: '='
    },
    templateUrl: 'counter.html',
    controller: function () {
      function increment() {
        this.count++;
      }
      function decrement() {
        this.count--;
      }
      this.increment = increment;
      this.decrement = decrement;
    },
    controllerAs: 'counter'
  };
});

Components @ Zemanta front-end

oneApp.directive('zemGridCellBaseField', [function () {
    return {
        restrict: 'E',
        scope: {},
        controllerAs: 'ctrl',
        bindToController: {
            data: '=',
            column: '=',
            row: '=',
            grid: '=',
        },
        templateUrl: '/components/zem-grid/templates/zem_grid_cell_base_field.html',
        controller: [function ($scope, zemGridDataFormatter, zemGridUIService) {
            var vm = this;

            $scope.$watch('ctrl.row', update);
            $scope.$watch('ctrl.data', update);

            function update () {
                var value = vm.data ? vm.data.value : undefined;
                vm.parsedValue = zemGridDataFormatter.formatValue(value, vm.column.data);

                vm.goalStatusClass = '';
                if (vm.data) {
                    vm.goalStatusClass = zemGridUIService.getGoalStatusClass(vm.data.goalStatus);
                }
            }
        }],
    };
}]);

"How?" & "Why?"

...
controllerAs: 'ctrl',
bindToController: {
    foo: '=',
},
...

Use controllerAs and bindToController (DON'T bind anything to $scope) ...

... easier to test controllers without injecting $scope,

... we only inject Angular's services when we need some "special" functionality from Angular ($watch, $on, $broadcast),

... closer to native JavaScript since controller is represented with a JavaScript "class",

... easier to upgrade Angular since $scope is not used as a view model for templates anymore.

// Use properties
// bound to controller
// in templates like
// this:
{{ctrl.foo}}

"How?" & "Why?"

...
scope: {},
...

Isolated directives' scope ...

... to only allow explicit passing of data through our app and prevent children from messing with parents' data and functionality.

"How?" & "Why?"

...
controller: [function (zemGridDataFormatter, zemGridUIService) {
    var vm = this;

    vm.parsedValue = zemGridDataFormatter.formatValue(value, vm.column.data);
    vm.goalStatusClass = zemGridUIService.getGoalStatusClass(vm.data.goalStatus);
}],
...

 Move business logic into services ...

... much easier to test!

"How?" & "Why?"

grid
├── zemGrid.component.html
├── zemGrid.component.js
├── zemGrid.component.spec.js
└── zemGrid.less

Related component files (directive & controller, template, tests etc.) are grouped together and this makes them easy to locate.

Directory structure (current)

one/

  • js/

    • controllers/

    • directives/

    • filters/

    • services/

    • app.js

    • config.js

    • constants.js

  • less/

    • . . . (style.less)

  • partials/

    • . . .  (xy.html)

test/

 

Directory structure (components)

components/

  • zem-grid/

    • services/

    • templates/

    • styles/

    • filters/

    • zem_grid.js

    • zem_grid_header.js

    • zem_grid_body.js

    • . . .

  • zem-grid-actions/

  • zem-chart/

  • zem-infobox/

Directory structure (best-practice - sort of)

app/

  • core/

    • config.js

    • constants.js

    • core.module.js

    • core.routes.js

  • dashboard/

    • all_accounts/

      • allAccounts.routes.js

    • dashboard.module.js

    • dashboard.routes.js

  • layout/

    • (wireframe)

  • widgets/

    • common/

    • zem-grid/

      • components/

        • grid-footer/

          • etc. (js,html,specs)

      • services/   (internal)

      • shared/     (external api)

      • zemGrid.component.html

      • zemGrid.component.js

      • zemGrid.component.spec.js

      • zemGrid.less

      • zemGrid.module.js

Components in Angular

By George Byte

Components in Angular

  • 945