【问题标题】:Autocomplete not detecting the correct value from object properties Angular自动完成未从对象属性Angular中检测到正确的值
【发布时间】:2020-01-20 13:57:27
【问题描述】:

我有大量 counties 列表,并希望实现自动完成功能。 我正在使用相同的表单来add 新客户或update 现有客户。

现在,问题是添加新客户时,我的程序运行良好,但是当我更新它时,在县下拉列表中显示countyId 而不是name。 如何做到这一点? 按顺序查看图片

县架构为

{
  countyId: number;
  name:string
}

我正在尝试使用 html 中的以下代码

<mat-form-field appearance="outline" fxFlex="50" class="pr-4">
    <mat-label>County</mat-label>
    <input type="text" placeholder="County" name="" matInput formControlName="countyId" [matAutocomplete]="auto">

    <mat-autocomplete autoActiveFirstOption #auto="matAutocomplete">
        <mat-option *ngFor="let county of filteredCounties | async" [value]="county.name">
            {{county.name}}
        </mat-option>
    </mat-autocomplete>
</mat-form-field>

在我的组件文件中

import { Component, OnInit, Inject, ViewChild, AfterViewInit, ElementRef, ComponentRef } from '@angular/core';
import { FormGroup, FormControl, FormBuilder, Validators } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { CustomerService } from '../../../../services/customer/customer.service';
import { CountyService } from '../../../../services/county/county.service';
import { SubCountyService } from '../../../../services/subCounty/sub-county.service';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import PNotify from 'pnotify/dist/es/PNotify';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

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

export class NewCustomerComponent implements OnInit {
  dataSending = false;
  customerId;
  updatePerson;
  updatedCustomer;
  ready = false;
  dialogView;
  ref: ComponentRef<any>;
  newCustomerForm: FormGroup;
  counties; // list of counties
  subCounties;
  filteredCounties: Observable<string[]>;

  constructor(
    private _formBuilder: FormBuilder,
    private router: Router,
    private route: ActivatedRoute,
    private customerService: CustomerService,
    private countyService: CountyService,
    private subCountyService: SubCountyService,
    public dialogRef: MatDialogRef<NewCustomerComponent>,
    @Inject(MAT_DIALOG_DATA) public display: any
  ) {
    this.dialogView = display.view;
    this.route.paramMap.subscribe((params) => {
      this.customerId = params.get('customerId');
      if (this.customerId) {
        this.customerService.getCustomer(this.customerId).subscribe((response) => {
          this.updatePerson = response;
          this.newCustomerForm = this._formBuilder.group({
            name: [this.updatePerson.name, Validators.required],
            nationalId: [this.updatePerson.nationalId, Validators.required],
            gender: [this.updatePerson.gender, Validators.required],
            phone1: [this.updatePerson.phone1],
            phone2: [this.updatePerson.phone2],
            bishopName: [this.updatePerson.bishopName, Validators.required],
            bishopPhone: [this.updatePerson.bishopPhone, Validators.required],
            countyId: [this.updatePerson.countyId, Validators.required],
            county: [this.updatePerson.county, Validators.required],
            subCountyId: [this.updatePerson.subCountyId, Validators.required],
            address: [this.updatePerson.address, Validators.required],
            additionalInfo: [this.updatePerson.additionalInfo],
            input1: [this.updatePerson.input1],
            input2: [this.updatePerson.input2],
            defaultingRecord: [''],
          });
          this.ready = true;
        });
      }
      else {
        this.ready = true;
        this.newCustomerForm = this._formBuilder.group({
          nationalId: ['', Validators.required],
          name: ['', Validators.required],
          gender: ['', Validators.required],
          phone1: [''],
          phone2: [''],
          county: [''],
          countyId: ['', Validators.required],
          subCountyId: ['', Validators.required],
          bishopName: ['', Validators.required],
          bishopPhone: ['', Validators.required],
          address: ['', Validators.required],
          additionalInfo: [''],
          input1: [''],
          input2: [''],
          defaultingRecord: [''],
        });
      }
    });
  }

