Получить несколько динамически добавленных входных данных автозаполнения материалов для вызова того же метода для valueChange - PullRequest
0 голосов
/ 17 июня 2020

Спасибо за чтение.

У меня есть страница, где пользователь может ввести и выбрать адрес из автозаполнения. Источником автозаполнения является внешний API, который вызывается с помощью события valueChanges.

Результатом является прогнозируемый поиск адреса на основе пользовательского ввода. В настоящее время это работает для этой единственной цели.

<mat-form-field>
      <input matInput placeholder="Search Multi" aria-label="State" [matAutocomplete]="auto" [formControl]="searchMoviesCtrl" type="text">
      <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
        <mat-option *ngIf="isLoading" class="is-loading">Loading...</mat-option>
        <ng-container *ngIf="!isLoading">
          <mat-option *ngFor="let suggestion of filteredMovies" [value]="suggestion.text">
            <span><b>{{suggestion.text}}</b></span>
          </mat-option>
        </ng-container>
      </mat-autocomplete>
    </mat-form-field>

    <br>

  <ng-container *ngIf="errorMsg; else elseTemplate">
    {{errorMsg}}
  </ng-container>
  <ng-template #elseTemplate>
    <h5>Selected Value: {{searchMoviesCtrl.value}}</h5>
  </ng-template>
import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { debounceTime, tap, switchMap, finalize } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent implements OnInit {
  searchMoviesCtrl = new FormControl();
  filteredMovies: any;
  isLoading = false;
  errorMsg: string;

  constructor(private http: HttpClient) 
  { }

ngOnInit() {
    this.searchMoviesCtrl.valueChanges
      .pipe(
        debounceTime(500),
        tap(() => {
          this.errorMsg = "";
          this.filteredMovies = [];
          this.isLoading = true;
        }),
        switchMap(value => this.http.get("http://searchaddressapiurl?text=" + this.searchMoviesCtrl.value)
          .pipe(
            finalize(() => {
              this.isLoading = false
            }),
          )
        )
      )
      .subscribe(data => {
        console.log('Search text :' + this.searchMoviesCtrl.value);
        console.log(data);

        if (data['suggestions'] == undefined) {
          this.errorMsg = data['Error'];
          this.filteredMovies = [];
          console.log('coming here ERROR');
        } else {
          this.errorMsg = "";
          this.filteredMovies = data['suggestions'];
          console.log('coming here');
        }


        console.log(this.filteredMovies);
      });
  }

displayFn(suggestion : string) : string {
    console.log('Display - ' + suggestion);
    return suggestion ? suggestion : '';
  }
}

Однако я хочу разрешить пользователю добавлять дополнительные вводы автозаполнения, которые будут использовать / вызывать тот же API при изменении значения.

Как я могу go об этом лучше всего?

Я смог добиться добавления нескольких входов, выполнив следующие действия. Я просто не уверен, как go подключить эти входные данные к функции valueChange, которая вызывает API поиска, проходящий через введенный текст ... в идеале одна функция, которая обслуживает все входы

<form [formGroup]="autocompleteForm" novalidate >
        <div formArrayName="sites">
            <div *ngFor="let unit of autocompleteForm.controls.sites.controls; let i=index" class="form-group">
                <div [formGroupName]="i">
                    <div style="width: 100%;">

                    <mat-form-field>
                        <input matInput placeholder="Search Multi" aria-label="State" [matAutocomplete]="auto" formControlName="site" type="text" (input)="searchSite(this, i)">
                        <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
                        <mat-option *ngIf="isLoading" class="is-loading">Loading...</mat-option>
                        <ng-container *ngIf="!isLoading">
                            <mat-option *ngFor="let suggestion of filteredAddresses" [value]="suggestion.text">
                            <span><b>{{suggestion.text}}</b></span>
                            </mat-option>
                        </ng-container>
                        </mat-autocomplete>
                    </mat-form-field>

                    </div>

                </div>
            </div>
            <button (click)="addSite()">Add Site</button>
        </div>

    <ng-container *ngIf="errorMsg; else elseTemplate">
        {{errorMsg}}
    </ng-container>
    <ng-template #elseTemplate>
        <h5>Selected Value: {{site.value}}</h5>
    </ng-template>

