#angular #observable #mat-autocomplete
Вопрос:
У меня есть два раскрывающихся списка автозаполнения мата в реактивной угловой форме (Угловой и угловой материал v12).
Один работает. Он извлекает массив объектов из службы и не включает в себя наблюдаемое для параметров. Он разработан аналогично приведенному примеру. Другой подписывается на наблюдаемый для выпадающих вариантов, и они отображаются, но не могут фильтроваться. Я не вижу ошибок.
Моя гипотеза заключается в том, что либо фильтр срабатывает до того, как появляются данные, либо существует какая-то другая проблема с тем, как применяется фильтр. Данные отображаются просто отлично — я просто не могу печатать и фильтровать.
Я настроил наблюдаемый код немного по-другому, чтобы разрешить подписку на API для запуска до подписки на фильтр и для сравнения уникального идентификатора. Похоже, это не сработало. Я не могу определить, какая часть кода выходит из строя. Я попытался повторить рабочий код с наблюдаемым, но он не может фильтровать тип таким же образом (принимает два аргумента), и я застрял там.
Может быть, есть способ упростить мою подписку? Я понимаю, что происходит, но мой наблюдаемый опыт-это свет. Соответствующий код ниже:
Рабочий шаблон:
<mat-grid-tile-header>
Waste Type
</mat-grid-tile-header>
<mat-form-field appearance="standard">
<mat-label>Waste Type</mat-label>
<input
tabindex="2"
#waste_type
matInput
[matAutocomplete]="waste_type_auto"
placeholder="Choose a Waste Type"
class="input"
type="text"
formControlName="waste_type"
[ngClass]="{'is-success' : collectForm.get('waste_type').valid amp;amp; collectForm.get('waste_type').dirty, 'is-danger' : !collectForm.get('waste_type').valid }"
aria-label="Waste Type">
<mat-autocomplete #waste_type_auto="matAutocomplete">
<mat-option *ngFor="let waste_type of filteredWaste_Types$ | async" [value]="waste_type.name">
<img class="example-option-img" aria-hidden [src]="waste_type.icon" height="25">
<span>{{ waste_type.name }}</span>
</mat-option>
</mat-autocomplete>
<mat-hint [hidden]="collectForm.get('waste_type').valid" align="end">Waste type (e.g., Recycling) is required</mat-hint>
</mat-form-field>
Нерабочий шаблон (практически тот же):
<mat-grid-tile-header>
Tare
</mat-grid-tile-header>
<mat-form-field appearance="standard">
<mat-label>Waste Container</mat-label>
<input
tabindex="4"
#tare_container
placeholder="Enter Container"
matInput
[matAutocomplete]="tare_auto"
class="input"
type="text"
formControlName="tare_container"
[ngClass]="{'is-success' : collectForm.get('tare_container').valid amp;amp; collectForm.get('tare_container').dirty, 'is-danger' : !collectForm.get('tare_container').valid }"
aria-label="Tare Container">
<mat-autocomplete #tare_auto="matAutocomplete">
<mat-option *ngFor="let tare of filteredTares$ | async" [value]="tare.container_name">
<img class="example-option-img" aria-hidden [src]="tare.container_icon" height="25">
<span>{{ tare.container_name }}</span>
</mat-option>
</mat-autocomplete>
<mat-hint [hidden]="collectForm.get('tare_container').valid" align="end">Tare (e.g., Lg Compost Bin) is required</mat-hint>
</mat-form-field>
.ts for both filters
filteredWaste_Types$: Observable<Waste_Type[]>;
filteredTares$: Observable<Tare[]>;
// Working Value Changes Subscription
this.filteredWaste_Types$ = this.collectForm.get('waste_type').valueChanges
.pipe(
startWith(''),
map(value => this._filteredWaste_Types(value))
);
// Non-Working Value Changes Subscription
this.filteredTares$ = zip(
this.collectForm.get('tare_container').valueChanges
.pipe(
startWith('')),
this.ts.tares,
)
.pipe(
map(([value, tares]) => {
const filtered = value ? this._filteredTares(value, tares) : tares;
return filtered;
})
);
// Working Filter
private _filteredWaste_Types(value: string): Waste_Type[] {
const filterValue = value.toLowerCase();
return this.wts.waste_types.filter(waste_type => waste_type.name.toLowerCase().includes(filterValue));
}
// Non-Working Filter
private _filteredTares(value: string, tares: Tare[]): Tare[] {
const filterValue = value.toLowerCase();
return tares.filter(tare => tare.container_name.toLowerCase().includes(filterValue));
}
Working waste_type service:
waste_types: Waste_Type[] = [
{
name: 'Batteries',
icon: './assets/img/battery_icon.png'
},
{
name: 'Cardboard',
icon: './assets/img/cardboard_icon.png'
},
{
name: 'Compost',
icon: './assets/img/compost_icon.png'
}];
Нерабочее обслуживание тары:
public cachedTares: Tare[] = [];
public get tares() {
return this.api.getTares()
.pipe(
map((tares) => {
this.cachedTares = tares;
return tares;
})
);
};
API, который обслуживает нерабочую службу:
public getTares() {
const uri = '/containers';
if (this.isOnline) {
return this.http.get<Tare[]>(uri, this.httpOptions).pipe(
map(this.cacheGetRequest(uri)),
);
} else {
const result = this.getCachedRequest<Tare[]>(uri);
return new BehaviorSubject(result).asObservable();
}
}
Структура данных у каждого из них практически одинакова:
export interface Waste_Type {
name: string;
icon: string;
}
export interface Tare {
id: number;
container_name: string;
container_icon: string;
container_weight: number;
}
Комментарии:
1. есть шанс, что ты сможешь сделать стакблиц?
Ответ №1:
Я думаю, вам просто нужно zip
заменить combineLatest
оператор rxjs.
Разница в том, что:
Оператор combineLatest ведет себя аналогично zip, но в то время как zip излучает только тогда, когда каждый наблюдаемый источник ранее излучал элемент, combineLatest излучает элемент всякий раз, когда какой-либо из наблюдаемых источников излучает элемент
Это означает, что ваш filteredTares$
наблюдаемый выдает значение только один раз, когда вы получили ответ от TareService
. После этого он молчит, даже если valueChanges
выдает новые значения(даже когда вы вводите input
)
this.filteredTares$ = combineLatest([
this.collectForm2.get('tare_container').valueChanges.pipe(startWith('')),
this.ts.tares,
])
...
Комментарии:
1. Это сработало идеально! Спасибо @yurzui.