  // tslint:disable:typedef
  ngOnInit() {
    // Getting the list of counties
    this.countyService.getCounties().subscribe((response) => {
      this.counties = response;
      this.newCustomerForm.patchValue({
        county: response
      });
      if (!this.customerId) {

        this.filteredCounties = this.newCustomerForm.get('county').valueChanges.pipe(
          startWith(''),
          map(value => typeof value === 'string' ? value : value.name),
          map(value => this._filter(value))
        );
      }
    }, error => {
      this.counties = [
        {
          "countyId": 63,
          "name": "Mombasa"
        },
        {
          "countyId": 64,
          "name": "Isiolo"
        },
        {
          "countyId": 65,
          "name": "Murang'a"
        },
        {
          "countyId": 66,
          "name": "Laikipia"
        },
        {
          "countyId": 67,
          "name": "Siaya"
        },
        {
          "countyId": 68,
          "name": "Kwale"
        },
        {
          "countyId": 69,
          "name": "Meru"
        },
        {
          "countyId": 70,
          "name": "Kiambu"
        },
        {
          "countyId": 71,
          "name": "Nakuru"
        },
        {
          "countyId": 72,
          "name": "Kisumu"
        },
        {
          "countyId": 73,
          "name": "Kilifi"
        },
        {
          "countyId": 74,
          "name": "Tharaka-Nithi"
        },
        {
          "countyId": 75,
          "name": "Turkana"
        },
        {
          "countyId": 76,
          "name": "Narok"
        },
        {
          "countyId": 77,
          "name": "Homa Bay"
        },
        {
          "countyId": 78,
          "name": "Tana River"
        },
        {
          "countyId": 79,
          "name": "Embu"
        },
        {
          "countyId": 80,
          "name": "West Pokot"
        },
        {
          "countyId": 81,
          "name": "Kajiado"
        },
        {
          "countyId": 82,
          "name": "Migori"
        },
        {
          "countyId": 83,
          "name": "Lamu"
        },
        {
          "countyId": 84,
          "name": "Kitui"
        },
        {
          "countyId": 85,
          "name": "Samburu"
        },
        {
          "countyId": 86,
          "name": "Kericho"
        },
        {
          "countyId": 87,
          "name": "Kisii"
        },
        {
          "countyId": 88,
          "name": "Taita-Taveta"
        },
        {
          "countyId": 89,
          "name": "Machakos"
        },
        {
          "countyId": 90,
          "name": "Trans Nzoia"
        },
        {
          "countyId": 91,
          "name": "Bomet"
        },
        {
          "countyId": 92,
          "name": "Nyamira"
        },
        {
          "countyId": 93,
          "name": "Garissa"
        },
        {
          "countyId": 94,
          "name": "Makueni"
        },
        {
          "countyId": 95,
          "name": "Uasin Gishu"
        },
        {
          "countyId": 96,
          "name": "Kakamega"
        },
        {
          "countyId": 97,
          "name": "Nairobi"
        },
        {
          "countyId": 98,
          "name": "Wajir"
        },
        {
          "countyId": 99,
          "name": "Nyandarua"
        },
        {
          "countyId": 100,
          "name": "Elgeyo-Marakwet"
        },
        {
          "countyId": 101,
          "name": "Vihiga"
        },
        {
          "countyId": 102,
          "name": "Mandera"
        },
        {
          "countyId": 103,
          "name": "Nyeri"
        },
        {
          "countyId": 104,
          "name": "Nandi"
        },
        {
          "countyId": 105,
          "name": "Bungoma"
        },
        {
          "countyId": 106,
          "name": "Marsabit"
        },
        {
          "countyId": 107,
          "name": "Kirinyaga"
        },
        {
          "countyId": 108,
          "name": "Baringo"
        },
        {
          "countyId": 109,
          "name": "Busia"
        }
      ];
    });
    // Getting a list of sub-counties
    this.subCountyService.getSubCounties().subscribe((response) => {
      this.subCounties = response;
    });
  }
  displayFn(county?: any): string | undefined {
    return county ? county.name : undefined
  }
  private _filter(value) {
    console.log('value: ', value);
    const filterValue = value.toLowerCase();
    return this.counties.filter(county => county.name.toLowerCase().indexOf(filterValue) === 0);
  }
  addCustomer(customer) {
    console.log('Value of customer form before setting is: ', this.newCustomerForm.value);
    const selectedCountyId = this.counties.filter(county => county.name === this.newCustomerForm.value.countyId)[0];
    console.log('CountyId: ', selectedCountyId);
    if (!selectedCountyId) {
      PNotify.error({
        title: 'Please selecte a valid county',
        minHeight: '75px'
      });
      return;
    }
    this.newCustomerForm.patchValue({
      countyId: selectedCountyId.countyId
    });
    customer.countyId = selectedCountyId.countyId;
    console.log('Value of customer form after setting is: ', this.newCustomerForm.value);
    this.dataSending = true;
    this.customerService.addCustomer(customer).subscribe((response) => {
      if (this.dialogView) {
        PNotify.success({
          title: 'Customer added Successfully',
          minHeight: '75px'
        });
        this.dialogRef.close();
      }
      else {
        PNotify.success({
          title: 'Customer added Successfully',
          text: 'Redirecting to list page',
          minHeight: '75px'
        });
        this.dataSending = false;
        document.getElementById('submitButton').style.display = 'initial';
        this.router.navigate(['searchcustomer']);
      }

    }, (error) => {
      console.log('Following error occured: ', error);
      PNotify.error({
        title: 'Error occured while adding customer',
        text: 'Failed to add new customer',
        minHeight: '75px'
      });
      this.dataSending = false;
    });
  }

