Search code examples
eventsangularobservableangular2-services

Angular2 (rc1) observables do not appear to be working


I want two different components with 2 different routes, I want to pass information from componentA to componentB.

I did the following:

  1. Create a service that handle the event
  2. Set the information at componentA and pass to Service event
  3. Try to get this information at componentB

My service:

@Injectable()
export class TokenService{
private tokenSource = new Subject<Object>();
token$ = this.tokenSource.asObservable();

setToken(token: string) {
  this.tokenSource.next(token);
}

ComponentA:

@Component({
selector: 'login',
templateUrl: './login.html',
providers: [TokenService],
moduleId: module.id
})

export class LoginCmp {
   constructor(private tokenService: TokenService) {
}

someMethod() {
    this.tokenService.setToken('some dynamic value');
}
....

ComponentB:

@Component({
selector: 'cabecalho',
moduleId: module.id,
templateUrl: './cabecalho.html',
providers: [TokenService]
})

constructor(private tokenService: TokenService) {
tokenService.token$.subscribe(
  data => {
    console.log(data);

  });
}

When I set some information from ComponentA my console never print what I put at ComponentB with console.log(data)


Solution

  • Firstly, your components are using different instances of TokenService. You will need to use a shared service between them.

    Secondly, componentB subscribes to the Subject only after it was set in componentA. If you're not subscribed to a Subject at the time that a value is set, then you will not get the value. If you intend to set the value before subscribing, you should rather use a BehaviourSubject. Subscribing to a BehaviourSubject will allow you to receive the last value that was set as well as subsequent values.

    Thirdly, using asObservable implies that you don't want to be able to next the values of tokenSource. However, you have a function that does just that and is accessible from componentB. I would suggest setting tokenSource in a seperate service that will be shared between ComponentA and the tokenService.

    You could try the following approach. Create a shared Service:

    @Injectable()
    export class SharedService {
        tokenSource = new BehaviorSubject<Object>('initial value');
    
        setToken(token: string) {
            this.tokenSource.next(token);
        }
    }
    

    Initialise the shared instance of the SharedService in your AppComponent bootstrapper function.

    bootstrap(AppComponent, [ SharedService ]);
    

    Create a TokenService. Pass the instance of the SharedService to the constructor of your TokenService (Donot add the SharedService to [providers] as it will create a new instance):

    @Injectable()
    export class TokenService {
        token$: any;
        constructor(sharedService: SharedService) {
            this.token$ = sharedService.tokenSource.asObservable();
        }
    

    ComponentA (Again, donot specify SharedService in the [providers]. So Angular2 will search up the hierarchy until it finds the instance of SharedService.)

    @Component({
        selector: 'compA',
        templateUrl: '/compA.html',
    })
    
    export class ComponentA {
        constructor(private sharedService: SharedService){
            console.log('CompA');
            this.sharedService.setToken('some dynamic value');
        }
    }
    

    ComponentB creates a new instance of TokenService.

    @Component({
        selector: 'compB',
        templateUrl: '/compB.html',
        providers: [TokenService]
    })
    
    export class ComponentB implements OnDestroy {
        sub: any;
        constructor(private tokenService: TokenService) {
            console.log('CompB');
            this.sub = tokenService.token$.subscribe(
                data => {
                    console.log(data);
                });
        }
    
        ngOnDestroy() {
            if (this.sub)
                this.sub.unsubscribe();
        }
    }
    

    Thus you have a shared service that exposes your tokenSource values. And a tokenService that returns anObservable sequence, thus hiding the source sequence. And a component(B) that can subscribe to this sequence and get the last value that was set and subsequent values because you're using BehaviourSubject instead of Subject.