Obtaining necessary data prior to Angular 6 app initialization

My service retrieves essential data from the server for my components, but it should only be called once due to the heavy load it places on the server. I have encountered an issue where the service successfully fetches the data in the app module provider, as evidenced by logging, but when I try to access it in a component, it returns Undefined!

I attempted to optimize the service initialization by removing UserService from providers to avoid multiple initializations, but this approach led to errors.

The function responsible for fetching information:

getAdminInitInfo(): Promise<any> {
var vm = this;
const promise = this.http.get<AdminInit>(baseUrl + '/panel/admin/initial/')
  .toPromise()
  .then(
    response => {
      vm.adminInitInfo = response;
      console.log(response);
    }
  )
return promise;
}

The factory intended for use in APP_INITIALIZATION:

export function adminProviderFactory(provider: UserService) {
  return () => provider.getAdminInitInfo();
}

AppModule providers configuration:

providers: [
    AuthGuardService,
    AuthService,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: InterceptorService,
      multi: true
    },
    UserService,
    {
      provide: APP_INITIALIZER,
      useFactory: adminProviderFactory,
      deps: [UserService],
      multi: true
    }
]

Log output of the data retrieved in the component:

user.service.ts:23 service started 
user.service.ts:23 service started 
user.service.ts:70 {filters: {…}}
user.service.ts:78 undefined
overview.component.ts:19 undefined

Following this process, the adminInitInfo should be accessible whenever needed using this function:

getSomethingData() {
    var data = {};
    console.log(this.adminInitInfo);
    if (!this.adminInitInfo) {
      return undefined;
    }
    data['something'] = this.adminInitInfo.filters.something;
    data['something2'] = this.adminInitInfo.filters.something2;
    data['something3'] = this.adminInitInfo.filters.something3;
    return data;
}

Usage example in a component:

ngOnInit() {
    this.params = this.userService.getSomethingData();
}

Answer №1

If you're looking for a way to ensure that component requirements are met, one approach is to utilize a resolver in your routing configuration.

The key elements include:

The service that holds the necessary information (for example, ApplicationData which contains details like user name and version):

@Injectable()
export class ApplicationService {
    private cachedData: ApplicationData = null;

    public constructor(/* add dependencies here */) {
    }

    public initializeApplicationData(): Observable<ApplicationData> {
        if (this.cachedData) {
            return of(this.cachedData);
        }

        return /* call your service to retrieve application data */.getApplicationData().pipe(
            tap(x=> this.cachedData = x));
    }

    public getCachedData(): ApplicationData {
        return this.cachedData;
    }
}

The resolver responsible for preparing the required data before navigating to a route:

@Injectable()
export class AuthenticatedAppResolver implements Resolve<Observable<ApplicationData>> {

    public constructor(private applicationService: ApplicationService) { }

    public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<ApplicationData> {
        return this.applicationService.initializeApplicationData();
    }
}

The routing section where you secure your routes using the resolver:

    path: '',
    component: MyLayout,
    canActivate: [AuthGuard],
    children: [
        { path: 'home', component: HomeComponent },
        { path: 'page1', component: Page1Component },
        { path: 'page', component: APage2Component },
    ],
    resolve: { data: AuthenticatedAppResolver },

Make sure to register your components and services in the providers array of your module. Then you can access the getCachedData() method from the ApplicationService in any components protected by the resolver.

Note that the resolver may be called multiple times, but thanks to caching within the service, redundant requests are minimized.

Update

This approach works well for single-level sub modules. For more complex scenarios involving multiple modules needing the same data, consider creating a parent module and resolving the entire route structure there. You can then define individual routes within the routing module of that parent module. Here's an illustration:

const routes: Routes = [
  {
    path: 'login',
    component: LoginComponent
  },
  {
    path: '',
    component: MyLayout,
    canActivate: [AuthGuard],
    children: [
      {
        path: 'dashboard',
        children: [
          {
            path: '',
            component: DashboardComponent
          },
        ]
      },
      {
        path: 'publisher',
        loadChildren: './modules/publisher/publisher.module#PublisherModule'
      }
    ],
    resolve: {
      data: AuthenticatedAppResolver
    }
  },
]

Answer №2

Your current method seems overly complex. Also, you are not returning the value of response. Consider the following alternative implementation:

getAdminInitInfo(): Promise<any> {
    return this.http.get<AdminInit>(baseUrl + '/panel/admin/initial/')
    .toPromise()
    .then(
         response => { return response.json(); }
    );
}

The function response.json() already returns a promise directly.

An even more concise version may look like this:

getAdminInitInfo(): Promise<any> {
    return this.http.get<AdminInit>(baseUrl + '/panel/admin/initial/')
    .toPromise()
    .then(
         response => response.json()
    );
}

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

Setting up @cypress/code-coverage with Angular and TypeScript: A comprehensive guide

When following the instructions in Cypress documentation for @cypress/code-coverage, it recommends using the following code... // cypress/support/e2e.js import '@cypress/code-coverage/support' ...as well as... // cypress.config.ts import { defin ...

Tips for incorporating ngIf within a td element

