I have two Child Components that I want to get access to one of them from the other. I have a search input control on one of the pages. On the other, I have the pagination that I need access to the value. Is there a way to do this? I tried to use this searchItemsEvent: EventEmitter<any> but it's not working.
Child Component using input control
<div class="indication-grid-search-form-container">
<div class="form-inline">
<div class="form-group">
<label for="gridSearchPatternId">Indication description</label>
<div class="input-group">
<input type="text" class="form-control" id="gridSearchPatternId" placeholder="Search on indication" aria-describedby="gridSearchPatternAddonId" [(ngModel)]="searchString" (ngModelChange)="onSearchStringChanged()">
<span class="input-group-addon" id="gridSearchPatternAddonId"><span class="glyphicon glyphicon-search"></span></span>
</div>
</div>
<button *ngIf="allowEdit" type="button" class="btn btn-primary add-new-grid-button" (click)="onAddNewItem()">
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span> New Indication
</button>
</div>
</div>
@Component({
selector: ".indication-grid-filter-container",
templateUrl: "/Views/Components/Indication/IndicationGridFilter.html"
})
export class IndicationGridFilterComponent {
private searchTimeout;
@Input() allowEdit: boolean;
@Output() addNewItemEvent = new EventEmitter();
@Output() searchItemsEvent: EventEmitter<any> = new EventEmitter();
searchString: string;
onSearchStringChanged() {
clearTimeout(this.searchTimeout);
this.searchTimeout = setTimeout(() => {
this.searchItemsEvent.next(this.searchString.toLowerCase());
}, 500);
}
onAddNewItem() {
this.addNewItemEvent.next(null);
}
}
Child Component with control pagination
<table class="table table-hover grid-table" width="100%">
<thead class="grid-header">
<tr style="vertical-align:top;" class="grid-header-title">
<th width="40%">
<div>
<span>Indication description</span>
</div>
</th>
<th width="40%">
<div class="dropdown indications-category-filter">
<span>Category </span>
<span>
<img src="/Content/images/filterActive.png" style="width: 16px" *ngIf="currentIndicationTypeId === 0" />
<img src="/Content/images/filterSelected.png" style="width: 16px" *ngIf="currentIndicationTypeId > 0" />
</span>
<a href="#" class="dropdown-toggle" id="dropdownMenuCategoryActive" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"><span class="caret"></span></a>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuCategoryActive">
<li class="divider-bottom">
<table style="width:180px;" cellpadding="5" cellspacing="5">
<tr>
<td>
<span style="font-weight:bolder"> Refine</span>
</td>
<td align="right">
<span style="font-weight:normal">Results: {{itemsCount}}</span>
</td>
</tr>
</table>
</li>
<li class="divider-bottom">
<a href="#" (click)="selectIndicationType(0)" class="indications-category-filter-item">
<span class="glyphicon glyphicon-none" aria-hidden="true"></span>
Show All
<span class="badge">
{{totalCount}}
</span>
</a>
</li>
<li *ngFor="let item of stats">
<a href="#" (click)="selectIndicationType(item.Id)" class="indications-category-filter-item">
<span class="glyphicon glyphicon-ok" aria-hidden="true"
*ngIf="currentIndicationTypeId == item.Id"></span>
<span class="glyphicon glyphicon-none" aria-hidden="true"
*ngIf="currentIndicationTypeId != item.Id"></span>
{{item.Name}}
<span class="badge">
{{item.Count}}
</span>
</a>
</li>
</ul>
</div>
</th>
<th width="10%">
<div>
<span>#Templates</span>
</div>
</th>
<th width="10%"></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of items | paginate: { id: tabId, itemsPerPage: pageSize, currentPage: currentPage, totalItems: itemsCount }" class="indication-data-row">
<td>
{{item.ValueName}}
</td>
<td>
<span *ngFor="let category of getIndicationCategories(item)">
<span>{{category.Name}}</span>
</span>
</td>
<td>
{{item.TemplateCount}}
</td>
<td>
<div *ngIf="allowEdit" class="indications-grid-indication-operations">
<span style="display: inline">
<a (click)="editItem(item)">Edit</a> | <a (click)="changeArchiveStatus(item)">{{archiveOperationLabel}}</a>
</span>
</div>
</td>
</tr>
<tr *ngIf="items.length == 0">
<td colspan="3">
<table width="100%">
<tr style="height:150px">
<td></td>
</tr>
<tr>
<td align="center">
<span class="middle-size-information-label">No such indication exists.</span>
</td>
</tr>
<tr style="height:150px">
<td></td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
<div>
<div class="pagination-controls" (pageChange)="getPage($event)" id="{{tabId}}"></div>
</div>
import IndicationModel = OrderTemplateTool.Web.Models.Indications.IndicationModel;
import SentencePartModelBase = OrderTemplateTool.Core.Sentences.Indications.MultiselectSentencePartModel;
import { IndicationGridFilterComponent } from "../Indication/IndicationGridFilter";
import { Component, Input, Output, NgZone, AfterViewInit, Inject, EventEmitter, ViewChild, OnInit, ViewChildren, OnChanges, QueryList } from "@angular/core";
import { DictionaryItem } from "../../Models/dictionaryItem";
import { PaginationComponent } from "../Common/Pagination";
import { IndicationManager } from "../../Bll/IndicationManager";
import {BaseComponent} from "../baseComponent";
import { Dictionaries } from "../../Models/DynamicDictionaries";
import IndicationDictionaryModel = OrderTemplateTool.Web.Models.Indications.IndicationDictionaryModel;
import { Observable } from "rxjs/Observable";
import { Subject } from "rxjs";
@Component({
selector: "div.indications-grid-tab",
templateUrl: "/Views/Components/Indication/IndicationsGridTab.html"
})
export class IndicationsGridTabComponent implements OnInit {
@Input() allowEdit: boolean;
@Input() isActive = true;
@Output() onEditItem = new EventEmitter();
@Output() onChangeArchiveStatus = new EventEmitter();
@Output() searchFilter: EventEmitter<any> = new EventEmitter();
indicationTypes: DictionaryItem[];
currentIndicationTypeId = 0;
archiveOperationLabel: string;
constructor(@Inject(IndicationManager) private indicationManager: IndicationManager, private indicationGridFilterComponent: IndicationGridFilterComponent) {
this.indicationTypes = this.indicationManager.getIndicationTypes();
}
tabId: string;
totalCount: number;
items: IndicationDictionaryModel[] = [];
itemsCount: number;
stats: any[];
pageSize = IndicationManager.indicationsGridItemsPerPage;
currentPage: number;
ngOnInit() {
this.tabId = this.isActive ? "indications_active" : "indications_archive";
this.loadInitialState(1);
this.archiveOperationLabel = this.isActive ? "Archive" : "Restore";
}
loadInitialState(pageNumber: number, searchString?: string) {
return this.indicationManager.getIndicationsGridInitialState(pageNumber, this.isActive, (response) => {
this.stats = response.stats;
this.totalCount = response.totalCount;
this.itemsCount = response.itemsCount;
this.items = response.items;
this.currentPage = pageNumber;
}, searchString);
}
getPage(pageNumber: number) {
console.log('getPage');
const SearchPattern = $("#gridSearchPatternId");
console.log(SearchPattern.val());
//console.log(this.searchItemsEvent);
this.searchFilter.emit;
this.indicationManager.getIndicationsPageByActivity(
pageNumber,
this.isActive,
this.currentIndicationTypeId,
(response) => {
this.itemsCount = response.ItemsCount;
this.items = response.Indications;
this.currentPage = pageNumber;
},
SearchPattern.val()
);
}
getStats() {
this.indicationManager.getIndicationCategoryStatsByActivity(
this.isActive,
(response) => {
if (response.success) {
this.stats = response.stats;
this.totalCount = response.totalCount;
}
}
);
}
selectIndicationType(id: number) {
this.currentIndicationTypeId = id;
this.getPage(1);
}
editItem(item) {
this.onEditItem.next(item);
}
changeArchiveStatus(item) {
this.onChangeArchiveStatus.next(item);
}
private getIndicationDescription(item: IndicationModel): string {
return item.Sentence != null ?
item.Sentence.CompiledText :
"";
}
private getIndicationCategories(item: OrderTemplateTool.Web.Models.Indications.IndicationDictionaryModel): DictionaryItem[] {
var result: DictionaryItem[] = [];
var d: Dictionaries = new Dictionaries();
for (var i = 0; i < d.IndicationType.length; i++) {
if (d.IndicationType[i].Id == parseInt(item.DictionaryEnumValue.toString())) {
return [d.IndicationType[i]];
}
}
return [];
}
}
Angular recommends sharing data between non parent/child relationships through services. You can place values into this service with an RxJS pipe that debounces the search input (this replaces your
set/clearTimeoutlogic):For example:
component1.tssearch.service.tscomponent2.tsIn the template you can access this observable stream of values with the
asyncpipe, and in your code you cansubscribeto the stream. (See the RxJS docs)You can access this search value anywhere in the injection scope of
SearchService. Because of@Injectable({providedIn: 'root'})this would be the same service in your entire app. If this combination of component1/2 is repeatable, and they each need their ownSearchService, then remove{providedIn: 'root'}and instead addSearchServiceto a parent component'sprovidersthat contains only 1 comp1/comp2 combo:Another tip: With a
Subjectthe order of.nextand.subscribematters, usually this is not a problem, but if you see some first values going missing, you can try to switch to aBehaviorSubjectwhich will replay the last value to late subscribers: