Reputation: 643
I'm a beginner in Angular and I developed an Angular 4/5 application consisting of 5 components: A, B, C, D, and E. All these components are displayed on the single (same) page. The component A consist of a dropdown list in the navigation bar. Now, what I've to do is that after selecting any particular option in the dropdown list of the component A, I've to change the data of other components B, C, D, and E simultaneously.
I'm extremely confused as how to achieve that kind of data binding. Can anyone help on how to achieve this?
Since posting the whole code will be an extremely difficult task here, since I have about 2 dozen files so, I 've posted the code for component A, component B, and the root component.
component A code, where component A is navigation bar
navigation-bar.component.html:
<div class="container model-family-navigation-bar">
<nav class="navbar navbar-expand-sm navbar-dark model-family-navigation-bar">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav">
<!-- <div class="col-sm-auto"> -->
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle navigation-bar-content text-white" href="#" id="navbarDropdown" role="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<span id="selected">Model Family</span>
</a>
<div class="dropdown-menu bg-success table-striped" aria-labelledby="navbarDropdown">
<h1 class="dropdown-header text-white">Select Model Family</h1>
<div *ngFor="let item of dropdownElements">
<div class="dropdown-divider"></div>
<a class="dropdown-item" (click)="showList()">{{item.model_family}}</a>
</div>
</div>
</li>
<!-- </div> -->
<!-- <div class="col-sm-auto"> -->
<li class="nav-item">
<span class="navigation-bar-content navbar-text text-white ml-4">Proportional Directional Valve</span>
</li>
<!-- </div> -->
</ul>
<ul class="navbar-nav ml-auto">
<li class="nav-item navbar-right">
<a class="nav-link text-white" href="https://www.google.com/">Data Sheet</a>
</li>
</ul>
</div>
</nav>
</div>
navigation-bar.component.ts:
import { Component, OnInit } from '@angular/core';
import {NavigationDropdownElements} from './navigation-bar-dropdownElements'
@Component({
selector: 'navigation-bar',
templateUrl: './navigation-bar.component.html',
styleUrls: ['./navigation-bar.component.css']
})
export class NavigationBarComponent implements OnInit {
dropdownElements = NavigationDropdownElements;
ngOnInit() {}
}
navigation-bar-dropdownElements.ts
class NavigationDropdown{
model_family: string;
}
export const NavigationDropdownElements: NavigationDropdown[] = [
{ model_family: 'CP210-1'},
{ model_family: 'CP211-2'},
{ model_family: 'CP212-3'}
];
component B code, where component B is model-family-description
model-family-description.component.ts:
import { Component, OnInit } from '@angular/core';
import { descriptionElements1, descriptionElements2 } from './model-family-description-elements';
@Component({
selector: 'model-family-description',
templateUrl: './model-family-description.component.html',
styleUrls: ['./model-family-description.component.css']
})
export class ModelFamilyDescriptionComponent implements OnInit {
desc1 = descriptionElements1;
desc2 = descriptionElements2;
constructor() { }
ngOnInit() {
}
}
model-family-description.component.html
<div class="container border-section">
<div class="row">
<p class="ml-2">{{desc2.description}}</p>
</div>
</div>
model-family-description-elements.ts
class ModelFamilyDescription {
description: string;
}
export const descriptionElements1: ModelFamilyDescription =
{ description: 'This is a proportional, 3 position 4 way, directional control valve.' };
export const descriptionElements2: ModelFamilyDescription =
{ description: 'This is a proportional, 4 position 5 way, non-directional control valve.' };
root component:
app.component.html:
<navigation-bar></navigation-bar>
<model-family-description></model-family-description>
app.component.ts:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
}
Please help.
Upvotes: 1
Views: 5581
Reputation: 1263
You need to emit drop-down option changes from component A
Then receive the changes from component B and C with the setter method. Setter method always get called during value update.
Check Here: https://stackblitz.com/edit/angular-dshm6g
You can also use observable pattern from rxJs to solve this kind of problem.
Upvotes: 1
Reputation: 9800
First create a service:
For example:
message.service.ts
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class MessageService {
private subject = new Subject<any>();
sendMessage(message: string) {
this.subject.next({ text: message });
}
clearMessage() {
this.subject.next();
}
getMessage(): Observable<any> {
return this.subject.asObservable();
}
}
Then add that service to app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { MessageService } from './message.service';
@NgModule({
imports: [
BrowserModule,
FormsModule,
ModalModule.forRoot()
],
declarations: [
AppComponent
],
providers: [
MessageService
],
bootstrap: [
AppComponent
]
})
export class AppModule { }
Now you need to import the service in the component where you want to send the data from (component A in your case)
something like:
import { Component, OnInit } from '@angular/core';
import {NavigationDropdownElements} from './navigation-bar-dropdownElements'
import { MessageService } from './message.service';
@Component({
selector: 'navigation-bar',
templateUrl: './navigation-bar.component.html',
styleUrls: ['./navigation-bar.component.css']
})
export class NavigationBarComponent implements OnInit {
dropdownElements = NavigationDropdownElements;
constructor(private messageService: MessageService) {}
sendMessage(data:string): void {
// send message to subscribers via observable subject
this.messageService.sendMessage(data);
}
showList() {
//on a click, you send the message here
let message:string="data example";
this.sendMessage(message);
}
clearMessage(): void {
// clear message
this.messageService.clearMessage();
}
ngOnInit() {}
}
And finaly you have to import the service in the component where you want to read the message or data to receive from the first component (component B for example)
something like:
import { Component, OnInit, OnDestroy} from '@angular/core';
import { descriptionElements1, descriptionElements2 } from './model-family-description-elements';
import { Subscription } from 'rxjs/Subscription';
import { MessageService } from './message.service';
@Component({
selector: 'model-family-description',
templateUrl: './model-family-description.component.html',
styleUrls: ['./model-family-description.component.css']
})
export class ModelFamilyDescriptionComponent implements OnInit {
//you receive the data in this variable --> message
message: any;
subscription: Subscription;
desc1 = descriptionElements1;
desc2 = descriptionElements2;
constructor(private messageService: MessageService) {
}
ngOnDestroy() {
// unsubscribe to ensure no memory leaks
this.subscription.unsubscribe();
}
ngOnInit() {
// subscribe to component A messages
this.subscription = this.messageService.getMessage().subscribe(message => { this.message = message; });
}
}
Upvotes: 2
Reputation: 1134
there are two ways to do this using EventEmitter or using Subject I prefer Subject. you can read more about Subject from the documentation of rxjs lib http://reactivex.io/rxjs/file/es6/Subject.js.html#lineNumber19 it's not an angular implemented function unlike EventEmitter if you want to read more about EventEmitter https://angular.io/api/core/EventEmitter
Upvotes: 0
Reputation: 331
You should create a common @Injectable
with Subject
Example:
@Injectable()
export class CommonService {
private dropdownValue: Subject<string> = new Subject();
public getDropdownValue() : Observable {
return this.dropdownValue.asObservable();
}
public setDropdownValue(value: string) : void {
this.dropdownValue.next(value);
}
Then in component A:
constructor(
private commonService: CommonService
) {
...
}
protected onDropdownChange(value: string) : void {
this.commonService.setDropdownValue(value);
}
And in other components:
constructor(
private commonService: CommonService
) {
this.commonService.getDropdownValue().subscribe((newValue) => {
// action on data change
});
}
Upvotes: 2