Angular 9 Операция поиска в Formarray выполняется только для первого элемента управления Dynami c - PullRequest
0 голосов
/ 29 апреля 2020

У меня есть formarray с определенными полями, одно из которых является вводом. В этом поле при пользовательском вводе мы ищем в API и возвращаем некоторые данные.

Проблема в том, что он работает только для первого динамического управления c. Когда я добавляю больше элементов управления, это не работает для них.

Я предполагаю, что это происходит, потому что я написал логику поиска c в ngAfterViewInit().

Но какая тогда альтернатива.

Я не могу понять, как решить эту проблему.

Заранее спасибо

.ts

    purchaseform = this.fb.group({

      vendor_mobile : ['', [Validators.required, Validators.minLength(10), Validators.maxLength(10), Validators.pattern("^[0-9]*$")]],
      product : this.fb.array([this.addProductGroup()])

    })

    addProductGroup() {
      return this.fb.group({
        product_name : ['', Validators.required ],
        product_quantity : ['', [Validators.required, Validators.pattern("^[0-9]*$") ]],
        product_Buyingprice : ['', [ Validators.required, Validators.pattern("^[0-9]*$") ]],
      })
        }

get product() {
    return this.purchaseform.get('product') as FormArray;
  }

addproduct() {
    this.product.push(this.addProductGroup())  
   }

   remove_product(index) {
     return this.product.removeAt(index)
   }

    ngAfterViewInit() {
        // server-side search
    fromEvent(this.input.nativeElement,'keyup')
        .pipe(
            filter(Boolean),
            debounceTime(500),
            distinctUntilChanged(),
            tap((event:KeyboardEvent) => {
              console.log(event)
              console.log(this.input.nativeElement.value)

              this.productService.search_Products(this.input.nativeElement.value).subscribe(data =>{
                if(data){
                  this.product_list = data
                  console.log(this.product_list)
                }
              })
            })
        )
        .subscribe();
    }

. html

<form [formGroup]="purchaseform"> 
// other fields

<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" [displayWith]="displayFn" >

              <mat-option *ngFor="let state of product_list " [value]="state ">
                <span>{{state.name}}</span> 
              </mat-option>

              <mat-option *ngIf="!product_list || !product_list.length" class="text-danger">
                Such product does not exists
              </mat-option>

            </mat-autocomplete>

          </mat-form-field>

       <mat-form-field class="example-full-width">
        <mat-label>Enter product quantity</mat-label>
          <input matInput formControlName="product_quantity" type="number" >
        </mat-form-field>


        <mat-form-field class="example-full-width">
          <mat-label>Enter product price</mat-label>
        <input matInput formControlName="product_Buyingprice" type="number">
        </mat-form-field>


<button type="button" [disabled]="!purchaseform.valid" class="btn btn-primary"  (click) = "addproduct()">Add product</button>               
    <button [disabled] = "i==0" type="button" class="btn btn-danger" (click) = "remove_product(i)">Delete product</button>

      </ng-container>
</div>

</form>

1 Ответ

1 голос
/ 30 апреля 2020

если вы используете @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 разветвленный стек с этими изменениями

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...