J n J
Specs
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:
Component based architecture
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