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

Commit bd1f619

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

File tree

2 files changed

+419
-9
lines changed

2 files changed

+419
-9
lines changed

src/ng/compile.js

+46-3
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,27 @@
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 replace a bound object in the isolated scope. In that case, you must also replace your
202+
* object in ther parent scope with a new one, otherwise the one-way binding won't pick up changes.
203+
*
204+
* One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings
205+
* back to the parent. It does not however not make this completely impossible.
206+
*
207+
* Same as with bi-directional bindings, you can also use shallow watch for changes (i.e. $watchCollection instead of $watch):
208+
* `<*` or `<*attr` (`<*?` or `<*?attr` if the property is optional).
209+
*
189210
* * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
190211
* If no `attr` name is specified then the attribute name is assumed to be the same as the
191212
* local name. Given `<widget my-attr="count = count + value">` and widget definition of
@@ -826,7 +847,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
826847
var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
827848

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

831852
var bindings = {};
832853

@@ -2962,7 +2983,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
29622983
optional = definition.optional,
29632984
mode = definition.mode, // @, =, or &
29642985
lastValue,
2965-
parentGet, parentSet, compare;
2986+
parentGet, parentSet, compare, removeWatch;
29662987

29672988
switch (mode) {
29682989

@@ -3023,7 +3044,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
30233044
return lastValue = parentValue;
30243045
};
30253046
parentValueWatch.$stateful = true;
3026-
var removeWatch;
30273047
if (definition.collection) {
30283048
removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
30293049
} else {
@@ -3032,6 +3052,29 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
30323052
removeWatchCollection.push(removeWatch);
30333053
break;
30343054

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

0 commit comments

Comments
 (0)