1

I am having difficulties in accessing service data within ActivationEnd router service.

My TS:

export class ListConditionsComponent implements OnInit {
  conditions: Condition[];
  fCon: string[];

  constructor(private _service: ConditionService,
    private _router: Router,
    private _route: ActivatedRoute) { }

  ngOnInit():void {
    this._service.getConditions()
      .subscribe(data => this.conditions = data);

    this._router.events.subscribe(event => {
      if(event instanceof NavigationStart) {  
      }

      if(event instanceof ActivationEnd) {
        this.fCon = event.snapshot.queryParams["fCon"];        
        console.log("actiend fCon:" + this.fCon);
        console.log("actiend conditions:" + this.conditions);
      }
    });
  }
 }

Template:

<ul>
    <li *ngFor="let condition of conditions; let i = index">
        <label>
            <input type="checkbox" value="{{ condition.Id }}" *ngIf="fCon == condition.Id" checked />
            <input type="checkbox" value="{{ condition.Id }}" *ngIf="fCon != condition.Id" />
            <span>{{ condition.Name }}</span>
        </label>
    </li>
</ul>

My template being filled without any problem.
But within the TS the console.log says "actiend conditions:undefined".
I can without problem read the fCon variable, but only the conditions variable is being shown as undefined. I don't know why I cant access the "conditions" within my ActivationEnd event.

Anybody who knows why? thx.

Note:
In case you wonder, why I am accessing queryparams like this is, this is being done in a component which is not being loaded in [router-outlet], therefore I dont have access to the traditional way of accessing queryparams.

AFTER SEVERAL HOURS OF HEADACHE I FOUND THIS WORKS FOR ME

ngOnInit(): void {
  this._router.events.subscribe(event => {
    if (event instanceof ActivationEnd) {
      this.getAllConditions();
    }
  });
}

async getAllConditions() {
  this.conditions = await this._service.getConditions().toPromise();
  //from here I can continue as the data will be loaded from hereon
  console.log(this.conditions.length); //prints fine the length as the data is loaded
}

thx again to @Kurt Hamiton for pointing about the asynchronous loading and Im sure that your code will be useful for someone also, thats why Im marking yours as answer

Danny Web
  • 217
  • 5
  • 17
  • You may need to nest your conditions subscription inside the router subscription. – Tom Shaw Mar 01 '20 at 05:20
  • Create promises out of both subscriptions then use promise.all. – Tom Shaw Mar 01 '20 at 05:23
  • @TomShaw I have tried conditons subscriptions within router subscription and ActivationEnd, still no luck. but the template(html) is being filled correctly. how to create promise out of this code. pls. help – Danny Web Mar 01 '20 at 05:25
  • Just to make sure both services shoutd stay observable, as the data will be changed dynamically and in case I need to get the new values. – Danny Web Mar 01 '20 at 05:28
  • Found this https://stackoverflow.com/questions/41734921/rxjs-wait-for-all-observables-in-an-array-to-complete-or-error – Tom Shaw Mar 01 '20 at 05:31

1 Answers1

0

Each request is asynchronous, so you need to synchronise the calls by chaining the observables. If your service call depended on your route, you would append your service call to the route call. In your case the service call doesn't depend on the route, so should come first.

export class ListConditionsComponent implements OnInit {
  conditions: Condition[];
  fCon: string[];

  constructor(private _service: ConditionService,
    private _router: Router,
    private _route: ActivatedRoute) { }

  ngOnInit():void {
    // first, get the conditions from the service... 
    this._service.getConditions().pipe(
      // now save them to the "conditions" property
      tap(data => this.conditions = data),
      // now switch to a different observable
      concatMap(() => this._router.events)
    ).subscribe(events => {
      // subscribe will receive whatever the last observable in the chain is emitting
      if(event instanceof NavigationStart) {  
      }

      if(event instanceof ActivationEnd) {
        this.fCon = event.snapshot.queryParams["fCon"];        
        console.log("actiend fCon:" + this.fCon);
        console.log("actiend conditions:" + this.conditions);
      }
    });
  }
}

Observables are chained together, and processed in the pipe. In your case, you want to get data from the service, store it in the component, and then receive all router events.

Kurt Hamilton
  • 12,490
  • 1
  • 24
  • 40
  • Kurt Hamilton: Thank you very much for the explanation of why the request data is undefined 'cos of the asynchronous call. I get error on line 11 for "pip" - this._service.getConditions.pipe( and 14 and 16 for both tap and concatMap. Any special imports required? I can see you have this._service.getConditions.pipe( within another same getCondition call. Is that correct? – Danny Web Mar 02 '20 at 00:05
  • I needed implement: import { tap, concatMap } from 'rxjs/operators'; now the tap and concatMap errors gone, but still the first pipe gives an error: Property 'pipe' does not exist on type '() => Observable'.ts(2339). If I remove the first pipe TS runs fine, but nothing happens within the ActivationEnd block. seems like the script doesnt enter that area at all even simple console log doesnt work there :( – Danny Web Mar 02 '20 at 00:22
  • I have removed the nested pipes now only got 1 pipe and changed ").subscribe(events => {" to "event", cos Im refering to "event" instance. stil nothing happens. even a simple console.log("actiend ActivationEnd entered") in the first line after if(event instanceof ActivationEnd) { is not working. something wrong somewhere :( – Danny Web Mar 02 '20 at 00:37
  • Sorry - that was a typo on my part. That wasn't valid syntax for nesting observables. Hopefully you have more luck with my edited post. – Kurt Hamilton Mar 02 '20 at 06:44
  • 1
    Kurt Hamilton: I couldnt get your solution to work, but I found another way to solve it. Edited the post with the method I chosen to go with. Your contribute is welcome as your have pointed out the main issue. thx again man! – Danny Web Mar 04 '20 at 01:34
  • @user2160310 Resorting to converting an observable to a promise should never be the solution - there must have been something else you weren't quite getting right. Oh well, whatever works for you for now! – Kurt Hamilton Mar 04 '20 at 06:49