diff options
| -rw-r--r-- | angular-debug.js | 53 | ||||
| -rw-r--r-- | src/services.js | 15 | ||||
| -rw-r--r-- | src/widgets.js | 2 | ||||
| -rw-r--r-- | test/servicesSpec.js | 13 | ||||
| -rw-r--r-- | test/widgetsSpec.js | 5 |
5 files changed, 80 insertions, 8 deletions
diff --git a/angular-debug.js b/angular-debug.js index d80db4df..c96947a3 100644 --- a/angular-debug.js +++ b/angular-debug.js @@ -33,6 +33,7 @@ var consoleNode, PRIORITY_FIRST = -99999, PRIORITY_WATCH = -1000, PRIORITY_LAST = 99999, + PRIORITY = {'FIRST': PRIORITY_FIRST, 'LAST': PRIORITY_LAST, 'WATCH':PRIORITY_WATCH}, NOOP = 'noop', NG_EXCEPTION = 'ng-exception', NG_VALIDATION_ERROR = 'ng-validation-error', @@ -501,17 +502,34 @@ function toJsonArray(buf, obj, pretty, stack){ * bind to a new instance of elements. It also provides a list * of child paths which contain child templates */ -function Template() { +function Template(priority) { this.paths = []; this.children = []; this.inits = []; + this.priority = priority || 0; } Template.prototype = { init: function(element, scope) { + var inits = {}; + this.collectInits(element, inits); + foreachSorted(inits, function(queue){ + foreach(queue, function(fn){ + fn(scope); + }); + }); + }, + + collectInits: function(element, inits) { + var queue = inits[this.priority]; + if (!queue) { + inits[this.priority] = queue = []; + } element = jqLite(element); foreach(this.inits, function(fn) { - scope.$tryEval(fn, element, element); + queue.push(function(scope) { + scope.$tryEval(fn, element, element); + }); }); var i, @@ -520,7 +538,7 @@ Template.prototype = { paths = this.paths, length = paths.length; for (i = 0; i < length; i++) { - children[i].init(childNodes[paths[i]], scope); + children[i].collectInits(childNodes[paths[i]], inits); } }, @@ -575,13 +593,13 @@ Compiler.prototype = { }; }, - templatize: function(element){ + templatize: function(element, priority){ var self = this, widget, directiveFns = self.directives, descend = true, directives = true, - template = new Template(), + template, selfApi = { compile: bind(self, self.compile), comment:function(text) {return jqLite(document.createComment(text));}, @@ -590,7 +608,11 @@ Compiler.prototype = { descend: function(value){ if(isDefined(value)) descend = value; return descend;}, directives: function(value){ if(isDefined(value)) directives = value; return directives;} }; - + priority = element.attr('ng-eval-order') || priority || 0; + if (isString(priority)) { + priority = PRIORITY[uppercase(priority)] || 0; + } + template = new Template(priority); eachAttribute(element, function(value, name){ if (!widget) { if (widget = self.widgets['@' + name]) { @@ -632,7 +654,7 @@ Compiler.prototype = { // Process non text child nodes if (descend) { eachNode(element, function(child, i){ - template.addChild(i, self.templatize(child)); + template.addChild(i, self.templatize(child, priority)); }); } return template.empty() ? null : template; @@ -3339,6 +3361,8 @@ angularWidget('NG:SWITCH', function ngSwitch(element){ element.append(switchCase.element); childScope.$tryEval(switchCase.change, element); switchCase.template(switchCase.element, childScope); + if (scope.$invalidWidgets) + scope.$invalidWidgets.clearOrphans(); childScope.$init(); } }); @@ -3491,6 +3515,21 @@ angularService("$invalidWidgets", function(){ }); return count; }; + invalidWidgets.clearOrphans = function() { + for(var i = 0; i < invalidWidgets.length;) { + var widget = invalidWidgets[i]; + if (isOrphan(widget[0])) { + invalidWidgets.splice(i, 1); + } else { + i++; + } + } + }; + function isOrphan(widget) { + if (widget == window.document) return false; + var parent = widget.parentNode; + return !parent || isOrphan(parent); + } return invalidWidgets; }); var browserSingleton; diff --git a/src/services.js b/src/services.js index e2132f1c..e91a8eca 100644 --- a/src/services.js +++ b/src/services.js @@ -115,5 +115,20 @@ angularService("$invalidWidgets", function(){ }); return count; }; + invalidWidgets.clearOrphans = function() { + for(var i = 0; i < invalidWidgets.length;) { + var widget = invalidWidgets[i]; + if (isOrphan(widget[0])) { + invalidWidgets.splice(i, 1); + } else { + i++; + } + } + }; + function isOrphan(widget) { + if (widget == window.document) return false; + var parent = widget.parentNode; + return !parent || isOrphan(parent); + } return invalidWidgets; }); diff --git a/src/widgets.js b/src/widgets.js index f5f02813..b281ac2e 100644 --- a/src/widgets.js +++ b/src/widgets.js @@ -225,6 +225,8 @@ angularWidget('NG:SWITCH', function ngSwitch(element){ element.append(switchCase.element); childScope.$tryEval(switchCase.change, element); switchCase.template(switchCase.element, childScope); + if (scope.$invalidWidgets) + scope.$invalidWidgets.clearOrphans(); childScope.$init(); } }); diff --git a/test/servicesSpec.js b/test/servicesSpec.js index 91cc1f0e..618d9a15 100644 --- a/test/servicesSpec.js +++ b/test/servicesSpec.js @@ -80,11 +80,22 @@ describe("service $invalidWidgets", function(){ }); it("should count number of invalid widgets", function(){ - var scope = compile('<input name="price" ng-required></input>').$init(); + var scope = compile('<input name="price" ng-required ng-validate="number"></input>').$init(); expect(scope.$invalidWidgets.length).toEqual(1); scope.price = 123; scope.$eval(); expect(scope.$invalidWidgets.length).toEqual(0); scope.$element.remove(); + scope.price = 'abc'; + scope.$eval(); + expect(scope.$invalidWidgets.length).toEqual(1); + + jqLite(document.body).append(scope.$element); + scope.$invalidWidgets.clearOrphans(); + expect(scope.$invalidWidgets.length).toEqual(1); + + jqLite(document.body).html(''); + scope.$invalidWidgets.clearOrphans(); + expect(scope.$invalidWidgets.length).toEqual(0); }); }); diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js index c6158c37..c64f03ca 100644 --- a/test/widgetsSpec.js +++ b/test/widgetsSpec.js @@ -221,10 +221,15 @@ describe('ng:switch', function(){ it('should call init on switch', function(){ var scope = compile('<ng:switch on="url" change="name=\'works\'"><div ng-switch-when="a">{{name}}</div></ng:include>'); + var cleared = false; scope.url = 'a'; + scope.$invalidWidgets = {clearOrphans: function(){ + cleared = true; + }}; scope.$init(); expect(scope.name).toEqual(undefined); expect(scope.$element.text()).toEqual('works'); + expect(cleared).toEqual(true); }); }); |
