Angular Material Automcomplete с объектом

Я хочу создать ввод автозаполнения с угловым (17) и угловым материалом. У меня есть массив объектов (местоположений), которые имеют идентификатор, координаты и необязательный адрес. Я уже могу фильтровать значения и отображать их при вводе формы, но я не могу сохранить только адрес в элементе управления формой. Я могу сохранить только весь объект, но хочу сохранить только идентификатор. Это возможно?

Шаблон:

<form [formGroup] = "form">
  <mat-form-field>
    <input
      matInput
      placeholder = "Search"
      [matAutocomplete] = "auto"
      [formControlName] = "'location'" />
    <mat-autocomplete
      #auto = "matAutocomplete"
      [displayWith] = "displayFn">
      <mat-option
        *ngFor = "let option of filteredOptions | async"
        [value] = "option">
        {{ displayFn(option) }}
      </mat-option>
    </mat-autocomplete>
  </mat-form-field>
</form>

Компонент

locations: Location[] = [
    { id: '1', coordinates: { lat: 10, lon: 20 }, address: { street: 'Street 1', housenumber: '1', city: 'City 1' } },
    { id: '2', coordinates: { lat: 30, lon: 40 }, address: { street: 'Another Street 2', housenumber: '2', city: 'City 2' } },
    { id: '3', coordinates: { lat: 50, lon: 60 } }
  ];
  form = this.createForm();
  filteredOptions: Observable<Location[]> = of([]);

  constructor(private _formBuilder: FormBuilder) {
    this.filteredOptions = this.form.controls['location'].valueChanges.pipe(
      startWith(''),
      map((value) => (typeof value === 'string' ? value : value.id)),
      map((id) => (id ? this._filter(id) : this.locations.slice()))
    );

  }

  createForm(): FormGroup {
    return this._formBuilder.nonNullable.group({
      location: new FormControl<string>('')
    })
  }

  displayFn(location: Location): string {
    if (location) {
      if (location.address) {
        return location.address.street + ' ' + location.address.housenumber + ', ' + location.address.city
      } else {
        return location.coordinates.lat + ', ' + location.coordinates.lon;
      }
    }
    return '';
  }

  private _filter(value: string): any[] {
    const filterValue = value.toLowerCase();

    return this.locations.filter(
      (option) => {
        return option.id.toString().toLowerCase().includes(filterValue) ||
          (option.address &&
            (option.address.street.toLowerCase().includes(filterValue) ||
              option.address.city.toLowerCase().includes(filterValue))) ||
          option.coordinates.lat.toString().toLowerCase().includes(filterValue) ||
          option.coordinates.lon.toString().toLowerCase().includes(filterValue)
      }
    );
  }

По моему мнению, вам нужно разделить его, чтобы иметь два элемента управления (вход). Первый входной элемент — это <mat-autocomplete>, который используется для фильтрации [без [formControlName] = "'location'"]. Второй (входной) элемент управления — это скрытый элемент управления, который используется для хранения выбранного идентификатора местоположения. Итак, когда выбран мат-автозаполнение (опция), вам нужно подключить id ко второму элементу управления. И если возможно, можете ли вы попробовать воспроизвести в StackBlitz? Спасибо.

Yong Shun 26.02.2024 10:23

Это хорошее предложение. Я пробую это.

matthiaskurte 26.02.2024 10:48
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
0
2
198
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вам нужно использовать [value] = "option.id". Таким образом, когда опция выбрана, в элементе управления формой будет сохранен только идентификатор объекта Location.

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

matthiaskurte 26.02.2024 10:16
Ответ принят как подходящий

Я изменил код после подсказки Юн Шуна, чтобы иметь отдельный элемент управления формой для автозаполнения. Теперь это работает.

Шаблон

<form [formGroup] = "form">
  <mat-form-field>
    <input
      matInput
      placeholder = "Search"
      [formControl] = "location"
      [matAutocomplete] = "auto" />
    <mat-autocomplete
      #auto = "matAutocomplete"
      [displayWith] = "displayFn">
      <mat-option
        *ngFor = "let option of filteredOptions | async"
        [value] = "option">
        {{ displayFn(option) }}
      </mat-option>
    </mat-autocomplete>
  </mat-form-field>
</form>

Компонент

locations: Location[] = [
    { id: '1', coordinates: { lat: 10, lon: 20 }, address: { street: 'Street 1', housenumber: '1', city: 'City 1' } },
    { id: '2', coordinates: { lat: 30, lon: 40 }, address: { street: 'Another Street 2', housenumber: '2', city: 'City 2' } },
    { id: '3', coordinates: { lat: 50, lon: 60 } }
  ];
  form = this.createForm();
  location = new FormControl<Location | null>(null);
  filteredOptions: Observable<Location[]> = of([]);

  constructor(private _formBuilder: FormBuilder) {
    this.filteredOptions = this.location.valueChanges.pipe(
      startWith(''),
      map((value) => {
        if (value) {
         return typeof value === 'string' ? value : value.id
        }
        return null
      }),
      map((id) => {
        if (id) {
          this.form.controls['locationId'].setValue(id);
          return this._filter(id)
        }
        return this.locations.slice()
      })
    );

  }

  createForm(): FormGroup {
    return this._formBuilder.nonNullable.group({
      locationId: new FormControl<string>('', { nonNullable: true})
    })
  }

  displayFn(location: Location): string {
    if (location) {
      if (location.address) {
        return location.address.street + ' ' + location.address.housenumber + ', ' + location.address.city
      } else {
        return location.coordinates.lat + ', ' + location.coordinates.lon;
      }
    }
    return '';
  }

  private _filter(value: string): any[] {
    const filterValue = value.toLowerCase();

    return this.locations.filter(
      (option) => {
        return option.id.toString().toLowerCase().includes(filterValue) ||
          (option.address &&
            (option.address.street.toLowerCase().includes(filterValue) ||
              option.address.city.toLowerCase().includes(filterValue))) ||
          option.coordinates.lat.toString().toLowerCase().includes(filterValue) ||
          option.coordinates.lon.toString().toLowerCase().includes(filterValue)
      }
    );
  }

Отлично, я думаю, что эта строка this.form.controls['locationId'].setValue(id); может переместиться на первую map, если проверить, что value не является строковым типом. Исходя из вашей текущей логики, каждый раз, когда вводимый текст будет устанавливать элемент управления locationId. Вы можете распечатать все значение/форму элемента управления locationId в HTML, ввести поле и наблюдать за значением.

Yong Shun 26.02.2024 11:26

Другие вопросы по теме