Angular响应式表单控件的值仅限于用户输入?

3

我有一个响应式表单,想要获取该表单的值并将其分配给一个在接口中定义的类型变量。不过,这个表单是通过使用Google Places API填充的(因此所有的表单控件都被禁用且无法由用户编辑,除了第一个表单控件——即用户输入初始地址的字段)。

响应式表单如下:

ngOnInit() {
    // Initialization of branch Address reactive form (Form 2 - Step 2)
    this.addBranchAddressReactiveForm();
  }

// Form Group 2 - Branch Address (Step 2)
  addBranchAddressReactiveForm() {
    this.addBranchAddressForm = this.fb.group({
      // Branch Address Control
      branchAddress: ['',
        Validators.compose([Validators.required])
      ],
      // Branch Street Name Control
      branchStreetName: [{value: '', disabled: true},
        Validators.compose([Validators.required])
      ],
      // Branch Suburb Control
      branchSuburb: [{value: '', disabled: true},
        Validators.compose([Validators.required])
      ] ,
      // Branch City Control
      branchCity: [{value: '', disabled: true},
        Validators.compose([Validators.required])
      ],
      // Branch Province Control
      branchProvince: [{value: '', disabled: true},
        Validators.compose([Validators.required])
      ],
      // Branch Country Control
      branchCountry: [{value: '', disabled: true},
        Validators.compose([Validators.required])
      ],
      // Branch ZIP Control
      branchZip: [{value: '', disabled: true},
        Validators.compose([Validators.required])
      ]
    });
  }

如上所示,除第一个控件(branchAddress)外,所有控件均被禁用 - 因为这些控件是在选择地址后填充的。当查看组件.html文件中的表单本身时,可以看到“值”是动态分配的(因为这些值取决于所选地址)。
<form [formGroup]="addBranchAddressForm">
            <ng-template matStepLabel>Enter New Branch Address</ng-template>
            <div class="new-branch-form-container">

              <!-- Main Branch Address Parameter (Input that generates the autocomplete dropdown list) -->
              <mat-form-field>
                <mat-label>Enter New Branch Address</mat-label>
                <input matInput placeholder="Enter a location" formControlName="branchAddress" ngx-google-places-autocomplete #placesRef="ngx-places" [options]="addressOptions" (onAddressChange)="handleAddressChange($event)"/>
                <mat-hint>Enter the address of the new branch and select the appropriate address item</mat-hint>
                <mat-error>Please enter the new branch’s address</mat-error>
              </mat-form-field>

              <!-- Branch Street Name (Autofilled) -->
              <mat-form-field>
                <mat-label>Street Name</mat-label>
                <input matInput formControlName="branchStreetName" readonly value={{streetName}}>
                <mat-hint>The street name of the address selected</mat-hint>
                <mat-error>Please enter the street name of the address specified</mat-error>
              </mat-form-field>
              <br>

              <!-- Branch Suburb (Autofilled) -->
              <mat-form-field>
                <mat-label>Suburb</mat-label>
                <input matInput formControlName="branchSuburb" readonly value={{suburb}}>
                <mat-hint>The suburb of the address selected</mat-hint>
                <mat-error>Please enter a valid suburb</mat-error>
              </mat-form-field>
              <br>

              <!-- Branch City (Autofilled) -->
              <mat-form-field>
                <mat-label>City / Town</mat-label>
                <input matInput formControlName="branchCity" readonly value={{city}}>
                <mat-hint>The city of the address selected</mat-hint>
                <mat-error>Please enter a valid city</mat-error>
              </mat-form-field>

              <!-- Branch Province (Autofilled) -->
              <mat-form-field>
                <mat-label>Province</mat-label>
                <input matInput formControlName="branchProvince" readonly value="{{province}}">
                <mat-hint>The province of the address selected</mat-hint>
                <mat-error>Please enter the province of the address specified</mat-error>
              </mat-form-field>
              <br>

              <!-- Branch Country (Autofilled) -->
              <mat-form-field>
                <mat-label>Country</mat-label>
                <input matInput formControlName="branchCountry" readonly value={{country}}>
                <mat-hint>The country of the address selected</mat-hint>
                <mat-error>Please enter the country of the address specified</mat-error>
              </mat-form-field>
              <br>

              <!-- Branch ZIP (Autofilled) -->
              <mat-form-field>
                <mat-label>ZIP</mat-label>
                <input matInput formControlName="branchZip" readonly value={{zip}}>
                <mat-hint>The ZIP / Postal code of the address selected</mat-hint>
                <mat-error>Please enter the ZIP / Postal code of the address specified</mat-error>
              </mat-form-field>
              <br>
            </div>

            <!-- Button to navigate to previous step in stepper -->
            <button mat-stroked-button matStepperPrevious color="primary">Back</button>

            <!-- Button to navigate to next step in stepper (disabled until branch address form is valid) -->
            <button mat-stroked-button matStepperNext color="primary" [disabled]="!addBranchAddressForm.valid">Next</button>
          </form>

我的问题如下。我希望能够将此表单中的值分配给一个对象,以便通过连接到Web API(不重要)创建数据库记录 - 尽管在尝试将此表单的值分配给变量时,传递的唯一“值”是由用户自己输入的值。因此,例如,如下所示,用户在选择地址之前键入“12”。

enter image description here

下面的代码只会捕获“12”这个值(尽管在地址选择后,其余控件已被填充):
const branch: Branch = this.addBranchDetailsForm.value;

因此,当记录上述“branch”时,输出如下,而不是每个单独表单控件中找到的所有值的对象:

enter image description here

我正在按以下方式填充表单控件的值:

public handleAddressChange(address: any) {

    // Full unformatted address (Not Displayed)
    this.address = address.formatted_address;
    console.log(this.address);

    // Place id (Not Displayed)
    this.placeId = address.place_id;
    console.log(this.placeId);

    // Latitude coordinate (Not Displayed)
    this.latitude = address.geometry.location.lat();
    console.log(this.latitude);

    // Longitude coordinate (Not Displayed)
    this.longitude = address.geometry.location.lng();
    console.log(this.longitude);

    // Reset street address on method refresh
    this.streetName = "";

    // Street number (Displayed i.t.o)
    for (var i = 0; i < address.address_components.length; i++) {
      for (var x = 0; x < address.address_components[i].types.length; x++) {
        if (address.address_components[i].types[x] === "street_number") {
          this.streetName = JSON.parse(JSON.stringify(address.address_components[i].long_name)) + " ";
        }
      }
    }

    // Street name (Displayed i.t.o)
    for (var i = 0; i < address.address_components.length; i++) {
      for (var x = 0; x < address.address_components[i].types.length; x++) {
        if (address.address_components[i].types[x] === "route") {
          this.streetName += JSON.parse(JSON.stringify(address.address_components[i].long_name));
        }
      }
    }

    // Reset suburb on method refresh
    this.suburb = "";

    // Suburb (Displayed)
    for (var i = 0; i < address.address_components.length; i++) {
      for (var x = 0; x < address.address_components[i].types.length; x++) {
        if (address.address_components[i].types[x] === "sublocality") {
          this.suburb = JSON.parse(JSON.stringify(address.address_components[i].long_name))
        }
      }
    }

    // Reset city on method refresh
    this.city = "";

    // City (Displayed)
    for (var i = 0; i < address.address_components.length; i++) {
      for (var x = 0; x < address.address_components[i].types.length; x++) {
        if (address.address_components[i].types[x] === "locality") {
          this.city = JSON.parse(JSON.stringify(address.address_components[i].long_name))
        }
      }
    }

    // Reset province on method refresh
    this.province = "";

    // Province (Displayed)
    for (var i = 0; i < address.address_components.length; i++) {
      for (var x = 0; x < address.address_components[i].types.length; x++) {
        if (address.address_components[i].types[x] == "administrative_area_level_1") {
          this.province = JSON.parse(JSON.stringify(address.address_components[i].long_name))
        }
      }
    }

    // Reset country on method refresh
    this.country = "";

    // Country (Displayed)
    for (var i = 0; i < address.address_components.length; i++) {
      for (var x = 0; x < address.address_components[i].types.length; x++) {
        if (address.address_components[i].types[x] == "country") {
          this.country = JSON.parse(JSON.stringify(address.address_components[i].long_name))
        }
      }
    }

    // Reset zip on method refresh
    this.zip = "";

    // ZIP (Displayed)
    for (var i = 0; i < address.address_components.length; i++) {
      for (var x = 0; x < address.address_components[i].types.length; x++) {
        if (address.address_components[i].types[x] == "postal_code") {
          this.zip = JSON.parse(JSON.stringify(address.address_components[i].long_name))
        }
      }
    }
  }

handleAddressChange方法接受在表单的第一个控件(字段)中输入的地址,如下所示:
<!-- Main Branch Address Parameter (Input that generates the autocomplete dropdown list) -->
              <mat-form-field>
                <mat-label>Enter New Branch Address</mat-label>
                <input matInput placeholder="Enter a location" formControlName="branchAddress" ngx-google-places-autocomplete #placesRef="ngx-places" [options]="addressOptions" (onAddressChange)="handleAddressChange($event)"/>
                <mat-hint>Enter the address of the new branch and select the appropriate address item</mat-hint>
                <mat-error>Please enter the new branch’s address</mat-error>
              </mat-form-field>

当激活此方法时,如上所示的变量(例如 this.suburb / this.city)将被填充,因此这些变量将作为值属性的一部分显示在相应的输入字段中。例如:
<!-- Branch Suburb (Autofilled) -->
              <mat-form-field>
                <mat-label>Suburb</mat-label>
                <input matInput formControlName="branchSuburb" readonly value={{suburb}}>
                <mat-hint>The suburb of the address selected</mat-hint>
                <mat-error>Please enter a valid suburb</mat-error>
              </mat-form-field>
              <br>

在上面,你可以看到变量(this.suburb)被设置为值属性{{suburb}}。如果有任何原因,请告诉我。感谢您的时间。

你能在 StackBlitz 上重现这个问题吗? - Brian Smith
1个回答

2

因为存在被禁用的字段,formGroup.value 不会返回这些值。您需要调用 getRawValue() 方法来获取字段的值,而不考虑它们的启用/禁用状态。

您需要执行以下操作:

const branch: Branch = this.addBranchDetailsForm.getRawValue();

从Angular文档中得知:https://angular.io/api/forms/FormGroup#getRawValue

getRawValue() - 检索所有值,无论是否禁用。 value属性是获取组的值的最佳方法, 因为它在FormGroup中排除了禁用的控件。

从您更新的代码中可以清楚地看出,您没有设置formGroup的值,因此尽管使用了getRawValue()方法,您仍然得到空值。 在这里,您没有更新formControl。
 this.streetName = JSON.parse(JSON.stringify(address.address_components[i].long_name)) + " ";

你需要将这个更改为:
this. addBranchDetailsForm.get('branchStreetName').setValue(JSON.stringify(address.address_components[i].long_name)) + " ";

同样适用于所有其他领域。

使用getRawValue()确实改变了输出 - 但是,它仍然将所有表单控件视为空的?我的输出类似于这样: branchAddress:“1” branchCity:“” branchCountry:“” branchProvince:“” branchStreetName:“” branchSuburb:“” branchZip:“” - SeventhWarhawk
你是如何给formGroup设置值的? - Ritesh Waghela
请查看此代码片段 https://stackblitz.com/edit/example-angular-material-reactive-form-uacyao?file=app/app.component.ts,使用您的代码的简化版本,我可以获取表单值。在分支地址中输入任何内容,您将看到它会更新街道,并且表单值将被记录在控制台中。 - Ritesh Waghela
请您查看问题更新,了解我如何设置formGroup的值。 - SeventhWarhawk
1
这可能是由于 stringify 引起的:请尝试这样做:this.addBranchDetailsForm.get('branchStreetName').setValue(address.address_components[i].long_name); - Ritesh Waghela
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接