Angular does not update ng class on ng-view

I am using angular 1.6.5 for my angular application and came across very strange behavior.

I want to achieve: when ngroute changes, I have to remove the active class from the current view, wait for the vacation animation to complete, and then add the active class to the new view.

I installed the application and routes in config.

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>" }); }); 

I have a service that stores animation timings and booleans showing the visibility of a view:

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

I have one main controller with a simple debugging function and one onRouteChangeStart function, which should remove the active class from the current view (making visibility a logical 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; } }); 

Lastly, ngAnimate, which waits for the vacation animation to complete, then deletes the current view (using the done () method) and re-introduces the new view, making the appearance logical: 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); } } }); 

Here is the plunker

The first time you switch pages (from navigation), you will see that everything works fine, but when you go to pages, the second class of viewing time does not update, so the animation does not play. During debugging, you can clearly see that the boolean visibility is updated correctly, but the ng class is not updated when exiting the view.

Your help would be greatly appreciated.

+5
source share
1 answer

Quote from here :

This is what happens:

  • In $routeChangeStart you change the value that (after calculation) will tell ngClass to remove the active class from the output view.
  • At the same time, $route begins to prepare an introductory look, including getting its template.
  • Once everything is ready, it raises the $routeChangeSuccess event, which signals ngView to start replacing the two views.
  • During the swap process, ngView destroys the outgoing region, after which the region observers cease to be ... observable.

So, if steps 1-4 are performed quickly enough, the viewport of the output object will be destroyed before the necessary expressions are evaluated for ngClass to remove the active class. The first time you visit a route, the animation works because $route needs to fulfill a server request for an input input template (which gives ngClass time to do its job). However, when you visit a previously visited route, the template is already cached and the transition is fast.


You can get around this by deliberately slowing down template extraction (even turning the virtual machine is enough). For instance:

 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 by plnkr 1 )

Another way around this is to implement your own, simplified, synchronous version of ngClass so that classes are applied immediately before the torn field of view is destroyed. An uncleaned, non-optimized, not ready-for-release version of such a directive might look like this:

 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 by plnkr 2 )


All that was said seems like your weird setup:

  • Listen to the event ( $routeChangeStart ).
  • Set the flag ( animationProperties.visibility.views ) that runs ngClass to remove the class.
  • Removing a class, launches CSS animation.
  • At the same time, custom JavaScript animation('.view') ( animation('.view') ) sync with CSS animations and then return a flag.
  • By setting the flag back, invoke the opposite CSS animation for the incoming view.

๐Ÿ˜•

For starters, why use CSS and JS animation? For example, you can handle the opacity change from a JS animation (assuming your actual setup is more complex and requires JS animation for other effects).

Or, you can much more easily handle fade in / out with pure CSS animations based on the automatically added / removed ng-enter / ng-leave classes (these are just 4 tiny CSS rules: 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; } 

( Updated by plnkr 3 )

+1
source

Source: https://habr.com/ru/post/1272043/


All Articles