Skip to content

Commit

Permalink
Let angular use ObjectObserve for method observation
Browse files Browse the repository at this point in the history
  • Loading branch information
mhevery committed Aug 29, 2012
1 parent 98443d6 commit 5fb6509
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 17 deletions.
9 changes: 7 additions & 2 deletions src/ng/directive/ngRepeat.js
Expand Up @@ -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
Expand Down
73 changes: 58 additions & 15 deletions src/ng/rootScope.js
Expand Up @@ -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();
});
}

/**
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
};
}
},

/**
Expand Down
84 changes: 84 additions & 0 deletions test/ObjectObserve.html
@@ -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.