indra257
indra257

Reputation: 86

Angular 4/5 : Share data between Angular components

In my angular app, when I login in to the app, I get an access token which I need to use it in my API calls in other components (not parent/child relationship). I tried using Shared service but it's not working

Data Service:

import { Injectable } from '@angular/core';
import { Subject }    from 'rxjs/Subject';

@Injectable()
export class DataService {

  private userInfo = new Subject<any>();
  currentuserInfo$ = this.userInfo.asObservable();

  constructor() { }

  updateUserInfo(message: any) {
    this.userInfo.next(message)
  }

}

In my login component I have

this.dataService.updateUserInfo(data.userToken);

Other Component

this.dataService.currentuserInfo$.subscribe((data: any) => { 
  console.log(data);
});

Here I am unable to get the tocken that was updated with the login component.

In the shared Service I tried both Subject and Behavior Subject but both didn't work.

I made sure that I just mentioned this DataService as a provider only in app.module.ts. I didn't mentioned it in login component or in any other component

Upvotes: 0

Views: 1272

Answers (3)

Ashish Ranjan
Ashish Ranjan

Reputation: 12960

@indira: As @JB Nizet has explained, on a page refresh, your whole application refreshes so whatever in your is contained in your subject, will be lost!

Now, a Simple subject should also have been working in your case, are you subscribing to the Subject inside a function which is definitely called? for example inside ngOnInit()

ngOnInit() {
    this.dataService.currentuserInfo$.subscribe((data) => {
      console.log("data subscribed", data);
    })
}

I tried on my machine and the above works fine.

Why was Subject not working?

A possible reason for the Simple Subject to fail could be: You are just testing the functionality of the Subject and not doing an actual login. And in your scenario, your login component is loading before other components which have actually done the subscription. But wait: If you have already pushed new data to the Subject before it has been subscribed, you won't get the data in the subscription. Only after the next this.dataService.updateUserInfo(data.userToken), you will get the values in the Subscription. If this is the case, then you can definitely use the BehaviorSubject.

As far as saving the state of the Subject is concerned, you have options like: cookies, localstorage.

for example: when you are updating the Subject from your login component, store the data in localstorage:

this.dataService.updateUserInfo(data.userToken)
localStorage.setItem('serviceData', String(data.userToken));

And then apart from listening to the Subject in your other components, you can also take out the value of the token localStorage.getItem('serviceData') upon ngOnInit()

I myself prefer localStorage over cookies. Cookies have a size and a number limit per domain. See this link for more info. Even localStorage has a size limit, but that's huge. Using cookies or localStorage can also be clear from this SO question. You can analyze your requirement and implement either of them.

Upvotes: 0

Dmitry Grinko
Dmitry Grinko

Reputation: 15204

the best way to add token in requests is to use interceptor

import { Injectable, Injector, EventEmitter } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { AuthService } from '../services/auth.service';

@Injectable()
export class HttpsRequestInterceptor implements HttpInterceptor {
    private actionUrl: string;

    constructor(
        private injector: Injector
    ) {
        this.actionUrl = 'https://test.com/dev';
    }


    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const _req = {
            url: this.actionUrl + req.url,
            headers: req.headers.set('Content-Type', 'application/json')
        };

        const user = this.injector.get(AuthService).getAuthenticatedUser();

        if (user) {
            user.getSession((err: any, session: any) => {
                if (session) {
                    const token = session.getIdToken().getJwtToken();
                    _req.headers = req.headers
                        .set('Content-Type', 'application/json')
                        .set('Authorization', token);
                }
            });
        }

        return next.handle(req.clone(_req));
    }
}

UPDATED:

if you need to create a shareable service - you can try to use the behavior subject. Please look at this example

UPDATED2:

if you want to use shareable data in spite of the page refreshing - you should use localstorage:

localStorage.setItem('str', 'whatever string'); // set string
localStorage.setItem('obj', JSON.stringify(obj)); // set object
const str = localStorage.getItem('str'); // get string
const obj = JSON.parse(localStorage.getItem('obj')); // get object
const str = localStorage.removeItem('str'); // remove 

Upvotes: 0

Stefan
Stefan

Reputation: 1511

Can you show how you have been using BahviorSubject and explain what means that this is not working? I think that it should work.

First, you need to declare it somewhere in the service, for example

token: BegaviorSubject<type> = new BehaviorSubject(null);

Then somewhere where you receive that token you need to set next

this.token.next(receivedToken);

And finally, subscribe to it form other components

this.yourService.token.subscribe(token => {
   this.token = token;
}

If you have some errors please show it.

Upvotes: 0

Related Questions