diff --git a/rxDecorateDirective.js b/rxDecorateDirective.js | |
index 56912ca..1ecb8b5 100644 | |
--- a/rxDecorateDirective.js | |
+++ b/rxDecorateDirective.js | |
@@ -1,4 +1,7 @@ | |
+'use strict'; | |
+ | |
function rxDecorateDirective($provide, directiveName) { | |
+ // Duck-typing function lifted from the rx.js source. | |
function isObservable(obj) { | |
return obj && typeof obj.subscribe === 'function'; | |
} | |
@@ -16,76 +19,65 @@ function rxDecorateDirective($provide, directiveName) { | |
return function postLink(scope, iElement, iAttrs) { | |
var originalScope = scope; | |
- var originalLinkArgs = arguments; | |
- | |
- // Make a new child scope that we'll pass along to the original directive link | |
- // function instead of the actual scope. If we receive an Observable to subscribe | |
- // to, we'll be able to update the child scope the original directive is watching | |
- // without modifying the original scope. If we receive anything else, we simply let | |
- // prototypal inheritance propagate the change from the original scope to the child | |
- // scope. | |
- var childScope = originalScope.$new(false); | |
+ var linkArgs = arguments; | |
- // Replace `originalScope` with `childScope` in the link function args, used when | |
- // calling the original link function with `apply()` below. | |
- originalLinkArgs[0] = childScope; | |
- | |
- // Name of the scope property containing the thing we need to watch. | |
- var propertyName = iAttrs[directiveName]; | |
+ // Expression in the directive's attribute value we need to watch. | |
+ var attrExpression = iAttrs[directiveName]; | |
// If the current scope property value is null or undefined, watch for the first | |
- // value that isn't. Otherwise, go ahead and use the current value. | |
- if (isUndefinedOrNull(originalScope[propertyName])) { | |
- originalScope.$toObservable(propertyName) | |
- .pluck('newValue') | |
- .skipWhile(isUndefinedOrNull) | |
- .first() | |
- .subscribe(onFirstUsefulValue); | |
- } else { | |
- onFirstUsefulValue(); | |
- } | |
+ // value that isn't, in case it's an Observable. | |
+ originalScope.$toObservable(attrExpression) | |
+ .pluck('newValue') | |
+ .skipWhile(function isUndefinedOrNull(value) { | |
+ return typeof value === 'undefined' || value === null; | |
+ }) | |
+ .first() | |
+ .subscribe(function onFirstUsefulValue(firstValue) { | |
+ // If we're bound to an Observable, subscribe to it. | |
+ if (isObservable(firstValue)) { | |
+ // The property name we want the original directive linking function to | |
+ // set a watcher on. | |
+ var RX_VALUE_SCOPE_PROPERTY = '$$rxValue'; | |
+ | |
+ // Get a shared version of the value stream we're bound to, since we'll | |
+ // be making multiple subscriptions. | |
+ var valueStream = firstValue.share(); | |
- // A stream of the new values that the watcher might produce if we need to use it. | |
- //var watchedValueStream = originalScope.$toObservable(propertyName) | |
- // .pluck('newValue'); | |
+ // Create a new isolate scope to pass to the original linking function. | |
+ var isolateScope = originalScope.$new(true); | |
- function isUndefinedOrNull(value) { | |
- return typeof value === 'undefined' || value === null; | |
- } | |
+ // Replace `originalScope` with `isolateScope` in the link function | |
+ // args, used when calling the original link function with `apply()` | |
+ // below. | |
+ linkArgs[0] = isolateScope; | |
- function onFirstUsefulValue() { | |
- // If we're bound to an Observable, subscribe to it. | |
- if (isObservable(originalScope[propertyName])) { | |
- // Get a shared version of the value stream we're bound to, since we'll | |
- // be making multiple subscriptions. | |
- var valueStream = originalScope[propertyName].share(); | |
+ // Replace the original directive name attribute value with the | |
+ // property name we want the original directive to watch instead. | |
+ iAttrs.$set(directiveName, RX_VALUE_SCOPE_PROPERTY); | |
- // Invoke the original linking function once, the first time we | |
- // get a value from the Observable. | |
- valueStream.first().subscribe(function() { | |
- originalLinkFn.apply(directiveConfig, originalLinkArgs); | |
- }); | |
+ // Invoke the original linking function with the modified link args we | |
+ // prepared earlier. | |
+ originalLinkFn.apply(directiveConfig, linkArgs); | |
- // Subscribe to the Observable, updating the child scope property the | |
- // original linking function code is watching whenever we receive a new | |
- // value. | |
- var valueStreamDisposable = valueStream | |
- .safeApply(childScope, function onNextValue(value) { | |
- childScope[propertyName] = value; | |
- }) | |
- .subscribe(); | |
+ // Subscribe to the Observable, updating the child scope property the | |
+ // original linking function code is watching whenever we receive a new | |
+ // value. | |
+ var valueStreamDisposable = valueStream | |
+ .safeApply(isolateScope, function onNextValue(value) { | |
+ isolateScope[RX_VALUE_SCOPE_PROPERTY] = value; | |
+ }) | |
+ .subscribe(); | |
- // Our subscription should not live longer than the scope. | |
- originalScope.$on('$destroy', function() { | |
- valueStreamDisposable.dispose(); | |
- }); | |
- } else { | |
- // Else the directive is not bound to an Observable, so the child scope will | |
- // prototypally inherit the changes to the parent and the original directive's | |
- // watcher should work normally. | |
- originalLinkFn.apply(directiveConfig, originalLinkArgs); | |
- } | |
- } | |
+ // Our subscription should not live longer than the scope. | |
+ originalScope.$on('$destroy', function() { | |
+ valueStreamDisposable.dispose(); | |
+ }); | |
+ } else { | |
+ // Else the directive is not bound to an Observable, so we call the | |
+ // original linking function with its original args. | |
+ originalLinkFn.apply(directiveConfig, linkArgs); | |
+ } | |
+ }); | |
}; | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment