Search code examples
angularng2-bootstrap

Angular 2: How to pass value from one component to refresh another component on change detection?


So I'm using ng2-bootstrap typeahead for a search field that I have. When I select a value returned, I want it to refresh the pane below with the details that come from the selected item.

In my parent component, I have this:

HTML:

<input [(ngModel)]="asyncSelected"
                           [typeahead]="dataSource"
                           (typeaheadLoading)="changeTypeaheadLoading($event)"
                           (typeaheadNoResults)="changeTypeaheadNoResults($event)"
                           (typeaheadOnSelect)="onSelectSearchValue($event)"
                           [typeaheadOptionsLimit]="7"
                           [typeaheadMinLength]="3"
                           [typeaheadWaitMs]="500"
                           [typeaheadOptionField]="'name'"
                           name="searchField"
                           class="form-control" style="width: 250px;" placeholder="Search Chip Families">
...
<div class="container-fluid">
    <router-outlet></router-outlet>
</div>

TypeScript:

public onSelectSearchValue(e: TypeaheadMatch): void {
    this.chipFamilyId = e.item.id;
}

How is it that I pass this value that comes back from the typeahead to my component that handles the call to the service to lookup the details and place it in the <router-outlet>? Using change detection didn't seem to work.

My child component is as follows:

HTML:

<div class="content" *ngIf='chipFamilies && chipFamilies.length'>
    <ol class="breadcrumb">
        <li><a [routerLink]="['/home']">Home</a></li>
        <li><a [routerLink]="['/chipFamily']">{{chipFamilies.Hierarchy}}</a></li>
        <li class="active">{{chipFamilies.Name}}</li>
    </ol>
    <h2>{{chipFamilies.Hierarchy}}</h2>
...

TypeScript:

export class ChipFamilyComponent implements OnInit, OnChanges {
    errorMessage: string;
    @Input() chipFamilyId: number = 11223;
    chipFamilies: IChipFamily[];

    constructor(private _adminService: ChipFamilyService) {

    }

    ngOnInit(): void {
        //Initially load 11223 on load
        this._adminService.getChipFamily(this.chipFamilyId).subscribe(
            chipFamilies => this.chipFamilies = chipFamilies,
            error => this.errorMessage = <any>error
        );
    }

    ngOnChanges(changes:{[propKey: string]: SimpleChange}) {
        debugger;
        this._adminService.getChipFamily(this.chipFamilyId).subscribe(
            chipFamilies => this.chipFamilies = chipFamilies,
            error => this.errorMessage = <any>error
        );
    }
}

Solution

  • From Harry_Ninh's excellent suggestion, here's what I came up with to allow communication between components without using a @Input and any component selectors:

    In my service, I added a subject to allow the parent to announce that a search was made:

    @Injectable()
    export class ChipFamilyService {
        private searchStringSubject = new Subject<string>();
        private _searchUrl = 'http://localhost:8888/chipfamily/'; 
    
        constructor(private _http: Http) {
        }
    
        searchAnnounced$ = this.searchStringSubject.asObservable();
    
        announceSearch(searchValue: string) {
            this.searchStringSubject.next(searchValue);
        }
    

    In my parent component, I just made the announcement after the result was selected from my typeahead field:

    public onSelectSearchValue(e: TypeaheadMatch): void {
        this.chipFamilyService.announceSearch(e.item.id);
    }
    

    In my child component, I subscribe to the announcement and then update the model based on the new data in the constructor:

    @Component({
        template: require('./chip-family.component.html')
    })
    export class ChipFamilyComponent implements OnInit {
        errorMessage: string;
        chipFamilyId: number = 11223;
        chipFamilies: IChipFamily[];
        private gridOptions:GridOptions;
        subscription: Subscription;
    
        constructor(private chipFamilyService: ChipFamilyService) {
            this.subscription = chipFamilyService.searchAnnounced$.subscribe(
                chipFamilyId => {
                    this.chipFamilyId = Number(chipFamilyId);
                    this.chipFamilyService.getChipFamily(this.chipFamilyId).subscribe(
                        chipFamilies => this.chipFamilies = chipFamilies,
                        error => this.errorMessage = <any>error
                    );
                });
    
        }
    

    This achieves the desired result of the view panel updating with the new data based on the typeahead search.