I am new to angular6, Please do not consider this question as duplicated, as i could not able to find relevant answer so far, I have 2 components incident Component<
While, the solution suggested in some of the other answers, namely using a service to broadcast data changes, for example using an RxJS Subject
, can make this work and is at times the most natural approach, it should not be your first choice because it introduces shared, mutable application scoped state.
It should be avoided wherever possible for some of the following reasons:
Using the router, and by implication the URL, to control the rendered components introduces a beneficial decoupling keeping your components cleaner and rendering them independent of their relationship to one another independent of their relative layout in the view tree. This is very advantageous for maintenance and even more so for reusability.
By relying on the changes broadcast through a shared service, you are introducing another source of truth, implicitly relying on the router. That is to say, if the you navigate to the detail route before navigating to the list route, the detail route will not work. In order to work around that, the application becomes much more complex.
The structurally simplest approach is to have both components retrieve their data independently. This downside is additional HTTP requests and loading time but, unless you are working with very large data structures or your server takes a very long time to process the requests, it is often worth it. At the very least, you should try this approach first.
Here is what it would look like
data.service.ts
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {map} from 'rxjs/operators';
import formatIncident from './format-incident';
import Incident from './incident';
@Injectable({providedIn: 'root'}) export class IncidentService {
constructor(http: HttpClient) {
this.http = http;
}
getIncidents() {
return this.http.get('api/incidents')
.pipe(
map(data => data.Events.map(formatIncident)
);
}
getIncident(id: string) {
return this.http.get<Incident>(`api/incidents/${id}`).map(formatIncident);
}
}
format-incident.ts
import Incident from './incident';
export default function formatIncident(eventJSON): Incident {
const unquotedEventJSON = eventJSON.replace(/'/g, '"');
const objectIdIndex = unquotedEventJSON.indexOf('"_id": ObjectId');
const eventJSONSansId = unquotedEventJSON.substr(objectIdIndex, 44);
const incident = JSON.parse(eventJSONSansId);
const id = unquotedEventJSON.substr(0, objectIdIndex);
return {
...incident,
id
};
}
incident.ts
export default interface Incident {
id: string,
Title: string;
Ticket: string;
}
incidents.component.ts
import {Component} from '@angular/core';
import {Observable} from 'rxjs';
@Component({
selector: 'app-incidents',
templateUrl: './incidents.component.html'
})
export class IncidentsComponent {
constructor(router: Router, incidentService: IncidentService) {
this.router = router;
this.incidentService = incidentService;
}
ngOnInit() {
this.incidents$ = this.incidentService.getIncidents();
}
incidents$?: Observable<Incident>;
}
incidents.component.html
<div class="card" *ngFor="let incident of incidents$ | async">
<div class="card-header">
<span class="badge">A</span><small>{{incident.Title}}</small>
<span class="badge">A</span><small>{{incident.Ticket}}</small>
<div class="card-footer">
<a class="btn btn-sm btn-link"
routeLink="/incidents/{{incident.id}}"
[routerLinkActive]="router.url.match(incident.id)">View in Detail</button>
</div>
</div>
</div>
incident-detail.component.ts
import {Component} from '@angular/core';
import {map} from 'rxjs/operators';
@Component({
selector: 'app-incident-detail',
templateUrl: './incident-detail.component.html'
})
export class IncidentDetailComponent {
constructor(route: ActivatedRoute, incidentService: IncidentService) {
route.paramMap.get('incidentId')
.pipe(
map(id => incidentService.getIncident(id))
)
.subscribe(incident => {
this.incident = incident;
});
}
incident?: Incident;
}
incident-detail.component.html
<div class="card">
<div class="card-header">
<span class="badge">A</span><small>{{incident?.Title}}</small>
<span class="badge">A</span><small>{{incident?.Ticket}}</small>
</div>
</div>
app.module.ts
import {NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
import {HttpClientModule} from '@angular/common/http';
import {CommonModule} from '@angular/common';
@NgModule({
components: [IncidentsComponent, IncidentDetailComponent],
imports: [
CommonModule,
HttpClientModule,
RouterModule.forRoot([
{path: 'incidents', component: IncidentsComponent},
{path: 'incidents/incidentId', component: IncidentDetailComponent}
])
]
}) export class AppModule {}
This is a bog standard approach and works well for many situations but there a few for which it is a not a great fit.
Create a data sharing service first, here's sample code, you should modify according to your need
Data sharing service
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
@Injectable()
export class DataSharingService {
private title = new BehaviorSubject<string>("");
currentTitle = this.title.asObservable();
setTitle(txt: string){
this.title.next(txt);
}
}
inject this service into your incident component
Data sender component
constructor(
private dataSharingService: DataSharingService
) { }
use this service where you want in your incident component before navigation
this.dataSharingService.setTitle("Your title or data");
Now you surely want to receive data into your incident detail component, for that here's sample code
Data receiver component
title: string;
constructor(private dataSharingService: DataSharingService ) { }
ngOnInit() {
this.dataSharingService.currentTitle.subscribe(data => {
this.title = data;
})
}
Note: Please modify according to your requirement, this service I made to set and get string
title. This service is useful for independent components, without any child-parent relationship.
We can use the Decorators for making a communication between parent and child components. @Input and @Output are parts of the @angular/core. The @Input decorator is for obtaining the data that is passed from a component. @Output can pass data back from child to parent
when your incident-detail-component is a child element of your incident-component, you can simply pass the data in as an input variable. Take a look here: https://angular.io/api/core/Input