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

gog*_*dze 6 javascript angularjs ng-animate ng-view

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

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

我在配置中设置了app和routs.

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>"
      });
});
Run Code Online (Sandbox Code Playgroud)

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

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

我有一个主控制器具有简单的调试功能和一个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;
  }
});
Run Code Online (Sandbox Code Playgroud)

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

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);
    }
  }
});
Run Code Online (Sandbox Code Playgroud)

这是掠夺者

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

非常感谢您的帮助!

gka*_*pak 2

这里引用我自己的话:

这是正在发生的事情:

  1. 在 a 上$routeChangeStart,您可以更改(一旦评估)将告知从离开视图中ngClass删除该类的值。active
  2. 同时$route开始准备进入视图,包括获取其模板。
  3. 一旦一切准备就绪,它就会触发该$routeChangeSuccess事件,该事件发出信号ngView开始交换两个视图。
  4. 在交换过程中,ngView会破坏离开视图的范围,从此时开始,范围的观察者将停止被...观察。

因此,如果步骤 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;
});
Run Code Online (Sandbox Code Playgroud)

更新 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);
      }
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

更新 plnkr 2


话虽这么说,你的设置似乎很奇怪:

  • 监听事件 ( $routeChangeStart)。
  • animationProperties.visibility.views设置触发ngClass删除类的标志 ( )。
  • 删除该类会触发 CSS 动画。
  • 同时,让自定义 JavaScript 动画 ( animation('.view')) 与 CSS 动画同步,然后重新设置标志。
  • 通过重新设置标志,为进入视图触发相反的 CSS 动画。

对于初学者来说,为什么要同时使用 CSS 和 JS 动画?例如,您可以处理 JS 动画的不透明度变化(假设您的实际设置更复杂并且需要 JS 动画来实现其他效果)。

ng-enter或者,您可以使用基于自动添加/删除/类的纯 CSS 动画更轻松地处理淡入/淡出ng-leave(这只是 4 个微小的 CSS 规则:smiley:):

[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;
}
Run Code Online (Sandbox Code Playgroud)

更新 plnkr 3