diff options
| author | Misko Hevery | 2010-03-18 14:43:49 -0700 |
|---|---|---|
| committer | Misko Hevery | 2010-03-18 14:43:49 -0700 |
| commit | df607da0d1b9726bce6584238fe3ad7e9b65a966 (patch) | |
| tree | 472a81182226bde4c3541592f940983c9486eea2 | |
| parent | 7634a3ed5227f8bc2a2ba83752d0e2c78adb6051 (diff) | |
| download | angular.js-df607da0d1b9726bce6584238fe3ad7e9b65a966.tar.bz2 | |
support for templates
| -rw-r--r-- | lib/jstestdriver/JsTestDriver.jar | bin | 3090800 -> 3092033 bytes | |||
| -rw-r--r-- | src/Scope.js | 15 | ||||
| -rw-r--r-- | src/directives.js | 8 | ||||
| -rw-r--r-- | test/CompilerSpec.js | 95 |
4 files changed, 82 insertions, 36 deletions
diff --git a/lib/jstestdriver/JsTestDriver.jar b/lib/jstestdriver/JsTestDriver.jar Binary files differindex 1a37d230..2c7a5154 100644 --- a/lib/jstestdriver/JsTestDriver.jar +++ b/lib/jstestdriver/JsTestDriver.jar diff --git a/src/Scope.js b/src/Scope.js index 442477b4..3633f960 100644 --- a/src/Scope.js +++ b/src/Scope.js @@ -1,5 +1,6 @@ function Scope(initialState, name) { this.widgets = []; + this.evals = []; this.watchListeners = {}; this.name = name; initialState = initialState || {}; @@ -11,6 +12,7 @@ function Scope(initialState, name) { this.state['$root'] = this.state; } this.set('$watch', bind(this, this.addWatchListener)); + this.set('$eval', bind(this, this.addEval)); }; Scope.expressionCache = {}; @@ -48,17 +50,23 @@ Scope.prototype = { updateView: function() { var self = this; this.fireWatchers(); - _.each(this.widgets, function(widget){ + foreach(this.widgets, function(widget){ self.evalWidget(widget, "", {}, function(){ this.updateView(self); }); }); + foreach(this.evals, bind(this, this.apply)); }, addWidget: function(controller) { if (controller) this.widgets.push(controller); }, + addEval: function(fn, listener) { + // todo: this should take a function/string and a listener + this.evals.push(fn); + }, + isProperty: function(exp) { for ( var i = 0; i < exp.length; i++) { var ch = exp.charAt(i); @@ -190,8 +198,7 @@ Scope.prototype = { }, fireWatchers: function() { - var self = this; - var fired = false; + var self = this, fired = false; foreach(this.watchListeners, function(watcher) { var value = self.eval(watcher.expression); if (value !== watcher.lastValue) { @@ -206,6 +213,6 @@ Scope.prototype = { }, apply: function(fn) { - fn.apply(this.state, slice(arguments, 0, arguments.length)); + fn.apply(this.state, slice.call(arguments, 1, arguments.length)); } }; diff --git a/src/directives.js b/src/directives.js index 7c5cc257..26cbfe2c 100644 --- a/src/directives.js +++ b/src/directives.js @@ -58,12 +58,12 @@ angular.directive("bind-attr", function(expression, element){ angular.directive("repeat", function(expression, element){ var anchor = document.createComment(expression); jQuery(element).replace(anchor); - var template = this.templetize(element); + var template = this.compile(element); var lhs = "item"; var rhs = "items"; - var children = []; return function(){ - this.$watch(rhs, function(items){ + var children = []; + this.$eval(rhs, function(items){ foreach(children, function(child){ child.element.remove(); }); @@ -102,7 +102,7 @@ angular.directive("action", function(expression, element){ //ng-eval angular.directive("eval", function(expression, element){ return function(){ - this.$onUpdate( expression); + this.$eval(expression); }; }); //ng-watch diff --git a/test/CompilerSpec.js b/test/CompilerSpec.js index 35e0e605..4ffdf7f5 100644 --- a/test/CompilerSpec.js +++ b/test/CompilerSpec.js @@ -1,3 +1,9 @@ +/** + * Template provides directions an how to bind to a given element. + * It contains a list of init functions which need to be called to + * bind to a new instance of elements. It also provides a list + * of child paths which contain child templates + */ function Template() { this.paths = []; this.children = []; @@ -26,6 +32,11 @@ Template.prototype = { } }, + setExclusiveInit: function(init) { + this.inits = [init]; + this.addInit = noop; + }, + addChild: function(index, template) { this.paths.push(index); @@ -33,39 +44,22 @@ Template.prototype = { } }; +/////////////////////////////////// +// Compiler +////////////////////////////////// + function Compiler(directives){ this.directives = directives; } DIRECTIVE = /^ng-(.*)$/; -/** - * return { - * element: - * init: function(element){...} - * } - * - * internal data structure: { - * paths: [4, 5, 6], - * directive: name, - * init: function(expression, element){} - * } - * - * template : { - * inits: [fn(), fn()} - * paths: [1, 5], - * templates: [ - * inits: [] - * paths: [] - * templates: - * ] - * } - */ Compiler.prototype = { compile: function(element) { - var template = this.templetize(element); - return function(){ + var template = this.templetize(element) || new Template(); + return function(element){ var scope = new Scope(); + scope.element = element; return { scope: scope, element:element, @@ -79,17 +73,24 @@ Compiler.prototype = { childTemplate, recurse = true; // Process attributes/directives - for (i = 0, items = element.attributes, length = items.length; + for (i = 0, items = element.attributes || [], length = items.length; i < length; i++) { item = items[i]; var match = item.name.match(DIRECTIVE); if (match) { directive = this.directives[match[1]]; if (directive) { - init = directive.call({}, item.value, element); + init = directive.call(this, item.value, element); template = template || new Template(); - template.addInit(init); + if (directive.exclusive) { + template.setExclusiveInit(init); + i = length; // quit iterations + } else { + template.addInit(init); + } recurse = recurse && init; + } else { + error("Directive '" + match[0] + "' is not recognized."); } } } @@ -136,7 +137,7 @@ describe('compiler', function(){ }; compiler = new Compiler(directives); compile = function(html){ - var e = element(html); + var e = element("<div>" + html + "</div>"); var view = compiler.compile(e)(e); view.init(); return view.scope; @@ -183,4 +184,42 @@ describe('compiler', function(){ var scope = compile('<span ng-hello="misko" ng-stop="true"><span ng-hello="adam"/></span>'); expect(log).toEqual("hello misko"); }); + + it('should allow creation of templates', function(){ + directives.duplicate = function(expr, element){ + var template, + marker = document.createComment("marker"), + parentNode = element.parentNode; + parentNode.insertBefore(marker, element); + parentNode.removeChild(element); + element.removeAttribute("ng-duplicate"); + template = this.compile(element); + return function(marker) { + var parentNode = marker.parentNode; + this.$eval(function() { + parentNode.insertBefore( + template(element.cloneNode(true)).element, + marker.nextSibling); + }); + }; + }; + var scope = compile('before<span ng-duplicate="expr">x</span>after'); + expect($(scope.element).html()).toEqual('before<!--marker-->after'); + scope.updateView(); + expect($(scope.element).html()).toEqual('before<!--marker--><span>x</span>after'); + scope.updateView(); + expect($(scope.element).html()).toEqual('before<!--marker--><span>x</span><span>x</span>after'); + }); + + it('should allow for exculsive tags which suppress others', function(){ + directives.exclusive = function(){ + return function() { + log += ('exclusive'); + }; + }; + directives.exclusive.exclusive = true; + + compile('<span ng-hello="misko", ng-exclusive/>'); + expect(log).toEqual('exclusive'); + }); }); |
