Skip to content

Commit 87ac444

Browse files
committed
docs(guide/components): update to use one-way binding
1 parent 75f23f0 commit 87ac444

File tree

1 file changed

+32
-15
lines changed

1 file changed

+32
-15
lines changed

docs/content/guide/component.ngdoc

+32-15
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,20 @@ change will be reflected in the parent component. For components however, only t
109109
the data should modify it, to make it easy to reason about what data is changed, and when. For that reason,
110110
components should follow a few simple conventions:
111111

112-
- Inputs are realized with `@` and `=` bindings
112+
- Inputs should be using `<` and `@` bindings. The `<` symbol denotes {@link $compile#-scope- one-way bindings} which are
113+
available since 1.5. The difference to `=` is that the bound properties in the component scope are not watched, which means
114+
if you assign a new value to the property in the component scope, it will not update the parent scope. Note however, that both parent
115+
and component scope reference the same object, so if you are changing object properties or array elements in the
116+
component, the parent will still reflect that change.
117+
The general rule should therefore be to never change an object or array property in the component scope.
118+
`@` bindings can be used when the input is a string, especially when the value of the binding doesn't change.
113119
```js
114120
bindings: {
115-
hero: `=`,
121+
hero: `<`,
122+
comment: '@'
116123
}
117124
```
118-
- Outputs are realized with `&` bindings, which function as callbacks to component events
125+
- Outputs are realized with `&` bindings, which function as callbacks to component events.
119126
```js
120127
bindings: {
121128
onDelete: '&',
@@ -153,11 +160,11 @@ above:
153160
Instead of an ngController, we now have a heroList component that holds the data of
154161
different heroes, and creates a heroDetail for each of them.
155162

156-
The heroDetail component now contains the following new functionality:
157-
- a delete button that calls the bound onDelete function of the heroList component
163+
The heroDetail component now contains new functionality:
164+
- a delete button that calls the bound `onDelete` function of the heroList component
158165
- an input to change the hero location, in the form of a reusable editableField component. Instead
159-
of manipulating the hero object itself, it sends the changes upwards to the heroDetail, which sends
160-
it upwards to the heroList component, which updates it.
166+
of manipulating the hero object itself, it sends a changeset upwards to the heroDetail, which sends
167+
it upwards to the heroList component, which updates the original data.
161168

162169
<example name="heroComponentTree" module="heroApp">
163170
<file name="index.js">
@@ -212,7 +219,7 @@ it upwards to the heroList component, which updates it.
212219
templateUrl: 'heroDetail.html',
213220
controller: HeroDetailController,
214221
bindings: {
215-
hero: '=',
222+
hero: '<',
216223
onDelete: '&',
217224
onUpdate: '&'
218225
}
@@ -252,8 +259,8 @@ it upwards to the heroList component, which updates it.
252259
templateUrl: 'editableField.html',
253260
controller: EditableFieldController,
254261
bindings: {
255-
fieldValue: '@',
256-
fieldType: '@',
262+
fieldValue: '<',
263+
fieldType: '@?',
257264
onUpdate: '&'
258265
}
259266
});
@@ -269,7 +276,7 @@ it upwards to the heroList component, which updates it.
269276
<hr>
270277
<div>
271278
Name: {{$ctrl.hero.name}}<br>
272-
Location: <editable-field field-value="{{$ctrl.hero.location}}" field-type="text" on-update="$ctrl.update('location', value)"></editable-field><br>
279+
Location: <editable-field field-value="$ctrl.hero.location" field-type="text" on-update="$ctrl.update('location', value)"></editable-field><br>
273280
<button ng-click="$ctrl.onDelete({hero: $ctrl.hero})">Delete</button>
274281
</div>
275282
</file>
@@ -303,17 +310,25 @@ application, every view is a component:
303310
```
304311
<br />
305312
When using {@link ngRoute.$routeProvider $routeProvider}, you can often avoid some
306-
boilerplate, by assigning the resolved dependencies directly to the route scope:
313+
boilerplate, by passing the resolved route dependencies directly to the component. Since 1.5,
314+
ngRoute automatically assigns the resolves to the route scope property `$resolve` (you can also
315+
configure the property name via `resolveAs`). When using components, you can take advantage of this and pass resolves
316+
directly into your component without creating an extra route controller:
317+
307318
```js
308319
var myMod = angular.module('myMod', ['ngRoute']);
309320
myMod.component('home', {
310321
template: '<h1>Home</h1><p>Hello, {{ $ctrl.user.name }} !</p>',
311-
bindings: {user: '='}
322+
bindings: {
323+
user: '<'
324+
}
312325
});
313326
myMod.config(function($routeProvider) {
314327
$routeProvider.when('/', {
315328
template: '<home user="$resolve.user"></home>',
316-
resolve: {user: function($http) { return $http.get('...'); }}
329+
resolve: {
330+
user: function($http) { return $http.get('...'); }
331+
}
317332
});
318333
});
319334
```
@@ -349,7 +364,9 @@ angular.module('docsTabsExample', [])
349364
})
350365
.component('myPane', {
351366
transclude: true,
352-
require: {tabsCtrl: '^myTabs'},
367+
require: {
368+
tabsCtrl: '^myTabs'
369+
},
353370
bindings: {
354371
title: '@'
355372
},

0 commit comments

Comments
 (0)