1

Alright, this question is probably asked quit alot, but I am still going to ask.

I am new to Typescript/Angular 2. I have two pages, lets say Page A and Page B On page A i use api A to request data. After retreiving the data, the user can click on a link/button and he/she switches to page B through routing, page B uses data specific from page A to request data from api B. Now the API stuff is working fine. But I just can't seem to get data from page A to page B.

Now I have read that Parent/Child/Sibling uses a injectable service with a obeservable and I tried that. But it doesn't work... at all.

I made sure that the providers are set correctly and that the injectable service is instantiated only once.

Is their a written rule/way to go about this? My situation isn't really Parent/Child related. But kinda is.

Basically this is what I did: Service:

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

@Injectable()
export class ShareService {

   public data : Subject<string> = new BehaviorSubject<string>(null);

   constructor(){
   } 
   setData(newdata : string){
       this.data.next(newdata);
   }

   clearData(){
       this.data.next();
   }
}

Page A:

import {Component, OnInit, OnDestroy} from '@angular/core';
import {ShareService} from '../../services/share.service'
import {Subject} from 'rxjs/Rx';
import 'rxjs/add/observable/of'; //proper way to import the 'of' operator
import 'rxjs/add/operator/share';
import 'rxjs/add/operator/map';
@Component({
    selector: 'pagea',
    template: `
        <button (click)="gotoPageB()" class="item" > click me </button>
`
})
export class CurrentlyAiring {
    constructor(private shareService: ShareService, private router: Router){
    }
    gotoPageB(){
        this.shareService.setData("Normally I would send a json string here!");
        this.router.navigate(['/pageb']);  
    }

}

Page B:

import {Component, OnInit, OnDestroy} from '@angular/core';
import {Router} from '@angular/router';
import {ShareService} from '../../services/share.service'
import {Subject} from 'rxjs/Rx';
import 'rxjs/add/observable/of'; //proper way to import the 'of' operator
import 'rxjs/add/operator/share';
import 'rxjs/add/operator/map';
@Component({
    selector: 'pageb',
    template: `
        <button  class="item" > {{somedata}} </button>
`
})
export class CurrentlyAiring {
    somedata : string;
    constructor(private shareService: ShareService){
        this.packlistobserver = this.shareService.data.subscribe((data) => {
            if(json !== null){
                this.somedata = data;
            } 
        });
    }

}

I only put the necessary code here which should work in my opinion, but I might have left out something important! I just didn't want to put the whole code here. But basically what is above here is what I am trying to pull of.

At one point I was just giving up and started using localStorage to share data, but I didn't really like it, since I can't imagine that there isn't a Angular 2/typescript way to do it without the use of localStorage.

Thanks in advance for all the help you can give this noob ;).

EDIT: https://stackblitz.com/edit/angular-p2ucdh

It works here, I must have made a grave mistake in my actual application :(. At this point I think I might rewrite the whole damn thing.

Actual project (with horrible code): https://github.com/EldinZenderink/LittleWeebTS/

  • 1
    Overall, this looks good - a `BehaviorSubject` with a singleton service (i.e. instantiated only once, by being provided at the top level of your app) is the right approach in sharing data between components. And since a `BehaviorSubject` will always return a value on the first subscribe, your `PageB` *should* be receiving a non-null value. If you'd be able to recreate a version of this in [StackBlitz](https://stackblitz.com/fork/angular), I think we'd be able to diagnose the issue quite quickly. Otherwise, perhaps someone else will immediately notice something I'm missing – Jack Koppa Oct 16 '17 at 20:47
  • Short of recreating in StackBlitz, another first step would be to create a simple public variable in your service, set the variable in `PageA`, and then run a console.log of that service variable in `PageB` on init. Just to make sure they're sharing the same service instance, in which case we could move on to diagnosing whatever the issue is with the `Subject` – Jack Koppa Oct 16 '17 at 20:50
  • 1
    As said by Jack Koppa, i created a stackblitz: https://stackblitz.com/edit/angular-p2ucdh , and it works there.... apparently I must be doing something stupid in my main application, probably will rewrite fully (i really need to as I am using a css framework that only works with jQuery... bad practice something something... ) – eldin zenderink Oct 16 '17 at 21:12
  • Awesome! This actually looks perfect, and so far, seems to be working as expected for me. It loads, I get a button saying "click me", and upon click, I'm brought to the `pageb` route, and I see "**HELLO: Normally I would send a json string here!**" Is it working that way for you? – Jack Koppa Oct 16 '17 at 21:15
  • 1
    yep see my previous answer, anyway thanks for your help :D, and for pointing out stackblitz to me, thats going to be helpfull in the future for sure! – eldin zenderink Oct 16 '17 at 21:16
  • Ah, sorry, missed the edit :( Do you happen to have any console errors in your application, or any other missing functionality, rather than just this data sharing? The simple setup you provided in StackBlitz looks great, in terms of providers, declarations, & imports – Jack Koppa Oct 16 '17 at 21:18
  • Nope, appart from a connection error to my backend (completely unrelated to this issue) since backend isn't running: https://imgur.com/0RlgiFV (packlist is pageb in this case, currentlyairing page a), sorry I am a bit short on time today, I need to catch my sleep. I will be back tomorrow, thanks for thinking with me! – eldin zenderink Oct 16 '17 at 21:20
  • Hmmm... yeah, nothing comes to mind from that console. If you want to stick with your current app, I'd recommend comparing functionality you have working in StackBlitz to your app, and seeing which parts are breaking & which are working in your local app. Feel free to message me on chat with other questions. A final thought is that you can actually do some initial development in StackBlitz, then export, run an `npm install`, and continue development locally using `ng serve` like usual. Wouldn't go *too* far on StackBlitz, to be safe, but it's a great tool – Jack Koppa Oct 16 '17 at 21:23
  • Thanks :), i gotta catch my sleep now, I added the actual project git to the main post, but it's not nice to read. There is some stupid thinking involved as well. But I will try to compare the difference tomorrow! – eldin zenderink Oct 16 '17 at 21:25
  • So @JackKoppa ... I just don't get the need for the BehaviorSubject. What is it really providing in this case? I redid the above code without it and it works fine. I have it here: https://stackblitz.com/edit/angular-spfkgs So far every example with BehaviorSubject could be rewritten without it. It would be great to better understand when it is *really* needed. – DeborahK Oct 17 '17 at 03:53
  • Yup, @DeborahK, that code definitely works perfectly well in this case. The `BehaviorSubject` (& from what I've seen, RxJS in general) is most useful when 2+ components start to depend on having the most up-to-date data. Which is why for `ShareService` here, it might make sense to use a `BehaviorSubject` from the beginning. For instance, if we imagine a persistent sidebar component, `Sidebar`, that should change whenever `ShareService` updates, we can subscribe to `ShareService.data` there as well, and know that whenever data updates, all subscribed components will get the newest info – Jack Koppa Oct 17 '17 at 12:00
  • (a `BehaviorSubject` over other reactive components here in order to set an initial value, and to guarantee that subscribing components will always receive a value on their first subscribe; i.e. it's already "hot" when initialized) – Jack Koppa Oct 17 '17 at 12:01
  • It seems a simple getter in each component would achieve the same level of sharing? – DeborahK Oct 17 '17 at 15:21

1 Answers1

1

I would use an EventEmitter as follow:

Page B

@Component({
    selector: 'pageb',
    template: `
        <button  class="item" > {{somedata}} </button>
`
})
export class CurrentlyAiring implements OnInit {
    somedata : string;
    constructor(private shareService: ShareService){ }

    ngOnInit() {
        this.shareService.dataEmitter.subscribe(
            (data:string) => this.somedata = data
        );
    }
}

Page A

export class CurrentlyAiring  {

    constructor(private shareService: ShareService, private router: Router){
    }

    gotoPageB(){
        this.shareService.setData("Normally I would send a json string here!");
        this.router.navigate(['/pageb']);  
    }

}

Service Class

@Injectable()
export class ShareService {

   public dataEmitter = new EventEmitter<string>();

   constructor(){
   } 
   setData(newdata : string){
       this.dataEmitter.emit(newdata);

   }
}
Muhammed Misir
  • 400
  • 9
  • 22