</form>
ngOnInit(){
    this.autocompleteForm = this.formBuilder.group({
      sites: this.formBuilder.array([
         // load first row at start
         this.getSite()
      ])
    });

  displayFn(suggestion : string) : string {
    console.log('Display - ' + suggestion);
    return suggestion ? suggestion : '';
  }

  public searchSite(obj, index)
  {
    console.log(obj);
    console.log(index + ' - ' + obj);
  }

  private getSite() {
    return this.formBuilder.group({
      site: ['', [Validators.required]
    });
  }

  addSite() {
    const control = <FormArray>this.autocompleteForm.controls['sites'];
    control.push(this.getSite());
  }

ОБНОВЛЕНИЕ

Я обновил метод searchSite, который вызывается при изменении входных данных ... Он позволяет входным данным вызывать метод searchSite.

Это выполняет работу по возвращению необходимого набора результатов, но, по-видимому, делает много ненужных вызовов API, я полагаю, это из-за (входного) вызова onchange и подключения события .valueChanges.

Работа на данный момент все еще продолжается но просто отмечаю некоторый прогресс. Идеи по-прежнему приветствуются.

public searchSite(obj : MultiAutoCompleteComponent, index)
  {
    console.log(obj);
    console.log(index + ' - ' + obj);
    console.log(obj.autocompleteForm.controls.sites.value[index].site);
    console.log('Input value : ' + obj.autocompleteForm.controls);

    var searchText = obj.autocompleteForm.controls.sites.value[index].site;

    if(searchText.length < 2 || searchText == '' || searchText == null || searchText == undefined || searchText === -1)
    {
      console.log('Minimum not provided');
      return;
    }

    obj.autocompleteForm.controls.sites.valueChanges
      .pipe(
        debounceTime(500),
        tap(() => {
          this.errorMsg = "";
          this.filteredAddresses = [];
          this.isLoading = true;
        }),
        switchMap(value => this.http.get("http://searchaddressapi?text=" + searchText)
          .pipe(
            finalize(() => {
              this.isLoading = false
            }),
          )
        )
      )
      .subscribe(data => {
        console.log('Search text :' + this.site.value);
        console.log(data);

        if (data['suggestions'] == undefined) {
          this.errorMsg = data['Error'];
          this.filteredAddresses = [];
          console.log('Search site coming here ERROR');
        } else {
          this.errorMsg = "";
          this.filteredAddresses = data['suggestions'];
          console.log('Search site coming here');
        }


        console.log(this.filteredAddresses);
      });

  }

ОБНОВЛЕНИЕ 2

Итак, я внес некоторые изменения, в результате чего автозаполнение работает так, как ожидалось для моего варианта использования. (Добавление динамического c Множественные автозаполнения, которые вызывают один и тот же API)

Еще одна вещь, о которой я все еще хотел бы поговорить ... вызовы API для каждого ввода char.

чтобы решить эту проблему с помощью debounceTime, но это, похоже, не имело эффекта, это задержало вызовы, но они все еще были сделаны (все на char) вместо того, чтобы игнорировать те вызовы, отправленные во время debounceTime. По крайней мере, это то, что я думал, должно / должно произойти?

Я неправильно понимаю поведение deboundTime?

import { Observable, Subject } from 'rxjs';

searchTextChanged = new Subject<string>();

public searchSite(obj : MultiAutoCompleteComponent, index)
  {
    console.log(obj);
    console.log(index + ' - ' + obj);
    console.log(obj.autocompleteForm.controls.sites.value[index].site);
    console.log('Input value : ' + obj.autocompleteForm.controls);

    var searchText = obj.autocompleteForm.controls.sites.value[index].site;

    const items = this.autocompleteForm.get('sites') as FormArray;
    console.log('No of sites added: ' + items.length);


    if(searchText.length < 5 || searchText == '' || searchText == null || searchText == undefined || searchText === -1)
    {
      console.log('Minimum not provided, no serarch conducted');
      return;
    }
    else
    { 
        this.searchTextChanged.next(searchText);

        this.searchTextChanged
          .pipe(
            debounceTime(1000),
            tap(() => {
              this.errorMsg = "";
              this.filteredAddresses = [];
              this.isLoading = true;
            }),
            switchMap(value => this.http.get("http://searchaddressapi?text=" + searchText)
              .pipe(
                finalize(() => {
                  this.isLoading = false
                }),
              )
            )
          )
          .subscribe(data => {
            console.log('Search text :' + searchText);
            console.log(data);

            if (data['suggestions'] == undefined) {
              this.errorMsg = data['Error'];
              this.filteredAddresses = [];
              console.log('Search site coming here ERROR');
            } else {
              this.errorMsg = "";
              this.filteredAddresses = data['suggestions'];
              console.log('Search site coming here');
            }


            console.log(this.filteredAddresses);
          });
    }

  }

Что я делаю неправильно re. реализация задержки debounceTime?

ОБНОВЛЕНИЕ 3 - РЕШЕНО

Я заставил это работать по мере необходимости!

Динамически добавлять дополнительные автозаполнения, которые используют тот же API для набора результатов данных.

DebounceTime уменьшает количество вызовов API, когда пользователь вводит searchText.

Я уверен, что вы можете очистить это, и, как было предложено одним из комментаторов, вызов API в службе, но вот он все равно.

//multi-auto-complete.component.css
.example-form {
    min-width: 150px;
    max-width: 500px;
    width: 100%;
  }

  .example-full-width {
    width: 100%;
  }

//multi-auto-complete.component.html
<div>

<form [formGroup]="autocompleteForm" novalidate >
            <div formArrayName="sites">
                <div *ngFor="let unit of autocompleteForm.controls.sites.controls; let i=index" class="form-group">
                    <div [formGroupName]="i">
                        <div style="width: 100%;">

                        <mat-form-field>
                            <input matInput placeholder="Search Multi" aria-label="State" [matAutocomplete]="auto" formControlName="site" type="text" (input)="searchSite(this, i)">
                            <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
                            <mat-option *ngIf="isLoading" class="is-loading">Loading...</mat-option>
                            <ng-container *ngIf="!isLoading">
                                <mat-option *ngFor="let suggestion of filteredAddresses" [value]="suggestion.text">
                                <span><b>{{suggestion.text}}</b></span>
                                </mat-option>
                            </ng-container>
                            </mat-autocomplete>
                        </mat-form-field>

                        </div>

                    </div>
                </div>
                <button (click)="addSite()">Add Site</button>
            </div>

        <ng-container *ngIf="errorMsg; else elseTemplate">
            {{errorMsg}}
        </ng-container>
        <ng-template #elseTemplate>
            <h5>Selected Value: {{site.value}}</h5>
        </ng-template>

    </form>

</div>
//multi-auto-complete.component.ts
import { Component, OnInit, ɵConsole } from '@angular/core';
import { FormControl } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';

import { debounceTime, tap, switchMap, finalize } from 'rxjs/operators';
import { FormGroup, FormBuilder, FormArray, Validators } from '@angular/forms';
import { Observable, Subject } from 'rxjs';


export interface Suggestion {
  text: string;
  magicKey: string;
  isCollection: boolean;
}

@Component({
  selector: 'app-multi-auto-complete',
  templateUrl: './multi-auto-complete.component.html',
  styleUrls: ['./multi-auto-complete.component.css']
})
export class MultiAutoCompleteComponent implements OnInit {
  site = new FormControl();
  filteredAddresses: any;
  isLoading = false;
  errorMsg: string;

  autocompleteForm: FormGroup;

  results$: Observable<any>;
  searchTextChanged = new Subject<string>();

  constructor(private http: HttpClient, private router: Router, private formBuilder: FormBuilder) 
  { }

  ngOnInit(){
    this.autocompleteForm = this.formBuilder.group({
      sites: this.formBuilder.array([
         // load first row at start
         this.getSite()
      ])
    });

    this.results$ = this.searchTextChanged.pipe(
      debounceTime(500),
      switchMap(searchText => this.http.get("http://searchaddressapi?text=" + searchText)))

      console.log(this.results$.subscribe(data => { 
        this.filteredAddresses = data['suggestions'];
        console.log(data)
      }));

  }

  displayFn(suggestion : string) : string {
    console.log('Displaying selection - ' + suggestion);
    this.filteredAddresses = [];
    return suggestion ? suggestion : '';
  }

  public searchSite(obj : MultiAutoCompleteComponent, index)
  {
    console.log(obj);
    console.log(index + ' - ' + obj);
    console.log(obj.autocompleteForm.controls.sites.value[index].site);
    console.log('Input value : ' + obj.autocompleteForm.controls);

    var searchText = obj.autocompleteForm.controls.sites.value[index].site;

    //const items = this.autocompleteForm.get('sites') as FormArray;
    //console.log('No of sites added: ' + items.length);


    if(searchText.length < 5 || searchText == '' || searchText == null || searchText == undefined || searchText === -1)
    {
      console.log('Minimum characters not provided, no search conducted');
      return;
    }
    else
    { 
        this.searchTextChanged.next(searchText);
    }

  }

  /**
   * Create form site
   */
  private getSite() {
    return this.formBuilder.group({
      site: ['', [Validators.required]]
    });
  }

  /**
   * Add new site row into form
   */
  addSite() {
    const control = <FormArray>this.autocompleteForm.controls['sites'];
    control.push(this.getSite());

    this.filteredAddresses = [];
  }

}

1 Ответ

0 голосов
/ 18 июня 2020

Я заставил это работать по мере необходимости!

Динамически добавлять дополнительные автозаполнения, которые используют тот же API для набора результатов данных.

debounceTime уменьшает количество вызовов API, когда пользователь вводит searchText.

Я уверен, что вы можете это очистить, и, как предложил один комментатор, поместите вызов API в службу, но в любом случае вот он.

Пожалуйста, смотрите UPDATE 3 в моем вопросе, чтобы узнать код!

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