diff options
Diffstat (limited to 'src/directives.js')
| -rw-r--r-- | src/directives.js | 234 |
1 files changed, 150 insertions, 84 deletions
diff --git a/src/directives.js b/src/directives.js index 26cbfe2c..747da3f5 100644 --- a/src/directives.js +++ b/src/directives.js @@ -1,121 +1,187 @@ - -angular.directive("auth", function(expression, element){ +angularDirective("ng-init", function(expression){ return function(){ - if(expression == "eager") { - this.$users.fetchCurrent(); - } + this.$eval(expression); }; }); - -//expression = "book=Book:{year=2000}" -angular.directive("entity", function(expression, element){ - //parse expression, ignore element - var entityName; // "Book"; - var instanceName; // "book"; - var defaults; // {year: 2000}; - - parse(expression); - +angularDirective("ng-eval", function(expression){ return function(){ - this[entityName] = this.$datastore.entity(entityName, defaults); - this[instanceName] = this[entityName](); - this.$watch("$anchor."+instanceName, function(newAnchor){ - this[instanceName] = this[entityName].get(this.$anchor[instanceName]); - }); + this.$addEval(expression); }; }); - -angular.directive("init", function(expression, element){ - return function(){ - this.$eval(expresssion); +angularDirective("ng-bind", function(expression){ + return function(element) { + this.$watch(expression, function(value){ + element.text(value); + }); }; }); - -//translation of {{ }} to ng-bind is external to this -angular.directive("bind", function(expression, element){ - return function() { - this.$watch(expression, function(value){ - element.innerText = value; +var bindTemplateCache = {}; +function compileBindTemplate(template){ + var fn = bindTemplateCache[template]; + if (!fn) { + var bindings = []; + foreach(parseBindings(template), function(text){ + var exp = binding(text); + bindings.push(exp ? function(){ + return this.$eval(exp); + } : function(){ + return text; + }); + }); + bindTemplateCache[template] = fn = function(){ + var parts = [], self = this; + foreach(bindings, function(fn){ + parts.push(fn.call(self)); + }); + return parts.join(''); + }; + } + return fn; +}; +angularDirective("ng-bind-template", function(expression){ + var templateFn = compileBindTemplate(expression); + return function(element) { + var lastValue; + this.$addEval(function() { + var value = templateFn.call(this); + if (value != lastValue) { + element.text(value); + lastValue = value; + } }); }; }); - -// translation of {{ }} to ng-bind-attr is external to this -// <a href="http://example.com?id={{book.$id}}" alt="{{book.$name}}">link</a> -// becomes -// <a href="" ng-bind-attr="{href:'http://example.com?id={{book.$id}}', alt:'{{book.$name}}'}">link</a> -angular.directive("bind-attr", function(expression, element){ - return function(expression, element){ - var jElement = jQuery(element); - this.$watch(expression, _(jElement.attr).bind(jElement)); +angularDirective("ng-bind-attr", function(expression){ + return function(element){ + this.$addEval(function(){ + foreach(this.$eval(expression), function(value, key){ + element.attr(key, compileBindTemplate(value).call(this)); + }, this); + }); }; }); -angular.directive("repeat", function(expression, element){ - var anchor = document.createComment(expression); - jQuery(element).replace(anchor); - var template = this.compile(element); - var lhs = "item"; - var rhs = "items"; +angularDirective("ng-non-bindable", function(){ + this.descend(false); +}); + +angularDirective("ng-repeat", function(expression, element){ + var reference = this.comment("ng-repeat: " + expression), + r = element.removeAttr('ng-repeat'), + template = this.compile(element), + match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/), + lhs, rhs, valueIdent, keyIdent; + if (! match) { + throw "Expected ng-repeat in form of 'item in collection' but got '" + + expression + "'."; + } + lhs = match[1]; + rhs = match[2]; + match = lhs.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/); + if (!match) { + throw "'item' in 'item in collection' should be identifier or (key, value) but got '" + + keyValue + "'."; + } + valueIdent = match[3] || match[1]; + keyIdent = match[2]; + + var parent = element.parent(); + element.replaceWith(reference); return function(){ - var children = []; - this.$eval(rhs, function(items){ - foreach(children, function(child){ - child.element.remove(); - }); - foreach(items, function(item){ - var child = template(item); // create scope - element.addChild(child.element, anchor); - children.push(child); + var children = [], + currentScope = this; + this.$addEval(rhs, function(items){ + var index = 0, childCount = children.length, childScope, lastElement = reference; + foreach(items || [], function(value, key){ + if (index < childCount) { + // reuse existing child + childScope = children[index]; + } else { + // grow children + childScope = template(element.clone(), currentScope); + childScope.init(); + childScope.scope.set('$index', index); + childScope.element.attr('ng-index', index); + lastElement.after(childScope.element); + children.push(childScope); + } + childScope.scope.set(valueIdent, value); + if (keyIdent) childScope.scope.set(keyIdent, key); + childScope.scope.updateView(); + lastElement = childScope.element; + index ++; }); + // shrink children + while(children.length > index) { + children.pop().element.remove(); + } }); }; -}); +}, {exclusive: true}); +angularDirective("ng-action", function(expression, element){ + return function(){ + var self = this; + element.click(function(){ + self.$eval(expression); + }); + }; +}); -//ng-non-bindable -angular.directive("non-bindable", function(expression, element){ - return false; +angularDirective("ng-watch", function(expression, element){ + var match = expression.match(/^([^.]*):(.*)$/); + if (!match) { + throw "Expecting watch expression 'ident_to_watch: watch_statement' got '" + + expression + "'"; + } + return function(){ + this.$watch(match[1], match[2]); + }; }); -//Styling -// -//ng-class -//ng-class-odd, ng-class-even -//ng-style -//ng-show, ng-hide +function ngClass(selector) { + return function(expression, element){ + var existing = element[0].className + ' '; + return function(element){ + this.$addEval(expression, function(value){ + if (selector(this.$index)) { + if (isArray(value)) value = value.join(' '); + element[0].className = (existing + value).replace(/\s\s+/g, ' '); + } + }); + }; + }; +} +angularDirective("ng-class", ngClass(function(){return true;})); +angularDirective("ng-class-odd", ngClass(function(i){return i % 2 == 1;})); +angularDirective("ng-class-even", ngClass(function(i){return i % 2 == 0;})); -angular.directive("action", function(expression, element){ - return function(){ - var self = this; - jQuery(element).click(function(){ - self.$eval(expression); +angularDirective("ng-show", function(expression, element){ + return function(element){ + this.$addEval(expression, function(value){ + element.css('display', toBoolean(value) ? '' : 'none'); }); }; }); -//ng-eval -angular.directive("eval", function(expression, element){ - return function(){ - this.$eval(expression); +angularDirective("ng-hide", function(expression, element){ + return function(element){ + this.$addEval(expression, function(value){ + element.css('display', toBoolean(value) ? 'none' : ''); + }); }; }); -//ng-watch -// <div ng-watch="$anchor.book: book=Book.get();"/> -angular.directive("watch", function(expression, element){ - var watches = { - 'lhs':'rhs' - }; // parse - return function(){ - this.$watch(watches); + +angularDirective("ng-style", function(expression, element){ + return function(element){ + this.$addEval(expression, function(value){ + element.css(value); + }); }; }); -//widget related -//ng-validate, ng-required, ng-formatter -//ng-error |
