Angular不在ng-view上更新ng-class

时间:2017-09-22 12:57:43

标签: javascript angularjs ng-animate ng-view

我正在使用角度1.6.5进行角度应用,并且遇到了一种非常奇怪的行为。

我想要实现的是:当更改ngroute时,我必须从当前视图中删除活动类,等待完成离开动画,然后将活动类添加到新视图。

我在配置中设置了app和rout。

var app = angular.module('app', ['ngAnimate', 'ngRoute']);

app.config(function ($routeProvider) {
    $routeProvider
      .when('/', {
        templateUrl:"home.html",
        reloadOnSearch:false
      })
      .when('/about-us', {
        templateUrl:"about.html",
        reloadOnSearch:false
      })
      .when('/contact', {
        templateUrl:"contact.html",
        reloadOnSearch:false
      })
      .otherwise({
          template : "<h1>None</h1><p>Nothing has been selected</p>"
      });
});

我有一个服务,我存储动画时间和布尔指示视图的可见性:

app.service('animationProperties', function () {
  this.animationTimings = {
    views: 1000
  };
  this.visibility = {
    view : false
  };
});

我有一个带有简单调试功能的主控制器和一个onRouteChangeStart函数,它应该从当前视图中删除活动类(通过使视图可见性布尔为false):

app.controller('MainCtrl', function ($scope, $window, $location, 
                                     animationProperties) {

  $scope.animationProperties = animationProperties;

  $scope.$on('$routeChangeStart',function () {
    animationProperties.visibility.view = false;
  });

  $scope.toggleActive = function(){
    $scope.animationProperties.visibility.view = !$scope.animationProperties.visibility.view;
  }
});

最后,ngAnimate等待离开动画完成,然后删除当前视图(使用done()方法)并通过使可见性布尔值再次进入新视图:

app.animation('.view', function($timeout, animationProperties) {
  return {
    enter: function(element, done) {
      $timeout(function () {
        animationProperties.visibility.view = true;
        $timeout(function () {
          done();
        }, animationProperties.animationTimings.views);//Wait to enter
      },animationProperties.animationTimings.views); //Wait for leave function
    },
    leave: function(element, done) {
      $timeout(function () {
        done();
      }, animationProperties.animationTimings.views);
    }
  }
});

以下是plunker

第一次切换页面时(从导航)你会发现一切正常,但是当第二次访问页面时,视图类没有更新,因此不会播放动画。在调试时,您可以清楚地看到可见性布尔值正确更新,但是离开视图时的ng-class没有得到更新。

非常感谢您的帮助!!!

1 个答案:

答案 0 :(得分:1)

here引用自己:

这是正在发生的事情:

  1. $routeChangeStart上,您更改(一旦评估后)将告知ngClass从离开视图中删除active类的值。
  2. 同时,$route开始准备输入视图,包括获取其模板。
  3. 一切准备就绪后,会触发$routeChangeSuccess事件,表示ngView开始交换两个视图。
  4. 在交换过程中,ngView会破坏离开视图的范围,从那时起范围的观察者就会停止观看。
  5. 因此,如果步骤1-4发生得足够快,则在为ngClass计算必要表达式以删除active类之前,将终止离开视图的范围。第一次访问路线时,动画会起作用,因为$route必须为输入视图的模板发出服务器请求(这使ngClass时间完成其工作)。但是,当您访问以前访问过的路径时,模板已经被缓存,转换速度很快。

    您可以通过故意减慢模板检索来解决这个问题(甚至VM转动就足够了)。例如:

    app.decorator('$templateRequest', ($delegate, $timeout) => {
      const $templateRequest = (...args) => $delegate(...args).
        then(tmpl => $timeout().then(() => tmpl));
      Object.defineProperty($templateRequest, 'totalPendingRequests', {
        get: () => $delegate.totalPendingRequests,
        set: nv => $delegate.totalPendingRequests = nv,
      });
      return $templateRequest;
    });
    

    Updated plnkr 1

    解决它的另一种方法是实现自己的,简单化的ngClass同步版本,以便在离开视图的范围被销毁之前立即应用类。这种指令的粗略,未经优化,非生产就绪的版本可能如下所示:

    app.directive('myClass', () => (scope, elem, attrs) => {
      scope.$watchCollection(attrs.myClass, newValue => {
        Object.keys(newValue).forEach(c => {
          if (newValue[c]) {
            elem.addClass(c);
          } else {
            elem.removeClass(c);
          }
        });
      });
    });
    

    Updated plnkr 2

    所有这一切,你的看起来像一个奇怪的设置:

    • 收听活动($routeChangeStart)。
    • 设置一个标记(animationProperties.visibility.views),触发ngClass删除一个类。
    • 删除类,触发CSS动画。
    • 在此期间,让自定义JavaScript动画(animation('.view'))与CSS动画同步,然后重新设置标志。
    • 通过设置标志,触发输入视图的相反CSS动画。

    首先,为什么同时使用CSS和JS动画?例如,您可以处理JS动画中的不透明度变化(假设您的实际设置更复杂,需要JS动画来处理其他效果)。

    或者你可以使用基于自动添加/删除的ng-enter / ng-leave类的纯CSS动画更轻松地处理淡入/淡出(它只有4个很小的CSS规则:笑脸:):

    [ng-view].ng-enter {
      /* Wait for the leaving view to...leave, then start transitioning in. */
      transition: opacity 1s ease 1s;
    }
    
    [ng-view].ng-leave {
      /* Start transitioning out. */
      transition: opacity 1s ease;
    }
    
    [ng-view].ng-enter,
    [ng-view].ng-leave.ng-leave-active {
      /*
       * At the beginning of the entering animation and
       * at the end of the leaving animation,
       * the view must be fully invisible.
       */
      opacity: 0;
    }
    
    [ng-view].ng-enter.ng-enter-active,
    [ng-view].ng-leave {
      /*
       * At the end of the entering animation and
       * at the beginning of the leaving animation,
       * the view must be fully visible.
       */
      opacity: 1;
    }
    

    Updated plnkr 3