Permalink
Browse files

Let angular use ObjectObserve for method observation

  • Loading branch information...
1 parent 98443d6 commit 5fb650998a240523170674c50df71b70109ca412 @mhevery committed Aug 27, 2012
Showing with 149 additions and 17 deletions.
  1. +7 −2 src/ng/directive/ngRepeat.js
  2. +58 −15 src/ng/rootScope.js
  3. +84 −0 test/ObjectObserve.html
@@ -88,9 +88,14 @@ var ngRepeatDirective = ngDirective({
// We need an array of these objects since the same object can be returned from the iterator.
// We expect this to be a rare case.
var lastOrder = new HashQueueMap();
- scope.$watch(function(scope){
+ // Change the way we watch arrays. Instead of iterating on each digest, we now iterate
+ // on change in array. We don't yet support watching array contents, because angular does
+ // not have such concept, but it should be straight forward to add. This means that the
+ // demo app needs to create new array instance, but can reuse array items.
+ scope.$watch(rhs, function(newValue, oldValue, scope){
var index, length,
- collection = scope.$eval(rhs),
+ // no need to compute the collection, since it is passed in
+ collection = newValue,
collectionLength = size(collection, true),
childScope,
// Same as lastOrder but it has the current state. It will become the
View
@@ -136,6 +136,15 @@ function $RootScopeProvider(){
this['this'] = this.$root = this;
this.$$asyncQueue = [];
this.$$listeners = {};
+
+ var scope = this;
+ // Global observer for objects
+ this.$$observer = new ChangeSummaryDispatcher();
+ this.$$observer.observePathValue(this.$$asyncQueue, 'length', function() {
+ // Whenever we add $evalAsync, then automatically trigger 4digest
+ // to flush it in the current micro-task.
+ scope.$digest();
+ });
}
/**
@@ -195,7 +204,6 @@ function $RootScopeProvider(){
child['this'] = child;
child.$$listeners = {};
child.$parent = this;
- child.$$asyncQueue = [];
child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null;
child.$$prevSibling = this.$$childTail;
if (this.$$childHead) {
@@ -293,22 +301,57 @@ function $RootScopeProvider(){
eq: !!objectEquality
};
- // in the case user pass string, we need to compile it, do we really need this ?
- if (!isFunction(listener)) {
- var listenFn = compileToFn(listener || noop, 'listener');
- watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);};
- }
+ if (typeof watchExp == 'string') {
+ // A watch can be a string in which case it is a path
+ scope.$$observer.observePathValue(scope, watchExp, function(newValue, oldValue) {
+ listener(newValue, oldValue, scope);
+ });
+ // This is a quick hack to prime the listener with initial value.
+ scope.$evalAsync(function() {
+ listener(scope.$eval(watchExp), undefined, scope);
+ });
+ } else if (watchExp.parts) {
+ // A watch can be interpolation in which case watch the idividual paths.
+ forEach(watchExp.parts, function(part) {
+ if (part.exp) {
+ scope.$$observer.observePathValue(scope, part.exp, function(newValue, oldValue) {
+ listener(watchExp(scope), 'TOO LAZY TO IMPLEMENT', scope);
+ });
+ // This is a quick hack to prime the listener with initial value.
+ scope.$evalAsync(function() {
+ listener(watchExp(scope), undefined, scope);
+ });
+ }
+ });
+ } else if (typeof watchExp.exp == 'string') {
+ // A watch can be pre-compiled expression, in which case use the uncompiled path.
+ scope.$$observer.observePathValue(scope, watchExp.exp, function(newValue, oldValue) {
+ listener(newValue, oldValue, scope);
+ });
+ // This is a quick hack to prime the listener with initial value.
+ scope.$evalAsync(function() {
+ listener(watchExp(scope), undefined, scope);
+ });
+ } else {
+ // We can't use ObjectObserve, so go do normal stuff
+
+ // in the case user pass string, we need to compile it, do we really need this ?
+ if (!isFunction(listener)) {
+ var listenFn = compileToFn(listener || noop, 'listener');
+ watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);};
+ }
- if (!array) {
- array = scope.$$watchers = [];
- }
- // we use unshift since we use a while loop in $digest for speed.
- // the while loop reads in reverse order.
- array.unshift(watcher);
+ if (!array) {
+ array = scope.$$watchers = [];
+ }
+ // we use unshift since we use a while loop in $digest for speed.
+ // the while loop reads in reverse order.
+ array.unshift(watcher);
- return function() {
- arrayRemove(array, watcher);
- };
+ return function() {
+ arrayRemove(array, watcher);
+ };
+ }
},
/**
@@ -0,0 +1,84 @@
+<!doctype html>
+<html ng-app>
+<head>
+ <script src="../src/change_summary.js"></script>
+ <script src="../src/dispatcher.js"></script>
+ <script src="../src/angular-bootstrap.js"></script>
+ <script>
+ function Filter($scope) {
+ $scope.versions = [
+ { version: '1.0.0', name: 'temporal-domination', date: '2012-06-13' },
+ { version: '1.0.0rc12', name: 'regression-extermination', date: '2012-06-12' },
+ { version: '1.0.0rc11', name: 'promise-resolution', date: '2012-06-10' },
+ { version: '1.0.0rc10', name: 'tesseract-giftwrapping', date: '2012-05-23' },
+ { version: '1.0.0rc9', name: 'eggplant-teleportation', date: '2012-05-14' },
+ { version: '1.0.0rc8', name: 'blooming-touch', date: '2012-05-06' },
+ { version: '1.0.0rc7', name: 'rc-generation', date: '2012-04-30' },
+ { version: 'v1.0.0rc6', name: 'runny-nose', date: '2012-04-20' },
+ { version: '1.0.0rc5', name: 'reality-distortion', date: '2012-04-12' },
+ { version: '1.0.0rc4', name: 'insomnia-induction', date: '2012-04-05' },
+ { version: '1.0.0rc3', name: 'barefoot-telepathy', date: '2012-03-29' },
+ { version: '1.0.0rc2', name: 'silence-absorption', date: '2012-03-20' },
+ { version: '1.0.0rc1', name: 'moiré-vision', date: '2012-03-13' },
+ { version: '0.10.6', name: 'bubblewrap-cape', date: '2012-01-17' },
+ { version: '0.10.5', name: 'steel-fist', date: '2011-11-08' },
+ { version: '0.10.4', name: 'human-torch', date: '2011-10-22' },
+ { version: '0.10.3', name: 'shattering-heartbeat', date: '2011-10-13' },
+ { version: '0.10.2', name: 'sneaky-seagull', date: '2011-10-08' },
+ { version: '0.10.1', name: 'inexorable-juggernaut', date: '2011-09-09' },
+ { version: '0.10.0', name: 'chicken-hands', date: '2011-09-02' },
+ { version: '0.9.19', name: 'canine-psychokinesis', date: '2011-08-20' },
+ { version: '0.9.18', name: 'jiggling-armfat', date: '2011-07-29' },
+ { version: '0.9.17', name: 'vegetable-reanimation', date: '2011-06-30' },
+ { version: '0.9.16', name: 'weather-control', date: '2011-06-07' },
+ { version: '0.9.15', name: 'lethal-stutter', date: '2011-04-11' },
+ { version: '0.9.14', name: 'key-maker', date: '2011-04-01' },
+ { version: '0.9.13', name: 'curdling-stare', date: '2011-03-13' },
+ { version: '0.9.12', name: 'thought-implanter', date: '2011-03-03' },
+ { version: '0.9.11', name: 'snow-maker ', date: '2011-02-08' },
+ { version: '0.9.10', name: 'flea-whisperer ', date: '2011-01-26' },
+ { version: '0.9.9', name: 'time-shift', date: '2011-01-13' },
+ { version: '0.9.8', name: 'astral-projection', date: '2010-12-23' },
+ { version: '0.9.7', name: 'sonic-scream', date: '2010-12-10' },
+ { version: '0.9.6', name: 'night-vision', date: '2010-12-06' },
+ { version: '0.9.5', name: 'turkey-blast', date: '2010-11-25' },
+ { version: '0.9.4', name: 'total-recall', date: '2010-11-18' },
+ { version: '0.9.3', name: 'cold-resistance', date: '2010-11-10' },
+ { version: '0.9.2', name: 'faunal-mimicry', date: '2010-11-03' },
+ { version: '0.9.1', name: 'repulsion-field', date: '2010-10-26' },
+ { version: '0.9.0', name: 'dragon-breath', date: '2010-10-20' }
+ ];
+
+ $scope.$watch('predicate', function(predicate) {
+ $scope.filteredVersions = $scope.versions.filter(function(version) {
+ return !predicate ||
+ version.version.indexOf(predicate) != -1 ||
+ version.name.indexOf(predicate) != -1 ||
+ version.date.indexOf(predicate) != -1;
+ });
+ });
+
+ $scope.filteredVersions = $scope.versions;
+
+ }
+ </script>
+</head>
+<body ng-controller="Filter">
+ <label>Name:</label>
+ <input type="text" ng-model="predicate" placeholder="Enter a name here">
+ <hr>
+ <h1>predicate: {{predicate}}!</h1>
+ <table>
+ <tr>
+ <td>Date</td>
+ <td>Version</td>
+ <td>Code Name</td>
+ </tr>
+ <tr ng-repeat="release in filteredVersions">
+ <td>{{release.date}}</td>
+ <td>{{release.version}}</td>
+ <td>{{release.name}}</td>
+ </tr>
+ </table>
+</body>
+</html>

0 comments on commit 5fb6509

Please sign in to comment.