How to defer the rendering of the router-outlet in Angular 2

I am currently working on an Angular 2 application that consists of various components relying on data fetched from the server using the http-service. This data includes user information and roles.

Most of my route components encounter errors within their ngOnInit() methods if the required data is not yet loaded. The data is fetched and stored in a service that is injected into all components.

Is there a way to delay the rendering of the current route in my root component until the http call is completed?

Otherwise, I would need to implement a check and retry mechanism in the ngOnInit of all route components, which would be cumbersome.

I have attempted to hide the router-outlet element until the call is complete, but this results in an error stating "Cannot find primary outlet to load xxx".

Answer №1

One can choose to postpone the initial navigation of a router when setting up the Router module:

RouterModule.forRoot([
    // define routes here
], {
    initialNavigation: false // allows for delaying the navigation
}),

Subsequently, the initial navigation can be manually initiated using Angular Router, demonstrated below:

this.router.initialNavigation();

Answer №2

To achieve this functionality, I utilized a CanActivate guard. The crucial aspect of making it effective is to return an Observable from the canActivate method. This allows for the flexibility of delaying the navigation process as needed.

import { CanActivate } from '@angular/router';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { StateService } from '../../_services/state.service';
import { Subject } from 'rxjs/Rx';
import { Subscription } from 'rxjs/Subscription';

@Injectable()
export class LoadingGuard implements CanActivate {
    constructor(private state: StateService) {}

    public canActivate(): Observable<boolean> {
        if (!this.state.loading$.getValue()) { 
            return Observable.of(true); 
        }

        let subject = new Subject<boolean>();
        let subscription = this.state.loading$.subscribe(value => {
            if (!value) {
                subject.next(true);
                subject.complete();

                subscription.unsubscribe();
            }
        });

        return subject;
    }
}

The StateService mentioned above is responsible for evaluating the current user and pre-caching essential data for the application. It contains a subject named loading$ that signals when the loading process is complete.

Lastly, remember to add the guard to the app module.

import { LoadingGuard } from './app/loading-guard/loading-guard';
// other imports omitted

@NgModule({
    // other module properties omitted
    providers: [LoadingGuard]
})
export class AppModule {}

Then incorporate the guard into your routing configuration.

import { LoadingGuard } from './app/loading-guard/loading-guard';
// other imports omitted

export const rootRouterConfig: Routes = [
    { path: 'app', component: AppComponent, 
      canActivate: [LoadingGuard], 
      children: [
        { path: 'index', component: IndexComponent },
        // child routes omitted
      ] },
    { path: 'sign-in', component: SignInComponent },
    { path: '**', redirectTo: 'sign-in' }
];

For further information, refer to the official documentation on CanActivate: https://angular.io/docs/ts/latest/api/router/index/CanActivate-interface.html

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

Unexpected behavior from Bootstrap within React

I recently started working on a React project that I initiated with the create-react-app command. To incorporate Bootstrap into my project, I added the necessary CDNs to the public/index.html file after generating the project. <link rel="stylesheet" hr ...

Ionic 2: Image source not being updated

When working with Ionic 2, I encountered an issue where the src attribute of an <img> element was not updating inside the callback function of a plugin. Here is the template code: <img [src]="avatar_path" id="myimg" /> After using the Came ...

Tips for managing the sequence of chosen items

Currently, I am utilizing the react-dropdown-tree-select component in my project. I have a question regarding changing the order of selected items. The current default behavior is for the selected item order to match the data list order. However, I woul ...

Tips on choosing vml elements using jquery or vanilla javascript

I am trying to select a VML element using jQuery without relying on 'id' or 'class', but my attempts have been unsuccessful so far. <v:oval id="vmlElement" style='width:100pt;height:75pt' fillcolor="red"> </v:oval> ...

Utilizing objects from a JSON file within an HTML document

I'm currently in the process of creating a comprehensive to-do list, and I want to establish a JSON file that will link all the items on the list together. Despite my efforts, I find myself uncertain about the exact steps I need to take and the speci ...

