AngularJS Method of Preventing Page Flicker

  • 2021-08-03 08:17:54
  • OfStack

We know that when the application page or component needs to load data, it takes 1 time for the browser and angular to render the page. The interval here may be very small, and even people can't feel the difference; But it can also be very long, which will cause our users to see pages that have not been rendered.

This situation is called Flash Of Unrendered Content (FOUC) (K)? and is always unwanted. Here are a few different ways to prevent this from happening to our users.

1. ng-cloak

The ng-cloak directives are built-in directives for angular, which hide all the elements it contains:


<div ng-cloak>
 <h1>Hello {{ name }}</h1>
</div>

After the browser loads and compiles the rendering, angular automatically removes the ngCloak element attribute so that the element becomes visible.

The safe way to use ng-cloak in IE7 is to add an extra ng-cloak class to the element


<div ng-cloak class="ng-cloak">
 <h1>Hello {{ name }}</h1>
</div>

2. ng-bind

ng-bind is another angular built-in instructions for manipulating bound page data. We can use ng-bind instead of {{}} to bind elements to the page;

Using ng-bind instead of {{}} prevents the unrendered {{}} from being shown to the user, and using ng-bind instead of {{}} is much friendlier.

The above example can be rewritten as follows, which can prevent the page from appearing {{}}


<div>
 <h1>Hello <span ng-bind="name"></span></h1>
</div>

3. resolve

When using routes (routing) between different pages, we have other ways to prevent pages from being rendered before data is fully loaded into route.

Using resolve in route allows us to get the data we need to load before route is fully loaded. When the data is loaded successfully, the route will change and the page will be presented to the user; Data is not loaded successfully route does not change, the $routeChangeError event will get fired. "Will the $routeChangeError event (not) be activated?"


angular.module('myApp', ['ngRoute'])
.config(function($routeProvider) {
 $routeProvider
 .when('/account', {
  controller: 'AccountCtrl',
  templateUrl: 'views/account.html',
  resolve: {
   // We specify a promise to be resolved
   account: function($q) {
    var d = $q.defer();
    $timeout(function() {
     d.resolve({
      id: 1,
      name: 'Ari Lerner'
     })
    }, 1000);
    return d.promise;
   }
  }
 })
});

The resolve entry requires an key/value object, key is the name of the resolve dependency, and value can be a string (as a service) or a method that returns the dependency.

resolve is very useful when the resolve value returns a promise that becomes resolved or rejected.

When the route is loaded, the keys in the resolve parameter can be used as an injectable dependency:


angular.module('myApp')
.controller('AccountCtrl', 
 function($scope, account) {
  $scope.account = account;
});

We can also use resolve key to pass the results returned by the $http method, as $http returns promises from it 's method calls:


angular.module('myApp', ['ngRoute'])
.config(function($routeProvider) {
 $routeProvider
 .when('/account', {
  controller: 'AccountCtrl',
  templateUrl: 'views/account.html',
  resolve: {
   account: function($http) {
    return $http.get('http://example.com/account.json')
   }
  }
 })
});

It is recommended to define a standalone service to use resolve key and use service to return the required data accordingly (this is easier to test). To do this, we need to create an service:

First, look at accountService,


angular.module('app')
.factory('accountService', function($http, $q) {
 return {
  getAccount: function() {
   var d = $q.defer();
   $http.get('/account')
   .then(function(response) {
    d.resolve(response.data)
   }, function err(reason) {
    d.reject(reason);
   });
   return d.promise;
  }
 }
})

After defining service, we can use this service instead of calling $http directly in the above code:


angular.module('myApp', ['ngRoute'])
.config(function($routeProvider) {
 $routeProvider
 .when('/account', {
  controller: 'AccountCtrl',
  templateUrl: 'views/account.html',
  resolve: {
   // We specify a promise to be resolved
   account: function(accountService) {
    return accountService.getAccount()
   }
  }
 })
});

Related articles: