diff options
| author | Misko Hevery | 2013-03-20 16:24:23 -0700 | 
|---|---|---|
| committer | Misko Hevery | 2013-04-02 14:05:06 -0700 | 
| commit | 0b6f1ce5f89f47f9302ff1e8cd8f4b92f837c413 (patch) | |
| tree | 8cbc0c86024dd4f97d0aa54e0c9b7df9b0d56b86 /src/ng/directive | |
| parent | 4bfb66ce0be46d3a0e9da2f80f3e1d0c2b559828 (diff) | |
| download | angular.js-0b6f1ce5f89f47f9302ff1e8cd8f4b92f837c413.tar.bz2 | |
feat(ngAnimate): add support for animation
Diffstat (limited to 'src/ng/directive')
| -rw-r--r-- | src/ng/directive/ngInclude.js | 28 | ||||
| -rw-r--r-- | src/ng/directive/ngRepeat.js | 29 | ||||
| -rw-r--r-- | src/ng/directive/ngShowHide.js | 54 | ||||
| -rw-r--r-- | src/ng/directive/ngSwitch.js | 92 | ||||
| -rw-r--r-- | src/ng/directive/ngView.js | 20 | 
5 files changed, 154 insertions, 69 deletions
| diff --git a/src/ng/directive/ngInclude.js b/src/ng/directive/ngInclude.js index d4eacbe3..a385d00b 100644 --- a/src/ng/directive/ngInclude.js +++ b/src/ng/directive/ngInclude.js @@ -12,6 +12,13 @@   * (e.g. ngInclude won't work for cross-domain requests on all browsers and for   *  file:// access on some browsers).   * + * Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter** + * and **leave** effects. + * + * @animations + * enter - happens just after the ngInclude contents change and a new DOM element is created and injected into the ngInclude container + * leave - happens just after the ngInclude contents change and just before the former contents are removed from the DOM + *   * @scope   *   * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant, @@ -78,8 +85,8 @@   * @description   * Emitted every time the ngInclude content is reloaded.   */ -var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', -                  function($http,   $templateCache,   $anchorScroll,   $compile) { +var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', '$animator', +                  function($http,   $templateCache,   $anchorScroll,   $compile,   $animator) {    return {      restrict: 'ECA',      terminal: true, @@ -88,7 +95,8 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'            onloadExp = attr.onload || '',            autoScrollExp = attr.autoscroll; -      return function(scope, element) { +      return function(scope, element, attr) { +        var animate = $animator(scope, attr);          var changeCounter = 0,              childScope; @@ -97,8 +105,7 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'              childScope.$destroy();              childScope = null;            } - -          element.html(''); +          animate.leave(element.contents(), element);          };          scope.$watch(srcExp, function ngIncludeWatchAction(src) { @@ -110,9 +117,12 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'                if (childScope) childScope.$destroy();                childScope = scope.$new(); +              animate.leave(element.contents(), element); -              element.html(response); -              $compile(element.contents())(childScope); +              var contents = jqLite('<div/>').html(response).contents(); + +              animate.enter(contents, element); +              $compile(contents)(childScope);                if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {                  $anchorScroll(); @@ -123,7 +133,9 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'              }).error(function() {                if (thisChangeId === changeCounter) clearContent();              }); -          } else clearContent(); +          } else { +            clearContent(); +          }          });        };      } diff --git a/src/ng/directive/ngRepeat.js b/src/ng/directive/ngRepeat.js index a00bc9e4..fada0696 100644 --- a/src/ng/directive/ngRepeat.js +++ b/src/ng/directive/ngRepeat.js @@ -16,6 +16,13 @@   *   * `$middle` – `{boolean}` – true if the repeated element is between the first and last in the iterator.   *   * `$last` – `{boolean}` – true if the repeated element is last in the iterator.   * + * Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter**, + * **leave** and **move** effects. + * + * @animations + * enter - when a new item is added to the list or when an item is revealed after a filter + * leave - when an item is removed from the list or when an item is filtered out + * move - when an adjacent item is filtered out causing a reorder or when the item contents are reordered   *   * @element ANY   * @scope @@ -75,13 +82,15 @@        </doc:scenario>      </doc:example>   */ -var ngRepeatDirective = ['$parse', function($parse) { +var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) { +  var NG_REMOVED = '$$NG_REMOVED';    return {      transclude: 'element',      priority: 1000,      terminal: true,      compile: function(element, attr, linker) {        return function($scope, $element, $attr){ +        var animate = $animator($scope, $attr);          var expression = $attr.ngRepeat;          var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),            trackByExp, hashExpFn, trackByIdFn, lhs, rhs, valueIdentifier, keyIdentifier, @@ -130,6 +139,7 @@ var ngRepeatDirective = ['$parse', function($parse) {          $scope.$watchCollection(rhs, function ngRepeatAction(collection){            var index, length,                cursor = $element,     // current position of the node +              nextCursor,                // Same as lastBlockMap but it has the current state. It will become the                // lastBlockMap on the next iteration.                nextBlockMap = {}, @@ -184,7 +194,8 @@ var ngRepeatDirective = ['$parse', function($parse) {            for (key in lastBlockMap) {              if (lastBlockMap.hasOwnProperty(key)) {                block = lastBlockMap[key]; -              block.element.remove(); +              animate.leave(block.element); +              block.element[0][NG_REMOVED] = true;                block.scope.$destroy();              }            } @@ -200,12 +211,17 @@ var ngRepeatDirective = ['$parse', function($parse) {                // associated scope/element                childScope = block.scope; -              if (block.element == cursor) { +              nextCursor = cursor[0]; +              do { +                nextCursor = nextCursor.nextSibling; +              } while(nextCursor && nextCursor[NG_REMOVED]); + +              if (block.element[0] == nextCursor) {                  // do nothing                  cursor = block.element;                } else {                  // existing item which got moved -                cursor.after(block.element); +                animate.move(block.element, null, cursor);                  cursor = block.element;                }              } else { @@ -221,8 +237,8 @@ var ngRepeatDirective = ['$parse', function($parse) {              childScope.$middle = !(childScope.$first || childScope.$last);              if (!block.element) { -              linker(childScope, function(clone){ -                cursor.after(clone); +              linker(childScope, function(clone) { +                animate.enter(clone, null, cursor);                  cursor = clone;                  block.scope = childScope;                  block.element = clone; @@ -236,3 +252,4 @@ var ngRepeatDirective = ['$parse', function($parse) {      }    };  }]; + diff --git a/src/ng/directive/ngShowHide.js b/src/ng/directive/ngShowHide.js index 74195468..418a43ff 100644 --- a/src/ng/directive/ngShowHide.js +++ b/src/ng/directive/ngShowHide.js @@ -6,7 +6,18 @@   *   * @description   * The `ngShow` and `ngHide` directives show or hide a portion of the DOM tree (HTML) - * conditionally. + * conditionally based on **"truthy"** values evaluated within an {expression}. In other + * words, if the expression assigned to **ngShow evaluates to a true value** then **the element is set to visible** + * (via `display:block` in css) and **if false** then **the element is set to hidden** (so display:none). + * With ngHide this is the reverse whereas true values cause the element itself to become + * hidden. + * + * Additionally, you can also provide animations via the ngAnimate attribute to animate the **show** + * and **hide** effects. + * + * @animations + * show - happens after the ngShow expression evaluates to a truthy value and the contents are set to visible + * hide - happens before the ngShow expression evaluates to a non truthy value and just before the contents are set to hidden   *   * @element ANY   * @param {expression} ngShow If the {@link guide/expression expression} is truthy @@ -33,11 +44,14 @@     </doc:example>   */  //TODO(misko): refactor to remove element from the DOM -var ngShowDirective = ngDirective(function(scope, element, attr){ -  scope.$watch(attr.ngShow, function ngShowWatchAction(value){ -    element.css('display', toBoolean(value) ? '' : 'none'); -  }); -}); +var ngShowDirective = ['$animator', function($animator) { +  return function(scope, element, attr) { +    var animate = $animator(scope, attr); +    scope.$watch(attr.ngShow, function ngShowWatchAction(value){ +      animate[toBoolean(value) ? 'show' : 'hide'](element); +    }); +  }; +}];  /** @@ -45,8 +59,19 @@ var ngShowDirective = ngDirective(function(scope, element, attr){   * @name ng.directive:ngHide   *   * @description - * The `ngHide` and `ngShow` directives hide or show a portion of the DOM tree (HTML) - * conditionally. + * The `ngShow` and `ngHide` directives show or hide a portion of the DOM tree (HTML) + * conditionally based on **"truthy"** values evaluated within an {expression}. In other + * words, if the expression assigned to **ngShow evaluates to a true value** then **the element is set to visible** + * (via `display:block` in css) and **if false** then **the element is set to hidden** (so display:none). + * With ngHide this is the reverse whereas true values cause the element itself to become + * hidden. + * + * Additionally, you can also provide animations via the ngAnimate attribute to animate the **show** + * and **hide** effects. + * + * @animations + * show - happens after the ngHide expression evaluates to a non truthy value and the contents are set to visible + * hide - happens after the ngHide expression evaluates to a truthy value and just before the contents are set to hidden   *   * @element ANY   * @param {expression} ngHide If the {@link guide/expression expression} is truthy then @@ -73,8 +98,11 @@ var ngShowDirective = ngDirective(function(scope, element, attr){     </doc:example>   */  //TODO(misko): refactor to remove element from the DOM -var ngHideDirective = ngDirective(function(scope, element, attr){ -  scope.$watch(attr.ngHide, function ngHideWatchAction(value){ -    element.css('display', toBoolean(value) ? 'none' : ''); -  }); -}); +var ngHideDirective = ['$animator', function($animator) { +  return function(scope, element, attr) { +    var animate = $animator(scope, attr); +    scope.$watch(attr.ngHide, function ngHideWatchAction(value){ +      animate[toBoolean(value) ? 'hide' : 'show'](element); +    }); +  }; +}]; diff --git a/src/ng/directive/ngSwitch.js b/src/ng/directive/ngSwitch.js index f22e634d..8b0dab31 100644 --- a/src/ng/directive/ngSwitch.js +++ b/src/ng/directive/ngSwitch.js @@ -6,15 +6,30 @@   * @restrict EA   *   * @description - * Conditionally change the DOM structure. Elements within ngSwitch but without - * ngSwitchWhen or ngSwitchDefault directives will be preserved at the location - * as specified in the template + * The ngSwitch directive is used to conditionally swap DOM structure on your template based on a scope expression. + * Elements within ngSwitch but without ngSwitchWhen or ngSwitchDefault directives will be preserved at the location + * as specified in the template. + * + * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it + * from the template cache), ngSwitch simply choses one of the nested elements and makes it visible based on which element + * matches the value obtained from the evaluated expression. In other words, you define a container element + * (where you place the directive), place an expression on the **on="..." attribute** + * (or the **ng-switch="..." attribute**), define any inner elements inside of the directive and place + * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on + * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default + * attribute is displayed. + * + * Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter** + * and **leave** effects. + * + * @animations + * enter - happens after the ngSwtich contents change and the matched child element is placed inside the container + * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM   *   * @usage   * <ANY ng-switch="expression">   *   <ANY ng-switch-when="matchValue1">...</ANY>   *   <ANY ng-switch-when="matchValue2">...</ANY> - *   ...   *   <ANY ng-switch-default>...</ANY>   * </ANY>   * @@ -67,43 +82,48 @@        </doc:scenario>      </doc:example>   */ -var NG_SWITCH = 'ng-switch'; -var ngSwitchDirective = valueFn({ -  restrict: 'EA', -  require: 'ngSwitch', -  // asks for $scope to fool the BC controller module -  controller: ['$scope', function ngSwitchController() { -    this.cases = {}; -  }], -  link: function(scope, element, attr, ctrl) { -    var watchExpr = attr.ngSwitch || attr.on, -        selectedTranscludes, -        selectedElements, -        selectedScopes = []; +var ngSwitchDirective = ['$animator', function($animator) { +  return { +    restrict: 'EA', +    require: 'ngSwitch', -    scope.$watch(watchExpr, function ngSwitchWatchAction(value) { -      for (var i= 0, ii=selectedScopes.length; i<ii; i++) { -        selectedScopes[i].$destroy(); -        selectedElements[i].remove(); -      } +    // asks for $scope to fool the BC controller module +    controller: ['$scope', function ngSwitchController() { +     this.cases = {}; +    }], +    link: function(scope, element, attr, ngSwitchController) { +      var animate = $animator(scope, attr); +      var watchExpr = attr.ngSwitch || attr.on, +          selectedTranscludes, +          selectedElements, +          selectedScopes = []; -      selectedElements = []; -      selectedScopes = []; +      scope.$watch(watchExpr, function ngSwitchWatchAction(value) { +        for (var i= 0, ii=selectedScopes.length; i<ii; i++) { +          selectedScopes[i].$destroy(); +          animate.leave(selectedElements[i]); +        } -      if ((selectedTranscludes = ctrl.cases['!' + value] || ctrl.cases['?'])) { -        scope.$eval(attr.change); -        forEach(selectedTranscludes, function(selectedTransclude) { -          var selectedScope = scope.$new(); -          selectedScopes.push(selectedScope); -          selectedTransclude.transclude(selectedScope, function(caseElement) { -            selectedElements.push(caseElement); -            selectedTransclude.element.after(caseElement); +        selectedElements = []; +        selectedScopes = []; + +        if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) { +          scope.$eval(attr.change); +          forEach(selectedTranscludes, function(selectedTransclude) { +            var selectedScope = scope.$new(); +            selectedScopes.push(selectedScope); +            selectedTransclude.transclude(selectedScope, function(caseElement) { +              var anchor = selectedTransclude.element; + +              selectedElements.push(caseElement); +              animate.enter(caseElement, anchor.parent(), anchor); +            });            }); -        }); -      } -    }); +        } +      }); +    }    } -}); +}];  var ngSwitchWhenDirective = ngDirective({    transclude: 'element', diff --git a/src/ng/directive/ngView.js b/src/ng/directive/ngView.js index 6e92c2d8..2ffd64da 100644 --- a/src/ng/directive/ngView.js +++ b/src/ng/directive/ngView.js @@ -12,6 +12,13 @@   * Every time the current route changes, the included view changes with it according to the   * configuration of the `$route` service.   * + * Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter** + * and **leave** effects. + * + * @animations + * enter - happens just after the ngView contents are changed (when the new view DOM element is inserted into the DOM) + * leave - happens just after the current ngView contents change and just before the former contents are removed from the DOM + *   * @scope   * @example      <example module="ngView"> @@ -105,15 +112,16 @@   * Emitted every time the ngView content is reloaded.   */  var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$compile', -                       '$controller', +                       '$controller', '$animator',                 function($http,   $templateCache,   $route,   $anchorScroll,   $compile, -                        $controller) { +                        $controller,  $animator) {    return {      restrict: 'ECA',      terminal: true,      link: function(scope, element, attr) {        var lastScope, -          onloadExp = attr.onload || ''; +          onloadExp = attr.onload || '', +          animate = $animator(scope, attr);        scope.$on('$routeChangeSuccess', update);        update(); @@ -127,7 +135,7 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c        }        function clearContent() { -        element.html(''); +        animate.leave(element.contents(), element);          destroyLastScope();        } @@ -136,8 +144,8 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c              template = locals && locals.$template;          if (template) { -          element.html(template); -          destroyLastScope(); +          clearContent(); +          animate.enter(jqLite('<div></div>').html(template).contents(), element);            var link = $compile(element.contents()),                current = $route.current, | 
