diff options
| author | Misko Hevery | 2011-03-23 09:33:29 -0700 | 
|---|---|---|
| committer | Vojta Jina | 2011-08-02 01:00:03 +0200 | 
| commit | 8f0dcbab804180828d6859b1340c86cf161209fb (patch) | |
| tree | d13d47d47a1889cb7c96a87cecacd2e25307d51c /src/directives.js | |
| parent | 1f4b417184ce53af15474de065400f8a686430c5 (diff) | |
| download | angular.js-8f0dcbab804180828d6859b1340c86cf161209fb.tar.bz2 | |
feat(scope): new and improved scope implementation
- Speed improvements (about 4x on flush phase)
- Memory improvements (uses no function closures)
- Break $eval into $apply, $dispatch, $flush
- Introduced $watch and $observe
Breaks angular.equals() use === instead of ==
Breaks angular.scope() does not take parent as first argument
Breaks scope.$watch() takes scope as first argument
Breaks scope.$set(), scope.$get are removed
Breaks scope.$config is removed
Breaks $route.onChange callback has not "this" bounded
Diffstat (limited to 'src/directives.js')
| -rw-r--r-- | src/directives.js | 158 | 
1 files changed, 91 insertions, 67 deletions
diff --git a/src/directives.js b/src/directives.js index 9aa0d57e..4712f250 100644 --- a/src/directives.js +++ b/src/directives.js @@ -73,7 +73,7 @@   */  angularDirective("ng:init", function(expression){    return function(element){ -    this.$tryEval(expression, element); +    this.$eval(expression);    };  }); @@ -165,19 +165,19 @@ angularDirective("ng:init", function(expression){     </doc:example>   */  angularDirective("ng:controller", function(expression){ -  this.scope(true); -  return function(element){ -    var controller = getter(window, expression, true) || getter(this, expression, true); -    if (!controller) -      throw "Can not find '"+expression+"' controller."; -    if (!isFunction(controller)) -      throw "Reference '"+expression+"' is not a class."; -    this.$become(controller); -  }; +  this.scope(function(scope){ +    var Controller = +      getter(scope, expression, true) || +      getter(window, expression, true); +    assertArgFn(Controller, expression); +    return Controller; +  }); +  return noop;  });  /**   * @workInProgress + * @deprecated   * @ngdoc directive   * @name angular.directive.ng:eval   * @@ -208,17 +208,18 @@ angularDirective("ng:controller", function(expression){       <doc:scenario>         it('should check eval', function(){           expect(binding('obj.divide')).toBe('3'); -         expect(binding('obj.updateCount')).toBe('2'); +         expect(binding('obj.updateCount')).toBe('1');           input('obj.a').enter('12');           expect(binding('obj.divide')).toBe('6'); -         expect(binding('obj.updateCount')).toBe('3'); +         expect(binding('obj.updateCount')).toBe('2');         });       </doc:scenario>     </doc:example>   */ +// TODO(misko): remove me  angularDirective("ng:eval", function(expression){    return function(element){ -    this.$onEval(expression, element); +    this.$observe(expression);    };  }); @@ -257,15 +258,26 @@ angularDirective("ng:bind", function(expression, element){    element.addClass('ng-binding');    return function(element) {      var lastValue = noop, lastError = noop; -    this.$onEval(function() { +    this.$observe(function(scope) { +      // TODO(misko): remove error handling https://github.com/angular/angular.js/issues/347        var error, value, html, isHtml, isDomElement, -          oldElement = this.hasOwnProperty($$element) ? this.$element : undefined; -      this.$element = element; -      value = this.$tryEval(expression, function(e){ +          hadOwnElement = scope.hasOwnProperty('$element'), +          oldElement = scope.$element; +      // TODO(misko): get rid of $element https://github.com/angular/angular.js/issues/348 +      scope.$element = element; +      try { +        value = scope.$eval(expression); +      } catch (e) { +        scope.$service('$exceptionHandler')(e);          error = formatError(e); -      }); -      this.$element = oldElement; -      // If we are HTML then save the raw HTML data so that we don't +      } finally { +        if (hadOwnElement) { +          scope.$element = oldElement; +        } else { +          delete scope.$element; +        } +      } +      // If we are HTML than save the raw HTML data so that we don't        // recompute sanitization since it is expensive.        // TODO: turn this into a more generic way to compute this        if (isHtml = (value instanceof HTML)) @@ -289,7 +301,7 @@ angularDirective("ng:bind", function(expression, element){            element.text(value == undefined ? '' : value);          }        } -    }, element); +    });    };  }); @@ -301,10 +313,14 @@ function compileBindTemplate(template){      forEach(parseBindings(template), function(text){        var exp = binding(text);        bindings.push(exp -        ? function(element){ -            var error, value = this.$tryEval(exp, function(e){ +        ? function(scope, element) { +            var error, value; +            try { +              value = scope.$eval(exp); +            } catch(e) { +              scope.$service('$exceptionHandler')(e);                error = toJson(e); -            }); +            }              elementError(element, NG_EXCEPTION, error);              return error ? error : value;            } @@ -312,20 +328,30 @@ function compileBindTemplate(template){              return text;            });      }); -    bindTemplateCache[template] = fn = function(element, prettyPrintJson){ -      var parts = [], self = this, -         oldElement = this.hasOwnProperty($$element) ? self.$element : undefined; -      self.$element = element; -      for ( var i = 0; i < bindings.length; i++) { -        var value = bindings[i].call(self, element); -        if (isElement(value)) -          value = ''; -        else if (isObject(value)) -          value = toJson(value, prettyPrintJson); -        parts.push(value); +    bindTemplateCache[template] = fn = function(scope, element, prettyPrintJson) { +      var parts = [], +          hadOwnElement = scope.hasOwnProperty('$element'), +          oldElement = scope.$element; + +      // TODO(misko): get rid of $element +      scope.$element = element; +      try { +        for (var i = 0; i < bindings.length; i++) { +          var value = bindings[i](scope, element); +          if (isElement(value)) +            value = ''; +          else if (isObject(value)) +            value = toJson(value, prettyPrintJson); +          parts.push(value); +        } +        return parts.join(''); +      } finally { +        if (hadOwnElement) { +          scope.$element = oldElement; +        } else { +          delete scope.$element; +        }        } -      self.$element = oldElement; -      return parts.join('');      };    }    return fn; @@ -372,13 +398,13 @@ angularDirective("ng:bind-template", function(expression, element){    var templateFn = compileBindTemplate(expression);    return function(element) {      var lastValue; -    this.$onEval(function() { -      var value = templateFn.call(this, element, true); +    this.$observe(function(scope) { +      var value = templateFn(scope, element, true);        if (value != lastValue) {          element.text(value);          lastValue = value;        } -    }, element); +    });    };  }); @@ -446,10 +472,10 @@ var REMOVE_ATTRIBUTES = {  angularDirective("ng:bind-attr", function(expression){    return function(element){      var lastValue = {}; -    this.$onEval(function(){ -      var values = this.$eval(expression); +    this.$observe(function(scope){ +      var values = scope.$eval(expression);        for(var key in values) { -        var value = compileBindTemplate(values[key]).call(this, element), +        var value = compileBindTemplate(values[key])(scope, element),              specialName = REMOVE_ATTRIBUTES[lowercase(key)];          if (lastValue[key] !== value) {            lastValue[key] = value; @@ -467,7 +493,7 @@ angularDirective("ng:bind-attr", function(expression){            }          }        } -    }, element); +    });    };  }); @@ -510,14 +536,13 @@ angularDirective("ng:bind-attr", function(expression){   * TODO: maybe we should consider allowing users to control event propagation in the future.   */  angularDirective("ng:click", function(expression, element){ -  return annotate('$updateView', function($updateView, element){ +  return function(element){      var self = this;      element.bind('click', function(event){ -      self.$tryEval(expression, element); -      $updateView(); +      self.$apply(expression);        event.stopPropagation();      }); -  }); +  };  }); @@ -555,28 +580,27 @@ angularDirective("ng:click", function(expression, element){     </doc:example>   */  angularDirective("ng:submit", function(expression, element) { -  return annotate('$updateView', function($updateView, element) { +  return function(element) {      var self = this;      element.bind('submit', function(event) { -      self.$tryEval(expression, element); -      $updateView(); +      self.$apply(expression);        event.preventDefault();      }); -  }); +  };  });  function ngClass(selector) { -  return function(expression, element){ +  return function(expression, element) {      var existing = element[0].className + ' '; -    return function(element){ -      this.$onEval(function(){ -        if (selector(this.$index)) { -          var value = this.$eval(expression); +    return function(element) { +      this.$observe(function(scope) { +        if (selector(scope.$index)) { +          var value = scope.$eval(expression);            if (isArray(value)) value = value.join(' ');            element[0].className = trim(existing + value);          } -      }, element); +      });      };    };  } @@ -732,9 +756,9 @@ angularDirective("ng:class-even", ngClass(function(i){return i % 2 === 1;}));   */  angularDirective("ng:show", function(expression, element){    return function(element){ -    this.$onEval(function(){ -      toBoolean(this.$eval(expression)) ? element.show() : element.hide(); -    }, element); +    this.$observe(expression, function(scope, value){ +      toBoolean(value) ? element.show() : element.hide(); +    });    };  }); @@ -773,9 +797,9 @@ angularDirective("ng:show", function(expression, element){   */  angularDirective("ng:hide", function(expression, element){    return function(element){ -    this.$onEval(function(){ -      toBoolean(this.$eval(expression)) ? element.hide() : element.show(); -    }, element); +    this.$observe(expression, function(scope, value){ +      toBoolean(value) ? element.hide() : element.show(); +    });    };  }); @@ -815,8 +839,8 @@ angularDirective("ng:hide", function(expression, element){  angularDirective("ng:style", function(expression, element){    return function(element){      var resetStyle = getStyle(element); -    this.$onEval(function(){ -      var style = this.$eval(expression) || {}, key, mergedStyle = {}; +    this.$observe(function(scope){ +      var style = scope.$eval(expression) || {}, key, mergedStyle = {};        for(key in style) {          if (resetStyle[key] === undefined) resetStyle[key] = '';          mergedStyle[key] = style[key]; @@ -825,7 +849,7 @@ angularDirective("ng:style", function(expression, element){          mergedStyle[key] = mergedStyle[key] || resetStyle[key];        }        element.css(mergedStyle); -    }, element); +    });    };  });  | 
