Angular 未在 ng-view 上更新 ng-class
Angular not updating ng-class on ng-view
我正在为我的 angular 应用程序使用 angular 1.6.5,但遇到了一个非常奇怪的行为。
我想实现的是:当ngroute被更改时,我必须从当前视图中删除active class,等待离开动画完成,然后将active class添加到新视图中查看。
我已经在配置中设置了应用程序和路由。
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 函数,应该从当前视图中删除活动 class(通过使视图可见性布尔值为假):
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
第一次(从导航)切换页面时,您会发现一切正常,但是当第二次转到页面时,视图 class 没有更新,因此没有播放动画。在调试时,您可以清楚地看到可见性布尔值已正确更新,但离开视图时 ng-class 未更新。
非常感谢您的帮助!!!
引自here:
事情是这样的:
- 在
$routeChangeStart
上,您更改值(一旦评估)将告诉 ngClass
从离开视图中删除 active
class。
- 同时,
$route
开始准备进入视图,包括获取其模板。
- 一切准备就绪后,它会触发
$routeChangeSuccess
事件,这表明 ngView
开始交换两个视图。
- 在交换过程中,
ngView
破坏了离开视图的范围,从这一点开始范围的观察者停止被...观察。
因此,如果步骤 1-4 发生得足够快,在为 ngClass
评估必要的表达式以删除 active
class 之前,离开视图的范围将被破坏。第一次访问路线时,动画有效,因为 $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;
});
另一种解决方法是实现您自己的、简单的同步版本 ngClass
,以便在销毁离开视图的范围之前立即应用 classes。这种指令的原始、未优化、非生产就绪版本可能如下所示:
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);
}
});
});
});
综上所述,您的设置似乎很奇怪:
- 监听一个事件(
$routeChangeStart
)。
- 设置一个标志 (
animationProperties.visibility.views
) 触发 ngClass
删除 class。
- 删除 class,触发 CSS 动画。
- 与此同时,让自定义 JavaScript 动画 (
animation('.view')
) 与 CSS 动画同步,然后重新设置标志。
- 通过重新设置标志,为进入视图触发相反的 CSS 动画。
对于初学者,为什么要同时使用 CSS 和 JS 动画?例如,您可以处理 JS 动画的不透明度变化(假设您的实际设置更复杂并且需要 JS 动画来实现其他效果)。
或者您可以使用基于自动 added/removed ng-enter
/ng-leave
[=] 的纯 CSS 动画更轻松地处理淡入淡出 in/out 91=]es(这只是 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;
}
我正在为我的 angular 应用程序使用 angular 1.6.5,但遇到了一个非常奇怪的行为。
我想实现的是:当ngroute被更改时,我必须从当前视图中删除active class,等待离开动画完成,然后将active class添加到新视图中查看。
我已经在配置中设置了应用程序和路由。
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 函数,应该从当前视图中删除活动 class(通过使视图可见性布尔值为假):
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
第一次(从导航)切换页面时,您会发现一切正常,但是当第二次转到页面时,视图 class 没有更新,因此没有播放动画。在调试时,您可以清楚地看到可见性布尔值已正确更新,但离开视图时 ng-class 未更新。
非常感谢您的帮助!!!
引自here:
事情是这样的:
- 在
$routeChangeStart
上,您更改值(一旦评估)将告诉ngClass
从离开视图中删除active
class。 - 同时,
$route
开始准备进入视图,包括获取其模板。 - 一切准备就绪后,它会触发
$routeChangeSuccess
事件,这表明ngView
开始交换两个视图。 - 在交换过程中,
ngView
破坏了离开视图的范围,从这一点开始范围的观察者停止被...观察。
因此,如果步骤 1-4 发生得足够快,在为 ngClass
评估必要的表达式以删除 active
class 之前,离开视图的范围将被破坏。第一次访问路线时,动画有效,因为 $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;
});
另一种解决方法是实现您自己的、简单的同步版本 ngClass
,以便在销毁离开视图的范围之前立即应用 classes。这种指令的原始、未优化、非生产就绪版本可能如下所示:
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);
}
});
});
});
综上所述,您的设置似乎很奇怪:
- 监听一个事件(
$routeChangeStart
)。 - 设置一个标志 (
animationProperties.visibility.views
) 触发ngClass
删除 class。 - 删除 class,触发 CSS 动画。
- 与此同时,让自定义 JavaScript 动画 (
animation('.view')
) 与 CSS 动画同步,然后重新设置标志。 - 通过重新设置标志,为进入视图触发相反的 CSS 动画。
对于初学者,为什么要同时使用 CSS 和 JS 动画?例如,您可以处理 JS 动画的不透明度变化(假设您的实际设置更复杂并且需要 JS 动画来实现其他效果)。
或者您可以使用基于自动 added/removed ng-enter
/ng-leave
[=] 的纯 CSS 动画更轻松地处理淡入淡出 in/out 91=]es(这只是 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;
}