  updateCustomer(customer) {
    this.dataSending = true;
    this.updatedCustomer = this._formBuilder.group({
      customerId: [this.customerId],
      nationalId: [customer.nationalId],
      name: [customer.name],
      gender: [customer.gender],
      phone1: [customer.phone1],
      phone2: [customer.phone2],
      countyId: [customer.countyId],
      subCountyId: [customer.subCountyId],
      address: [customer.address],
      additionalInfo: [customer.additionalInfo],
      bishopName: [customer.bishopName],
      bishopPhone: [customer.bishopPhone],
      input1: [customer.input1],
      input2: [customer.input2],
    });

    this.customerService.updateCustomer(this.updatedCustomer.value, this.customerId).subscribe((response) => {
      PNotify.success({
        title: 'Customer updated Successfully',
        text: 'Redirecting to list page',
        minHeight: '75px'
      });
      this.dataSending = false;
      this.router.navigate(['searchcustomer']);
    }, (error) => {
      console.log('An error occured while updating customer: ', error);
      PNotify.error({
        title: 'Error occured while updating customer',
        text: 'Failed to update customer',
        minHeight: '75px'
      });
      this.dataSending = false;
    });
  }
}



图片便于理解

【问题讨论】:

    标签: javascript angular angular-material material-design angular-reactive-forms


    【解决方案1】:

    如果您为自动完成提供一个 id,它就会显示。在您说它有效的第一张图像中,它看起来有效,但实际上它不是在做出选择时存储countyId,而是您设置为valuename 属性。我会假设它实际上也是您想要存储在那里的 id。

    我认为最干净的解决方案是实际存储整个县对象。所以我建议如下:

    <input type="text" matInput formControlName="county" [matAutocomplete]="auto">
    
    <mat-autocomplete autoActiveFirstOption #auto="matAutocomplete" [displayWith]="displayFn">
      <mat-option *ngFor="let county of filteredCounties | async" [value]="county">
        {{county.name}}
      </mat-option>
    </mat-autocomplete>
    

    我删除了countyId 表单控件,而是添加了county,它存储了整个对象并添加了displayWith 以显示该县的name 属性。

    然后修改valueChanges 以检查用户是否输入或输入了一个对象:

    // add the correct name of your form
    this.filteredCounties = this.myForm
      .get("county")
      .valueChanges.pipe(
        startWith(""),
        map(value => typeof value === 'string' ? value : value.name),
        map(value => this._filter(value))
      );
    

    以及模板中显示的displayFn

    // don't use 'any' !!
    displayFn(county?: any): string | undefined {
      return county ? county.name : undefined
    }
    

    所以现在只剩下您要创建编辑表单的选项。如果您只有countyId 属性,则可以使用find 添加正确的对象。在此示例中,我将其设置为创建,但您可能会使用setValue(),但想法是一样的!

    // replace 2 with the variable/prop you have the desired id
    county: [this.counties.find(x => x.countyId === 2)],
    

    所以现在你有了整个对象。提交表单时,如果您只想要表单控件中的countyId,您可以通过:this.myForm.get('county').value.countyId 访问它。

    STACKBLITZ 上面的代码。

    【讨论】:

    • 我可以在输入字段中输入,但是数据没有被过滤,我认为原因是因为我的form 中没有县列表,而是我我从一个单独的服务中获取它们并填充到 html 和我的 form 我已经发布了我的组件的完整代码,请看一下
    • 对不起,我不明白到底是什么问题?为什么不过滤?你在你的问题中什么都没提到?你能分叉我的stackblitz并展示这个问题吗?您可以使用 rxjs of 模拟您的 http 请求 getCounties(),如下所示:of(an array of counties here)
    • 我的意思是,无论我输入什么,它都不会过滤列表,下拉列表中的所有项目都保持不变,我无法创建堆栈闪电战,因为它需要 http 请求需要身份验证和令牌,否则它不会返回数据
    • 正如我所说,您可以使用of(an array of counties here) 使用rxjs 模拟您的http 请求。
    • 好的,我正在复制我的组件,但这里需要一点帮助,因为我无法从rxjs 导入of 如何在我的项目中包含rxjs 扩展?我用谷歌搜索但找不到任何解决方案,因为我正在使用材料项目
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-29
    • 2022-12-25
    • 2016-05-02
    • 2011-02-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多