我尝试在我的 Angular 16 项目中创建一个自定义自动完成组件。 该组件应该能够从我的组件存储中延迟获取数据。
我做到了做这样的事情:
// MySearchComponent
@Input({ required: true }) searchValues$?: Observable<SearchInputObject[]>;
@Input({ required: true }) searchValuesSubscription!: any;// the trigger
@Input({ required: true }) fieldCtrl!: FormControl<SearchInputObject | null>; // form control to return the selected value
用
searchValuesSubscription
触发我的效果,以及 searchValues$
商店中关联的选择。
我发现该解决方案对开发人员不太友好,就好像我使用该组件 10 次一样,我必须在父组件中创建这 2 个属性才能使用 MySearchComponent
ngOnInit() {
this.searchValues$ = this._myStore.companyCategories$;
this.searchValuesSubscription$ = this._myStore.loadCompanyCategories;
}
并调用组件:
<app-async-search-input
[fieldCtrl]="docCategoryCtrl"
[searchValues$]="searchValues$"
[searchValuesSubscription]="searchValuesSubscription$">
</app-async-search-input>
效果摘录,以防万一有帮助:)
readonly loadCompanyCategories = this.effect<void>(trigger$ =>
trigger$.pipe(
exhaustMap(() =>
combineLatest([this.userInstance$, this.selectedCompanyId$]).pipe(
switchMap(([instance, selectedCompanyId]) => {
if (instance && selectedCompanyId) {
return this._apiService.getCompanyCategories(instance, selectedCompanyId).pipe(
tapResponse(
companiesCategories => {
this.patchState({
nonPersistent: {
...this.get().nonPersistent,
companyCategories: companiesCategories,
},
});
},
(error: HttpErrorResponse) => console.error(error)
)
);
}
return [];
})
)
)
)
);
这里是一个 stackblitz,显示了我所做的简化版本:https://stackblitz.com/edit/stackblitz-starters-frdx5b?file=src%2Fapp%2Fasync-autocomplete%2Fasync-autocomplete.component.ts 您需要单击“加载书籍”,以便 AsyncAutocompleteComponent 触发商店,然后在选择中显示书籍列表。
有没有办法让触发器返回可观察的值?或者这是不应该用 Effect 完成的事情?还有另一种方法可以做到这一点吗? 我愿意接受所有建议! 谢谢!
好的,这是一个分叉的 stackblitz,它显示了处理事件和数据流的正确方法。
正如我在评论中提到的,将可观察量和订阅传递到组件中是一种反模式。除非绝对必要,否则应避免这样做。
我将只发布与上面显示的不同的代码片段......
当您需要在父组件中触发某些内容时,请使用
@Output
,而不是传递订阅。
@Input({ required: true }) searchValues: Book[] | null = [];
@Input({ required: true }) fieldCtrl!: FormControl<any | null>; // form control to return the selected value
@Output() loadBooks = new EventEmitter();
然后该组件应该处理数据的异步绑定,以便每当数据更改时都会为异步自动完成组件运行更改检测。此外,还有一个新的事件处理程序,用于按下按钮时调用存储中的加载。
<p>Select a book :</p>
<app-async-autocomplete
[searchValues]="searchValues$ | async"
(loadBooks)="onLoadBooks()"
[fieldCtrl]="fieldCtrl"
></app-async-autocomplete>
你写的load函数效果也需要一些调整。确实没有必要先使用
exhaustMap
,然后再使用 switchMap
。当您需要在处理效果时展开可观察对象时,我建议使用 combineLastestWith
中的 rxjs
或 concatLatestFrom
库中找到的 @ngrx/operators
。
readonly loadBooks = this.effect<void>((trigger$) =>
trigger$.pipe(
tap((_) => console.log('fetch books')),
combineLatestWith(this.userInstance$, this.selectedCompanyId$),
switchMap(([_, instance, selectedCompanyId]) => {
if (instance && selectedCompanyId) {
return this._booksHttpService.getBooks().pipe(
tapResponse(
(newBooks) => {
console.log('newBooks=');
console.table(newBooks);
this.patchState({
books: newBooks,
});
},
(error: HttpErrorResponse) => console.error(error)
)
);
}
return [];
})
)
);
请参阅 stackblitz 以了解它们如何协同工作。