Ensuring AngularJS ui-router/app waits for $http data to avoid Flash of Unstyled Content (FOUC)

My question or situation pertains to the states defined in my AngularJS application. Here is an example of how I have them structured:

$stateProvider
            .state('myApp', {
                abstract: true,
                template: '<ui-view/>'
            })
            .state('myApp.stateOne', {
                url: 'state1',
                templateUrl: '/an/views/state-1.html',
                controller: 'StateOneCtrl'
            })
            .state('myApp.stateTwo', {
                url: 'state2',
                templateUrl: '/an/views/state-2.html'
                controller: 'StateTwoCtrl'
            })
            .state('myApp.stateThree', {
                url: 'state3',
                templateUrl: '/an/views/state-3.html'
                controller: 'StateThreeCtrl'
            })

In this scenario, if I need to verify whether a user is permitted to access 'mayApp.stateThree', I typically make a backend request. This is handled by a service (in this case, named IsAllowedService). Normally, I would include the logic for this check in the .run() block within my app.js file, as shown below:

.run(['IsAllowedService', '$state', function (IsAllowedService, $state) {

        $rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState) {

            // Check if we are trying to access myApp.stateThree and determine permission...
            if (toState.name === 'myApp.stateThree') {
                IsAllowedService.checkIfIsAllowed().then(function (resp) {
                    if(resp.allowed === false) {
                        $state.go('myApp.stateOne');
                    }
                });
            }

        });

}]);

While the above method works fine, it does not wait for the service response before loading 'mayApp.stateThree'. As a result, there is a quick display of the page before redirection occurs. I could replicate the same code in the 'StateThreeCtrl' but the flash issue persists. Is there a way to address this during state definition? For instance, something like the hypothetical code snippet below:

.state('myApp.stateThree', {
    url: '/an/state3',
    templateUrl: '/an/views/state-3.html'
    controller: 'StateThreeCtrl',
    resolve: {
        isAllowed : function () {
        IsAllowedService.checkIfIsAllowed().then(function (resp) {
            return resp;
            })
        }
    }

It is evident that directly injecting services such as $http may not be feasible, but is there a method to delay the rendering of the view / controller for 'mayApp.stateThree' until the result from

IsAllowedService.checkIfIsAllowed()
is obtained? Any guidance on structuring my application/code would be welcomed. Despite using ng-cloak in the HTML view, it did not resolve the issue!

Answer №1

It seems like you're on the right track in your application's run block, but there are a few things you can adjust to improve it. One way to enhance it is by preventing certain actions using:

  event.preventDefault(); //Prevent from going to the page

Additionally, incorporating custom data into your $states will enable you to validate conditions based on specific criteria. For example:

$stateProvider.state('home', {
  controller: 'HomeController as home',
  url: '/home',
  templateUrl: 'home.html',
  data: { roles: [ROLES.ANONYMOUS] }}); //You can set any condition here
$stateProvider.state('user', {
  controller: 'UserController as user',
  url: '/user',
  templateUrl: 'user.html',
  data: { roles: [ROLES.ADMIN, ROLES.USER] }});

To access this custom data, utilize the $stateChangeStart event:

  $rootScope.$on('$stateChangeStart', function(event, next) {
    if (!yourService.isAuthorized(next.data.roles)) {
      event.preventDefault(); //Prevent from going to the page -> avoid flickering
      $state.go('403'); //Or any other desired action
    } 
  });

The flickering issue arises when using a Promise and waiting for its fulfillment before redirecting the page. To resolve this, you can prevent the default action, authorize appropriately, and continue with your desired flow once the promise resolves.

       if (toState.name === 'myApp.stateThree') {
            event.preventDefault(); //prevent the request.
            IsAllowedService.checkIfIsAllowed().then(function(resp) {
                if (resp.allowed === false) {
                    $state.go('myApp.stateOne');
                } else { //He is allowed to go to state three.
                   $state.go('myApp.stateThree');
                } 
            }, function() { //handle server errors
              $state.go('myApp.stateOne'); //Prevent unwanted access
        });

If these conditions don't change during runtime, such as user role-based scenarios, consider fetching them upon user verification to eliminate the need for a promise altogether. Hopefully, this advice proves useful.

I previously shared a similar post here along with a working plunker.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Guide to successfully downloading an xlsx file in angular through express

I am creating an xlsx file based on user input within the express framework. The data is sent via a post request and I intend to send back the file content using res.download(...). However, when I do this, the data field in my ajax response ends up contai ...

Challenges in developing complex single-page applications

Currently, I am in the process of developing an extensive single-page web/javascript application that is going to be quite large. The technologies I am utilizing include ASP.NET MVC4, jquery, knockout.js, and amplify.js. One obstacle I am encountering is ...

Populating a ListBox without the need to constantly scroll upwards

I'm currently facing an issue with a ListBox that displays online users fetched from a MySQL database. Every second, the ListBox is updated with new users. The problem arises when adding an item to the ListBox causes it to scroll up, which I want to a ...

Is there a straightforward method to retrieve all information from Vue.js?

Is there a way to extract all of this data? I've attempted using this._data.forEach but it doesn't seem to be effective. Thank you! data() { return { childData: '', credit: '', company: '', email: ...

Using Node.js, Handlebars, and Express for template inheritance

As I embark on my Node.js learning journey, I am starting with creating simple applications to grasp the fundamentals. Recently, I wanted to implement a Django-like template structure in my projects but found myself stuck on how to achieve it. I have come ...

Is it possible to leverage both functions and variables within the ng-options expression in Angularjs?

I am faced with a situation where I have 2 select boxes. The first one is used to choose the user type (such as groups or individual), and the second one displays the options based on the selection made in the first box. I was wondering if it is possible t ...

Firefox triggers drag events while passing over a div scrollbar

I am looking to create a file drag and drop feature using a styled div: When dragging files over the div, I want the border color to change When dragging files out of the div, I want the border color to revert back to the original In Firefox 35 (Ubuntu) ...

Multiple minute delays are causing issues for the Cron server due to the use of setTimeout()

I have an active 'cron' server that is responsible for executing timed commands scheduled in the future. This server is dedicated solely to this task. On my personal laptop, everything runs smoothly and functions are executed on time. However, ...

AngularJS - displaying a notification when the array length is empty and customizing the visibility to conceal the default action

I've been working on an Angular project where I display a loading circle that disappears once the content is loaded. This circle is simply a CSS class that I call from my HTML only when the page first loads, like this: Actually, the circle consists o ...

Link various data to various text boxes using a common ngModel property in Angular 8

In my project, I am working on creating a time-picker that will open when the user focuses on a text-box. The challenge I'm encountering is that although there are multiple text-boxes on a single page, binding the selected value from the time-picker u ...

Introducing Laravel 6's Hidden Gems: Unleash the Power of @push

Hey everyone, I'm a newcomer to the world of Laravel and currently using Laravel 6.0 I've encountered an issue with my javascript code that utilizes @push. Strangely enough, the script only functions properly when I manually insert the code into ...

React Redux causing React Router to display empty pages

In my App.js, the following code is present: const Index = asyncRoute(() => import('~/pages/index')) const Register = asyncRoute(() => import('~/pages/register')) const AddDesign = asyncRoute(() => import('~/pages/add-des ...

Oops! An error occurred while trying to load the myApp module. The module 'ui.bootstrap' is missing and causing the failure

When using Firefox, I encountered the following error: SyntaxError: syntax error xml2json.js:1 SyntaxError: syntax error ui-bootstrap-tpls-0.13.0.js:1 Error: [$injector:modulerr] Failed to instantiate module myApp due to: [$injector:modulerr] Failed to in ...

After the table finishes loading, my goal is to display an export to Excel button

I am currently working on generating an HTML table using JSON data received from the backend Java application. My objective is to display an "Export to Excel" button after populating the table. The process involves users entering dates and selecting option ...

Looking to utilize HTML and AngularJS to locally upload a file?

I am facing an issue with my HTML page that is designed to upload files. I need to store the uploaded file locally using AngularJS, but despite trying various solutions, I have not been successful so far. Below is a snippet of my HTML code: <body> ...

Navigating between applications

Does anyone have suggestions on setting up routing between different apps without disrupting existing code? We want to make the integration process easy when adding a new app to our main application. For example, navigating from an electronics(main app) ...

Display the personalized list of user items on the MERN dashboard

I'm currently developing a React booking platform that interacts with my backend through a Rest API using axios and redux. My challenge now is to display personalized reservations and rooms for each user on the website. However, I'm facing an iss ...

Creating a customized post method in Angular's resource API

I am looking to streamline my code for posting data to a SharePoint list by utilizing a resource factory. Currently, I have been posting data using the following method: this.save = function(data) { data["__metadata"] = { "type": getItemTypeForListNam ...

Break up a line within an ionic element by inserting a linebreak tag

Currently, I am constructing an app using ionic framework and facing a challenge in inserting a linebreak within an ionic tag to ensure proper display inside the designated container... <h2>{{caravan.model}}</h2> ...

Is there a way to transfer a JSON map object from Flask and then utilize it as a JavaScript object?

python server code from flask import Flask, render_template, request import os import sys import json data_raw = [('0', '1', '0', '0'), ('0', '0', '1', '0'), ('1', ...