I'm trying to send data between two components using a service. However, I'm having trouble with the data populating when the 'child' component first loads.
When you select a 'release' from the unordered list on the release-list.component, it will load the release-detail.component through a router-outlet. The release-detail.component should show the release artist.
It works if you select a release and then select a release for a second time. But I'm having to 'load' the release-detail.component twice before the data is populated. I feel I'm close but I must be missing something. Any help would be greatly appreciated.
Thanks
I've referred to both a previous question and the Angular documentation
release.service.ts
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { MockReleaseList, ReleaseInterface } from './mock-releases';
@Injectable()
export class ReleaseService {
private releaseSubject$ = new Subject();
getReleases(): ReleaseInterface[] {
return MockReleaseList;
}
getRelease() {
return this.releaseSubject$;
}
updateRelease(release: {artist: string, title: string, category: string}[]) {
// console.log(release);
this.releaseSubject$.next(release);
}
}
release-list.component.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Location } from '@angular/common';
import { ReleaseService } from '../../../shared/services/release.service';
import { MockReleaseList, ReleaseInterface } from '../../../shared/services/mock-releases';
@Component({
selector: 'ostgut-releases',
template: `
<h3>Label List</h3>
<ul>
<li *ngFor="let release of releases" (click)="onSelect(release)">{{ release.artist }}</li>
</ul>
<router-outlet></router-outlet>
`,
})
export class ReleaseListComponent implements OnInit {
private releases: ReleaseInterface[];
constructor (
private _router: Router,
private _releaseService: ReleaseService
) {}
ngOnInit(): ReleaseInterface[] {
return this.releases = this._releaseService.getReleases();
}
onSelect(release: any): void {
this._releaseService.updateRelease(release);
this._router.navigate(['/release-list/release-detail', release.category]);
}
}
release-detail.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { ReleaseService } from '../../../../shared/services/release.service';
import { ReleaseInterface } from '../../../../shared/services/mock-releases';
@Component({
selector: 'ostgut-release-detail',
template: `
<h3>Release Detail</h3>
<p>{{ release.artist }}</p>
`,
})
export class ReleaseDetailComponent implements OnInit {
private routeParam: string;
private routeParamSub: any;
private release: any;
constructor(
private _activatedRoute: ActivatedRoute,
private _router: Router,
private _releaseService: ReleaseService
) {}
ngOnInit(): void {
this.release = this._releaseService.getRelease().subscribe(release=>{
return this.release = release;
});
}
}
I think the problem could be that your Subject
publishes its value before you are subscribing to it inside your ReleaseDetailComponent
. When you do it the second time, a new Release
is published, and the ReleaseDetailComponent
is already created - meaning it will work.
To fix this, you can try to use a BehaviorSubject
instead of a regular Subject
in your service. The BehaviorSubject
will give you the latest published value even if you subscribe to it after the last value was published. It also expects a default value though, so you might want to add some null-checking in your ReleaseDetailComponent
template.
An implementation could look something like this:
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject'; // <-- note the import!
import { MockReleaseList, ReleaseInterface } from './mock-releases';
@Injectable()
export class ReleaseService {
private releaseSubject$ = new BehaviorSubject(undefined);
// The rest is the same
}
And in your template, att null checking, as the default value we set to the BehaviorSubject
is undefined
. Of course, you can set the default value (the values passed into the constructor of the BehaviorSubject
) to an empty Release
or something else if you wish.
template: `
<h3>Release Detail</h3>
<p>{{ release?.artist }}</p> <!-- Note the '?' -->
`,
Here are the docs if you want to learn more about the different types of Subjects
:
I hope this helps!