Retrieve a specific value from the NGXS state by providing a parameter

I have a unique situation where my state contains JSON data like this: {id: "1", name: "ig1", description: "ig 11"} {id: "5", name: "hhh", description: "hhh"} {id: "6", name: "ggg", description: "hhh"} My goal is to specifically extract the data for id = ...

The FileReader encountered an error because the first parameter is not a 'Blob' type

I seem to be encountering an issue with a javascript FileReader that is throwing the error Uncaught TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'. This problem occurs intermitt ...

Setting up project with local library installation in Angular 7 - Project configuration

I am currently working on an Angular application that consists of a core module and a shared module. The structure of my project is as follows: ./repo | projects | core | shared | src (my app) When I build libraries, the output folder is dist ...

Adjusting the appearance of a JavaScript element based on its hierarchy level

Currently, I am utilizing Jqtree to create a drag and drop tree structure. My main goal is to customize the appearance of the tree based on different node levels. Javascript Tree Structure var data = [ { name: 'node1', id: 1, chi ...

Retrieve information for the designated page exclusively

When retrieving data from the backend using a service, I encounter an issue where the system may slow down if 2000 records are returned in one request. To address this, I would like to display only 10 records per page and fetch the next 10 records with eac ...

Can someone help me figure out the best way to locate a material-ui slider within a react

I am seeking to incorporate multiple material-ui sliders into a single react component that share a common event handler. However, I have encountered difficulties in identifying which slider triggered the event. Despite referring to the API documentation, ...

Is there a way to enlarge the font size for a complete tag?

I'm having trouble with a project. One of the tasks is to use jQuery or JavaScript to increase the font size of a paragraph. The console statements are incrementing by +1 with every mouse click (the first click adds 1, the second adds 2, and so on). ...

There seems to be an issue with the functionality of Angular Material on iOS devices

I recently developed a website using Angular and Angular Material. While the site functions properly on Windows and Android across all browsers, I encountered an issue with iOS devices running Safari. Some elements on the page do not display correctly on i ...

What is the process for retrieving the value of `submit.preloader_id = "div#some-id";` within the `beforesend` function of an ajax call?

In my JavaScript code, I have the following written: var formSubmit = { preloaderId: "", send:function (formId) { var url = $(formId).attr("action"); $.ajax({ type: "POST", url: url, data: $(formId).serialize(), dataTy ...

What is the best way to update text in an element when hovering or activating it?

I am currently designing a website with a prominently placed "Hire me" button in the center of the page. The button was implemented using the following code: <div id="hire_me_button"> <a href="contact.html"><h4 id="hire_me" class="butto ...

What is the best way to remove a specific HTML section using a JavaScript function?

I am struggling to figure out how to remove a specific HTML section using a button that is contained within the section itself. The section I want to delete was initially added by clicking a different button. Although I can successfully add a new section ...

Vue router beforeRouteEnter - when the page is refreshed, the button label vanishes

<script> export default { name: 'TEST', data() { return { prevRoute: '' } }, methods: { goBack() { return this.$router.go(-1); }, }, beforeRouteEnter(to, from, next) { next(vm => { ...

How to set up Apache to configure direct access to a specific route in Angular 2 routing?

I built a small Angular2 test application that includes routing functionality. Everything runs smoothly when the user starts from the home page and navigates to sub-pages. If the user tries to directly access a sub-page, they will encounter a 404 error u ...

Shopping Dialog with Bootstrap (nakupanda) captures form input [JSFiddle]

Having difficulty extracting data from my form. Currently utilizing the bootstrap dialog from nakupanda () The dialog (located within a fullcalendar select function) var form = $('#createEventForm').html(); BootstrapDialog.show({ mes ...

Having difficulty generating dynamic rows and tree dropdowns in AngularJS

Struggling to implement dynamic row functionality with Angular JS. The rows are working well, but I also need to incorporate a tree dropdown within each row. Unfortunately, clicking the "add row" button populates the same data in all rows. I have shared m ...