Resolution of page flicker issue during AngularJS page access

  • 2021-01-25 06:54:23
  • OfStack

We know that it takes a certain amount of time for both the browser and angular to render the page when the application page or component needs to load data. The spacing here may be so small that it is almost imperceptible; But it can also be very long, which can cause our users to see a page that has not been rendered.


This case 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, cloak ng -
The ng-cloak directive is a built-in angular directive that hides all elements it contains:


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

Ng-cloak is implemented by one directive, and the page is initialized by adding 1 line of CSS code to heade of DOM, as follows:


<pre class=  " prettyprint linenums " >

  [ng\:cloak],[ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak{

  Display:none ! important;

}

</pre>

<pre class=  " prettyprint linenums " >
 
  [ng\:cloak],[ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak{
 
  Display:none ! important;
 
}
 
</pre>


Angular sets the element with ng-cloak to display: none.

When angular is resolved to a node with ng-cloak, the ng-cloak and calss elements are dropped at the same time, thus preventing the node from flashing. As follows:


<script type = " text/ng-template "  id = " scenario.js-150 " >

  It( ' should remove the template directive and css class',function(){

 Expect(element( ' .doc-example-live #template1').attr( ' ng-cloak'))

  not().toBeDefined();

   Expect(element( ' .doc-example-live #template2').attr( ' ng-cloak')).

Not().toBeDefined();

});

</script>

<script type = " text/ng-template "  id = " scenario.js-150 " >
 
  It( ' should remove the template directive and css class',function(){
 
 Expect(element( ' .doc-example-live #template1').attr( ' ng-cloak'))
 
  not().toBeDefined();
 
   Expect(element( ' .doc-example-live #template2').attr( ' ng-cloak')).
 
Not().toBeDefined();
 
});
 
</script>


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

Using ng-bind instead of {{}} prevents unrendered {{}} from being shown to the user. Using ng-bind instead of {{}} with empty elements rendered by ng-bind is much more friendly.

The above example could be rewritten as follows to prevent {{}} from appearing on the page


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

3, resolve
When using routes (routing) between different pages, we have another way to prevent the page from being rendered before the 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, the route is changed and the page is presented to the user. route $routeChangeError event will get fired = routeChangeError $routeChangeError event will get fired = the $routeChangeError event will get fired = the $routeChangeError event will get fired = the $routeChangeError event get fired


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. value can be either 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.

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


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

as $http returns promises from it's method calls 'resolve key http $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 separate service method for using resolve key and to use service to return the required data accordingly (this method is easier to test). To do this, we need to create an service:

First, take a 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;
 }
 }
})

With service defined we can use this to replace the direct call to $http 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: