Migrating from 1.4 to 1.5: Introduction of New Features by Examples
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'
});
In AngularJS, a child scope normally prototypically inherits from its parent scope.
.component(), scope is always isolate).directive())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.
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('...'); }
}
});
});
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>
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>