Angular component loading before resolve service

1.5k views Asked by At

I am trying to load application level variables using API from the backend which will be used throughout the application. For the same I have using resolver but still that service is not getting loaded before any component is loaded, here is my code:

pub.global.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { CustomUrlBaseService } from './auth/customurlbase';
import { Observable, Subject } from 'rxjs';
import { Resolve } from '@angular/router';

@Injectable()
export class PubService extends CustomUrlBaseService implements Resolve<any> {
    appRoot = '/api/pub';
    StoreData = '';
    private loadPub = new Subject<any>();
    loadPubObservable$ = this.loadPub.asObservable();
    constructor(private http: HttpClient) {
      super();
    }
    resolve() {
      this.GetStoreData();
    }
    GetStoreData() {
      this.GetData().subscribe(res => {
        this.StoreData = res;
        console.log(res);
      })
    }
   GetData(): Observable<any> {
    const url = (`${this.baseURL + this.appRoot}/GetStoreData`);
    return this.http.get(url);
  }
}

pub-footer.component.ts

import { PubService } from './../../services/pub.global.service';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-pub-footer',
  templateUrl: './pub-footer.component.html',
  styleUrls: ['./pub-footer.component.scss']
})
export class PubFooterComponent implements OnInit {
  copyrightinfo;
  constructor(private pubService: PubService) { }

  ngOnInit() {
    console.log(this.pubService.StoreData);
    const FooterData = this.pubService.StoreData;
    this.copyrightinfo = FooterData['copyrightinfo'];
  }
}

app.routing.ts

{
    path: '',
    component: PubLayoutComponent,
    loadChildren: () => import('./views/pub/pub.module').then(m => m.PubModule),
    data: { title: '' },
    resolve: { items: PubService }
  },

I also tried app_initializer, now my api is getting called twice but not any before component is loaded: In my module.ts

PubService, {
      provide: APP_INITIALIZER, useFactory: initApp, deps: [PubService], multi: true
    }
export function initApp(initService: PubService) {
  return () => {
    initService.GetStoreData();
  }
}

My overall idea is to load Header and Footer information in just one api call and store in service variable and then get access of this json object from header and footer component. I even doubt the approach is correct.

2

There are 2 answers

0
Tony On

Change Subject to BehaviorSubject

BehaviorSubject

2
Cueball 6118 On

You can try this kind of setup.

First centralised Repository Service so you can control headers, paths and security (you can also have POST, PUT, DELETE in here if you want): -

repository.service.ts

@Injectable({
  providedIn: 'root'
})
export class RepositoryService {
  constructor(private http: HttpClient) { }

  public getData = (route: string) => {
    return this.http.get(this.createCompleteRoute(route, environment.endPoint), this.generateHeaders());
  }
  private createCompleteRoute = (route: string, envAddress: string) => {
    return `${envAddress}/${route}`;
  }

  private generateHeaders = () => {
    return {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'GET, POST, PATCH, PUT, DELETE, OPTIONS',
        'Access-Control-Allow-Headers': 'Origin, Content-Type, X-Auth-Token'
      })
    };
  }
}

Then a service which gets and sets the data: -

store-config.service.ts

@Injectable()
export class StoreConfigService {
    protected config: StoreConfig;
    constructor(private repoService: RepositoryService) { }

    public load(): Observable<StoreConfig> {
        return new Observable(observer => {
            if (this.config) {
                observer.next(this.config);
                observer.complete();
            } else {        
               this.repoService.getData(`<url here>`)
                        .subscribe(res => {
                            this.config = res as StoreConfig;
                            observer.next(this.config as StoreConfig);
                            observer.complete();
                        });
                }
            }
        });
    }
}

Finally tie it together in your component (or where ever you want access to the Store Data which will only call the actual API once per visit so you can call it as many times as you want with little overhead): -

constructor(public storeConfigService: StoreConfigService) { 
   // Here or in onInit ... or where ever
    this.storeConfigService.load()
    .subscribe(res => { <Do your stuff here> });
}