если вы используете @ViewChild
, viewChild получает только первый элемент.
если вы используете @ViewChildren, вам нужно создать так много событий для каждого элемента QueryList
@ViewChildren('input') inputs:QueryList<ElementRef>
this.inputs.forEach(input=>{
fromEvent(input.nativeElement,'keyup')
})
В любом случае это НЕ работает - работает, если в массиве сначала были фиксированные элементы. Ну, вы можете подписаться на input.changes и бла-бла-бла
Путь НЕ использовать из событий. Идея взята из этого блога Амира Тугендхафта . Первый - это не использование «productList», а наблюдаемый «productList» и «asyn c pipe». Поскольку у нас есть несколько "productList", нам нужен массив наблюдаемых
productList$:Observable<any>[]=[];
. И. html будет выглядеть как
<div formArrayName = "product" *ngFor="let prod of product?.controls; let i = index">
<ng-container [formGroupName]="i">
<mat-form-field class="example-full-width">
<mat-label>Enter product name</mat-label>
<input matInput #input
aria-label="product name"
[matAutocomplete]="auto"
formControlName ="product_name">
<mat-autocomplete #auto="matAutocomplete">
<ng-container *ngIf="product_list$[i] |async as results">
<mat-option *ngFor="let state of results " [value]="state.state">
<span>{{state.name}}</span>
</mat-option>
<mat-option *ngIf="prod.get('product_name').value &&
results?.length<=0" class="text-danger">
Such product does not exists
</mat-option>
<ng-container>
</mat-autocomplete>
</mat-form-field>
</ng-container>
</div>
Посмотрите, как мы используем <ng-container *ngIf="product_list$[i] |async as results">
и итерируем "results".
Что ж, следующим шагом является изменение функции addProductGroup для создания наблюдаемого и обращающегося к массиву productList $
. Способ подписки на valueChanges элемента управления, но возврат ответ службы с использованием switchMap
addProductGroup(index) {
//we use an auxiliar const
const group = this.fb.group({
product_name: ["", Validators.required],
product_quantity: [
"",
[Validators.required, Validators.pattern("^[0-9]*$")]
],
product_Buyingprice: [
"",
[Validators.required, Validators.pattern("^[0-9]*$")]
]
});
//See how the observables will be valueChange of the "product_name"
//of this formGroup, using a pipe and switchMap
this.productList$[index] = group.get("product_name").valueChanges.pipe(
debounceTime(300),
switchMap(value => this.productService.search_Products(value))
);
//finally, we return the group
return group;
}
Наконец, будьте осторожны при вызове addGroup для отправки в качестве аргумента «индекса», поэтому сначала
this.purchaseform = this.fb.group({
...
product: this.fb.array([this.addProductGroup(0)]) //<--see the "0"
});
И
addproduct() {
this.product.push(this.addProductGroup(this.product.length));
}
Вы можете увидеть стек стека (я имитирую использование сервиса, очевидно, вы обращаетесь к API)
ПРИМЕЧАНИЕ. Если вы хотите использовать ngbTypeHead, см. this post
Обновление Использование [displayWith]="displayFn"
Я написал в mat-option
<mat-option *ngFor="let state of results " [value]="state.state">
Это означает, что значение является Свойство "состояние", если нам нужен весь объект, нам нужно написать
<mat-option *ngFor="let state of results" [value]="state">
, но также нам нужно сделать немного изменить, во-первых, добавить в матAutocomplete дисплей с
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn" >
И наша функция может быть похожа, например,
displayFn(data: any): string {
return data ?data.name+'-'+data.state : '';
}
Во-вторых, изменить несколько «search_Products» в сервисе принимая во внимание, когда мы получили объект или строку. Мы можем заменить функцию на что-то вроде
search_Products(name: any): Observable<any> {
//name can be a string or an object, so
name=name.toLowerCase?name.toLowerCase():name.name
//now name is a string and can filter or call to the appi
return of(states.filter(x=>x && x.toLowerCase().indexOf(name)>=0)).pipe(map(result=>
{
return result.map(x=>({state:x,name:x}))
}))
}
I разветвленный стек с этими изменениями