I have a MasterComponent which loads header, footer, sidebar etc. On the header there is a dropdown whose options are set once the user is logged in. I want the header to be constant even if I navigate to different routes which loads different child component. Means that the selected option should not change and value of dropdown should be accessible in all the child component. Onchange of dropdown value the current child component should be updated/reloaded.
How should I approach this problem? I want to event-listener kind of functionality. Once the model from MasterComponent Changes , reload the current child component. On MasterComponent's variable variable update ChildComponent will listen to the update and run some function or call some API again and reload the ChildComponent.
// routes
const appRoutes: Routes = [
{
path: '',
redirectTo: 'login',
pathMatch: 'full',
},
{ path: 'login', component: LoginComponent },
{ path: 'logout', component: LogoutComponent },
{
path: '',
component: MasterComponent,
canActivate: [AuthGuard],
children: [
{ path: 'record/create', component: RecordCreateComponent }, // create record for selectedRestaurant in MasterComponent
{ path: 'record/', component: RecordComponent }, // shows all record of current selectedRestaurant in MasterComponent
{ path: 'record/:id/update', component:RecordUpdateComponent }, // show form to edit record having id
{ path: 'record/:id', component: RecordComponent }, // show record details having id
]
},
{ path: '**', redirectTo: 'login' }
];
// MasterComponent
@Component({
selector: 'master',
templateUrl: templateUrl,
styleUrls:[styleUrl1]
})
export class MasterComponent implements AfterViewInit, OnInit{
restaurants: Array<Restaurant> = [];
user:User;
selectedRestaurant: Restaurant;
constructor(private router: Router, private storageHelper:StorageHelper){
}
ngAfterViewInit() {
}
ngOnInit(){
this.user = JSON.parse(this.storageHelper.getItem('user'));
this.restaurants = this.user.restaurants;
this.selectedRestaurant = this.restaurants[0];
this.router.navigate(['/record/' + this.selectedRestaurant.id]);
}
onRestaurantChange(){
this.router.navigate(['/record/' + this.selectedRestaurant.id]);
}
createRecord(){
}
}
This question is related to
angular
update of @Vladimir Tolstikov's answer
Create a Child Component that use ngOnChanges
.
ChildComponent.ts::
import { Component, OnChanges, Input } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'child',
templateUrl: 'child.component.html',
})
export class ChildComponent implements OnChanges {
@Input() child_id;
constructor(private route: ActivatedRoute) { }
ngOnChanges() {
// create header using child_id
console.log(this.child_id);
}
}
now use it in MasterComponent's template and pass data to ChildComponent like:
<child [child_id]="child_id"></child>
In case, when we have no control over child component, like a 3rd party library component.
We can use *ngIf
and setTimeout
to reset the child component from parent without making any change in child component.
.template:
.ts:
show:boolean = true
resetChildForm(){
this.show = false;
setTimeout(() => {
this.show = true
}, 100);
}
You can use @input with ngOnChanges, to see the changes when it happened.
reference: https://angular.io/api/core/OnChanges
(or)
If you want to pass data between multiple component or routes then go with Rxjs way.
Service.ts
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class MessageService {
private subject = new Subject<any>();
sendMessage(message: string) {
this.subject.next({ text: message });
}
clearMessages() {
this.subject.next();
}
getMessage(): Observable<any> {
return this.subject.asObservable();
}
}
Component.ts
import { Component, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { MessageService } from './_services/index';
@Component({
selector: 'app',
templateUrl: 'app.component.html'
})
export class AppComponent implements OnDestroy {
messages: any[] = [];
subscription: Subscription;
constructor(private messageService: MessageService) {
// subscribe to home component messages
this.subscription = this.messageService.getMessage().subscribe(message => {
if (message) {
this.messages.push(message);
} else {
// clear messages when empty message received
this.messages = [];
}
});
}
ngOnDestroy() {
// unsubscribe to ensure no memory leaks
this.subscription.unsubscribe();
}
}
On Angular to update a component including its template, there is a straight forward solution to this, having an @Input
property on your ChildComponent and add to your @Component
decorator changeDetection: ChangeDetectionStrategy.OnPush
as follows:
import { ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'master',
templateUrl: templateUrl,
styleUrls:[styleUrl1],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent{
@Input() data: MyData;
}
This will do all the work of check if Input data have changed and re-render the component
Source: Stackoverflow.com