2

In my Angular application I have an API which returns the settings of the footer and the header of the web-site. However I want to call that API only once and use the data for both the header and the footer. There's no direct parent of the header and footer components so I can't just call it from the parent component and pass the data to the child components. As I understand, the correct way of doing this is a service. I've tried to do it as it was described here but this doesn't seem to work. So, my idea is that I need to have a dedicated place which will call the HTTP API, get the data and then the other components can just use that service (inject it) to get the data, all in one place.

Any clues how this can be implemented or what's wrong with the implementation in the topic referenced?

The code which doesn't work looks like this:

import { Observable } from 'rxjs/Observable';

@Injectable()
export class PersonService {
    // The person subject
    personStream: ReplaySubject<Person> = new ReplaySubject();

    // The person observable
    person$(): Observable<Person> {
        return this.personStream.asObservable();
    }

    // Get person from DB and add to stream
    getDataFromDB() {
        this.http.get(url).subscribe(response => {
            this.personStream.next(response.person);
        });
    }
}

@Component({...})
export class MyComponent implements OnInit {
    person: Person;

    constructor(private personService: PersonService) {}

    ngOnInit() {
        // Subscribe to person observable. Any time person data changes, this will be called.
        this.personService.person$().subscribe(person => this.person = person);

        // If you return the `this.http.get(url)` from `getDataFromDB`, you can just do this instead...
        this.personService.getDataFromDB().subscribe(person => this.person = person);
    }
}

Thanks!

cycero
  • 4,547
  • 20
  • 53
  • 78

3 Answers3

0

Either you can create Angular Service or you can use any state management library (NgRx) to store data and then share it in multiple pages. Please attach your code snippet (better upload your code to stackblitz) here so that issue can be resolved.

Suneet Bansal
  • 2,664
  • 1
  • 14
  • 18
0

The example below is one way of achieving what you're looking for.

Consider the following generalized object that may be returned by your settings API:

{
  // Settings for the header.
  "header": { }
  // Settings for the footer.
  "footer": { }
}

The Angular service to retrieve these settings may look as follows:

import { shareReplay } from 'rxjs/operators';


@Injectable({ providedIn: 'root' })
export class SettingsService {

  /**
   * Observable that emits app settings. 
   */
  readonly app$ = this.httpClient.get('url/to/settings/api').pipe(
    // The RxJs shareReplay operator ensures that the backend API is
    // only called once, and any subsequent subscription to app$ will
    // receive the value cached by shareReplay.
    shareReplay(1)
  );

  constructor(private httpClient: HttpClient) { }
}

The header component can then extract whatever it needs from the settings using the RxJs map operator.

import { map} from 'rxjs/operators';

@Component({ /* ... */ })
export class HeaderComponent {

  /**
   * Observable that emits the header settings.
   */
  settings$ = this.settingsService.app$.pipe(
    // Extract the header settings from the app settings.
    map(app => app.header)
  );

  constructor(private settingsService: SettingsService) { }
} 

The component template can subscribe to the settings$ Observable using the Angular async pipe:

<ng-container *ngIf="(settings$ | async) as settings">
  {{ settings.whatever }}
</ng-container>

Do the same for the footer or any other component that needs information from the SettingsService.

Sam Herrmann
  • 6,293
  • 4
  • 31
  • 50
0

BehaviourSubject can be used to post the messages / data across the components.

SharedService.ts

import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class SharedService {
    settingsNotify: BehaviorSubject <Settings> = new BehaviorSubject(null);

    settings$(): Observable<Settings> {
        return this.personStream.asObservable();
    }

    settingsCall() {
        this.http.get(url).subscribe(response => {
            this.shareValue.next(response);
        });
    }
}

AppComponent.ts

import { SharedService } from 'SharedService';
@component({})
export class AppComponent {
    constructor(private sharedService: SharedService) {}
    ngOnInit() {
       this.sharedService.apiCall();
    }
}

HeaderComponent.ts

export class HeaderComponent {
    constructor(private sharedService: SharedService) {}
    ngOnInit() {
        this.sharedService.settings$().subscribe((response) => {
          
        });
    }
}

FooterComponent.ts

export class FooterComponent {
    constructor(private sharedService: SharedService) {}
    ngOnInit() {
        this.sharedService.settings$().subscribe((response) => {
          
        });
    }
}

Please refer the document for BehaviourSubject

Karthikeyan
  • 183
  • 1
  • 8