My dilemma is with a table I have that displays data from a database. I need to be able to edit the data based on certain qualifications, so I want to include two buttons - one for deleting and one for editing. These buttons should only be enabled if the r ...

The API request does not provide randomized results and does not show any display

I am facing an issue with a button that is supposed to fetch a random user from an API. When I retrieve all the users, the information is displayed correctly. However, when I try to select a user at random, it does not work as expected. Also, it seems to a ...

Using the transform property with the scale function causes elements positioned in the bottom right corner to vanish

Issue specific to Google Chrome and Windows 10 I'm currently working on a flipbook that adjusts content size using transform:scale() based on the user's screen size. There is also a zoom feature that allows users to adjust the scale factor. I ha ...

matInput Placeholder Disappearing when Focused

Recently, I've dived into using Angular Material and decided to implement a code snippet from the Official Material Input documentation. However, when I interact with the input field, it mysteriously disappears upon focus. Before Focus After Focus ...

Is TypeScript's Structural Typing the exception to the rule?

Let me illustrate two scenarios where I encountered difficulties. The first example involves two points: one in 2d and one in 3d: type Point2D = { x: number, y: number }; type Point3D = { x: number, y: number, z: number }; let point2D: Point2D = { x: 10, ...

Having trouble retrieving data returned from Angular request

There seems to be an issue with accessing the response from a request in my Angular project. While I can print it on the console and access data.status and data.response, attempting to access data.response.entries results in the following problem. Here is ...

error: encountering issue with Vue TypeScript Jest tests - '$store' property is undefined

I've been facing issues with my tests failing after a recent npm install. I've tried adjusting versions up and down, but haven't had any success. Interestingly, the $store isn't directly used in any of the components or tests. Despit ...

Navigating UnwrapRefSimple in Vue Composition API and TypeScript: Best Practices

When I utilize reactive objects in Vue Composition API, I encounter Typescript errors relating to UnwrapRefSimple<T>. This issue appears to be specifically tied to using arrays within ref(). For instance: interface Group<T> { name: string ...

Ways to effectively utilize an interface incorporating props.children and other property varieties

Currently working on a project with Next.js and Typescript. In order to create a layout component, I defined the following interface: export interface AuxProps { children: React.ReactNode; pageTitle: 'string'; } The layout component code sn ...

Changing field visibility in Angular Reactive form (form validation) by toggling based on a checkbox in another component

I'm facing a challenge with a complex form where the fields depend on toggling checkboxes in a separate component (parent). My goal is to dynamically validate the form, with some fields being enabled and others disabled based on the toggling of the ch ...

Using Angular 2+ NgTemplateOutlet with ngFor loop

Consider the following arrays: heroes: Hero[]; villains: Villain[]; ... puppies: Puppy[] along with a template structure as shown below: <p *ngFor="let individual of heroes"> {{ individual.name }} - {{ individual.mobileNumber }} ... </ ...

JavaScript: Employ array destructuring for improved code readability (eslintprefer-destructuring)

How can I resolve the ESLint error that says "Use array destructuring. eslint(prefer-destructuring)"? The error occurs on this line of my code: let foo = 1; foo = obj.data[i][1]; //ESLint error on this line If anyone could provide assistance in fixing thi ...

Troubleshooting image upload issues with AWS S3 in Next.js version 13

I encountered a consistent problem with using the AWS SDK in NextJS to upload images. I keep getting error code 403 (Forbidden). Could there be other reasons why this error is occurring besides the accessKeyId and secretAccessKey being invalid? Below is my ...

The parameter 'CallHistoryMethodAction<[string, unknown?]>' does not match the 'UserActionType' parameter

Using a development environment with React, TypeScript, and connected-react-router. My Intention: I aim to utilize the router action asynchronously within the user action. After successful login, I want the application to navigate to the main screen. Err ...

What steps should I take to address the issue of ERESOLVE being unable to resolve the dependency tree during an

As I delve into the world of Angular and self-teaching, I encountered an issue after importing numerous new components into my project. Upon deleting the node_modules folder and running npm install, I am faced with the following error: npm ERR! ERESOLVE un ...

Navigating through an object's keys to access specific properties of another object in TypeScript

Here is the code snippet provided. You can find the full code here interface DataInterface { a: string[]; b: string[]; c: number[]; d: number[]; e: boolean[]; x: string y: number } const dataObject: DataInterface = { "a": [ ...

What is the process for including a selected-by option in a VIS network graph?

I'm trying to outline the neighboring nodes of the node that has been selected (highlightNearest). https://i.sstatic.net/lynhu.png Unfortunately, I haven't had success achieving this using JavaScript. Link to StackBlitz ...

Leverage AngularCLI to create components for projects containing multiple apps

In my current project setup, the configuration in my .angular-cli.json file is structured as follows: { "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "project": { "name": "suman-chrome-extension" }, "apps": [ { "r ...

Duplicate Execution Issue with ViewChild Function in Angular

In my application, I have three main components: Nav, App, and Form. Within the Nav component, there is a function that changes the position of CSS, which can be called from both the Nav and App components (triggered by the Form component). However, I am f ...