Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 93a857b

Browse files
committed
feat($compile): add one-way binding to the isolate scope defintion
1 parent 8bda5ec commit 93a857b

File tree

2 files changed

+419
-9
lines changed

2 files changed

+419
-9
lines changed

src/ng/compile.js

+47-3
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,28 @@
186186
* you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
187187
* `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
188188
*
189+
* * `<` or `<attr` - set up one-way (one-directional) binding between a local scope property and the
190+
* parent scope property of name defined via the value of the `attr` attribute. If no `attr`
191+
* name is specified then the attribute name is assumed to be the same as the local name.
192+
* Given `<dir my-attr="parentModel">` and directive definition of
193+
* `scope: { localModel:'<myAttr' }`, then isolate scope property `localModel` will reflect the
194+
* value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
195+
* in `localModel`, but changes in `localModel` will not reflect in `parentModel`. There are however
196+
* two caveats:
197+
* 1. one-way binding does not copy the value from the parent to the isolate scope, it simply
198+
* sets the same value. That means if your bound value is an object, changes to its properties
199+
* in the isolate scope will be reflected in the parent scope.
200+
* 2. one-way binding watches changes to the **identity** of the parent value. That is important should
201+
* you one-way bind an object, and then replace that object in the isolated scope. If you now change
202+
* a property of the object in your parent scope, the change will not be propagated to the isolated
203+
* scope, because the identity of the object has not changed. Instead you must assign a new object.
204+
*
205+
* One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings
206+
* back to the parent. However, it does not make this completely impossible.
207+
*
208+
* Same as with bi-directional bindings, you can also use shallow watch for changes (i.e. $watchCollection instead of $watch):
209+
* `<*` or `<*attr` (`<*?` or `<*?attr` if the property is optional).
210+
*
189211
* * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
190212
* If no `attr` name is specified then the attribute name is assumed to be the same as the
191213
* local name. Given `<widget my-attr="count = count + value">` and widget definition of
@@ -826,7 +848,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
826848
var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
827849

828850
function parseIsolateBindings(scope, directiveName, isController) {
829-
var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
851+
var LOCAL_REGEXP = /^\s*([@&]|[=<](\*?))(\??)\s*(\w*)\s*$/;
830852

831853
var bindings = {};
832854

@@ -2962,7 +2984,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
29622984
optional = definition.optional,
29632985
mode = definition.mode, // @, =, or &
29642986
lastValue,
2965-
parentGet, parentSet, compare;
2987+
parentGet, parentSet, compare, removeWatch;
29662988

29672989
switch (mode) {
29682990

@@ -3023,7 +3045,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
30233045
return lastValue = parentValue;
30243046
};
30253047
parentValueWatch.$stateful = true;
3026-
var removeWatch;
30273048
if (definition.collection) {
30283049
removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
30293050
} else {
@@ -3032,6 +3053,29 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
30323053
removeWatchCollection.push(removeWatch);
30333054
break;
30343055

3056+
case '<':
3057+
if (!hasOwnProperty.call(attrs, attrName)) {
3058+
if (optional) break;
3059+
attrs[attrName] = void 0;
3060+
}
3061+
if (optional && !attrs[attrName]) break;
3062+
3063+
parentGet = $parse(attrs[attrName]);
3064+
3065+
destination[scopeName] = parentGet(scope);
3066+
3067+
if (definition.collection) {
3068+
removeWatch = scope.$watchCollection(attrs[attrName], function onParentCollectionValueChange(newParentValue) {
3069+
destination[scopeName] = newParentValue;
3070+
});
3071+
} else {
3072+
removeWatch = scope.$watch(attrs[attrName], function onParentValueChange(newParentValue) {
3073+
destination[scopeName] = newParentValue;
3074+
});
3075+
}
3076+
removeWatchCollection.push(removeWatch);
3077+
break;
3078+
30353079
case '&':
30363080
// Don't assign Object.prototype method to scope
30373081
parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;

0 commit comments

Comments
 (0)