我有两个子组件,我想从其中一个子组件访问另一个子组件。我在其中一个页面上有一个搜索输入控件。另一方面,我有需要访问该值的分页。有没有办法做到这一点?我尝试使用这个searchItemsEvent: EventEmitter<any>
但它不起作用。
使用输入控件的子组件
<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);
}
}
具有控制分页的子组件
<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 建议通过服务在非父/子关系之间共享数据。您可以使用 RxJS 管道将值放入此服务中,该管道可以消除搜索输入的抖动(这会替换您的 set/clearTimeout
逻辑):
例如:
component1.ts
class Component1 {
constructor(private readonly searchService: SearchService) {}
onSearchStringChanged(searchString: string) {
this.searchService.searchNext(searchString);
}
}
search.service.ts
@Injectable({providedIn: 'root'})
class SearchService {
private search = new Subject<string>();
searchNext = (searchString: string) => this.search.next(string);
searchValue$ = this.search.pipe(
map(value => value.toLowerCase()),
debounceTime(500),
);
}
component2.ts
class Component2 {
searchValue$ = this.searchService.searchValue$;
constructor(private searchService: SearchService) {}
}
在模板中,您可以使用async
管道访问此可观察的值流,并且在代码中您可以订阅
该流。 (参见RxJS docs)
<div> The search value is {{ searchValue$ | async }} </div>
this.searchValue$.subscribe(value => console.log("This is called whenever a new search value is emitted"))
您可以在 SearchService
注入(inject)范围内的任何位置访问此搜索值。由于 @Injectable({providedIn: 'root'})
这将是整个应用程序中的相同服务。如果 component1/2 的这种组合是可重复的,并且它们每个都需要自己的 SearchService
,则删除 {providedIn: 'root'}
并添加 SearchService
到仅包含 1 个 comp1/comp2 组合的父组件的 providers
:
@Component({ providers: [SearchService] })
class ParentOfBothComponent {}
另一个提示:对于 Subject
,.next
和 .subscribe
的顺序很重要,通常这不是问题,但如果您看到一些第一个值丢失,您可以尝试切换到 BehaviorSubject
,它将向后期订阅者重播最后一个值:
private search = new BehaviorSubject<string>("");
关于angular - 两个子组件共享控制权,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77114942/