Angular 13-无法使用API​​的数据更新FormGroup

发布于 2025-01-30 15:32:12 字数 6622 浏览 3 评论 0原文

我正在尝试制作一个表格以提交数据,然后用数据填充该表格,以便在以后的阶段进行编辑。该页面可以是新页面,但是无论哪种方式,都可以保持表单布局和验证。

我尝试使用[(ngmodel)]的每个条目,除了可以多个条目的汽车列表之外,所有条目都可以使用。另外,这种方法给了我一条消息,说它被贬低了,这使我认为这不是这样: “看起来您在同一表单字段上与FormControlname .....”

所附的简化页面显示了我所遇到的问题。如何将输入用API的数据预填充?

car.component.html

<div class="center_container">
  <mat-card>
    <mat-card-content>
      <form class="input_form" [formGroup]="formValues">
        <p>
          <mat-form-field style="padding-right: 10px;" class="two-third-width">
            <mat-label>Name</mat-label>
            <input
              matInput
              placeholder="Seller name"
              autocomplete="off"
              formControlName="name"
            >
            <mat-icon class="mat-primary" matSuffix
                      matTooltip="[REQUIRED] Please input">info
            </mat-icon>
          </mat-form-field>

          <mat-form-field class="third-width">
            <mat-label>Margin</mat-label>
            <input
              matInput
              placeholder="Seller Margin..."
              autocomplete="off"
              formControlName="margin"
            >
            <mat-icon class="mat-primary" matSuffix
                      matTooltip="[REQUIRED] Please input">info
            </mat-icon>
          </mat-form-field>
        </p>

        <table class="full-width" formArrayName="cars">
          <tr *ngFor="let _ of cars.controls; index as i; let first=first">
            <ng-container [formGroupName]="i">
              <td>
                <mat-icon (click)="addRow()">add</mat-icon>
              </td>
              <td>
                <mat-icon *ngIf="!first" (click)="removeRow(i)">remove</mat-icon>
              </td>
              <td class="model">
                <mat-form-field class="full-width">
                  <mat-label>Model</mat-label>
                  <input
                    matInput
                    placeholder="Car model"
                    autocomplete="off"
                    formControlName="model"
                  >
                  <mat-icon class="mat-primary" matSuffix
                            matTooltip="[REQUIRED] Please input">info
                  </mat-icon>
                </mat-form-field>
              </td>
              <td class="state">
                <mat-form-field class="full-width">
                  <mat-label>State</mat-label>
                  <mat-select formControlName="state">
                    <mat-option value="new">new</mat-option>
                    <mat-option value="used">used</mat-option>
                  </mat-select>
                  <mat-icon class="mat-primary" matSuffix
                            matTooltip="[REQUIRED] Select it">info
                  </mat-icon>
                </mat-form-field>
              </td>
              <td class="value">
                <mat-form-field class="full-width">
                  <mat-label>Value</mat-label>
                  <input
                    matInput
                    placeholder="The value..."
                    autocomplete="off"
                    formControlName="value"
                  >
                  <mat-icon class="mat-primary" matSuffix
                            matTooltip="[REQUIRED] Some reminder">info
                  </mat-icon>
                </mat-form-field>
              </td>
            </ng-container>
          </tr>
        </table>
      </form>
    </mat-card-content>
    <mat-card-actions align="end">
      <button mat-raised-button [disabled]="!formValues.valid" (click)="saveSeller()" color="primary">Save seller <i
        class="material-icons">send</i></button>
    </mat-card-actions>
    <mat-card-footer style="margin: 1px;">
      <i class="mat-small">create date here</i>
    </mat-card-footer>
  </mat-card>
</div>

car.component.ts

import {Component, OnInit} from '@angular/core';
import {FormControl, FormGroup, FormArray, Validators} from '@angular/forms';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-car',
  templateUrl: './car.component.html',
  styleUrls: ['./car.component.css']
})
export class CarComponent implements OnInit {
  formValues = new FormGroup({
    cars: new FormArray([
      new FormGroup({
        model: new FormControl('', [Validators.required]),
        value: new FormControl('', [Validators.required]),
        state: new FormControl('', [Validators.required])
      })
    ]),
    name: new FormControl('', [Validators.required]),
    margin: new FormControl('', [Validators.required]),
    create_date: new FormControl('')
  });

  cars = this.formValues.get('cars') as FormArray;
  sellerIdFromRoute: any;
  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    const routeParams = this.route.snapshot.paramMap;
    this.sellerIdFromRoute = String(routeParams.get('sellerId'));
    this.getSeller(this.sellerIdFromRoute);
  }

  // Form
  addRow() {
    const group = new FormGroup({
      model: new FormControl('', [Validators.required]),
      value: new FormControl('', [Validators.required]),
      state: new FormControl('', [Validators.required])
    });

    this.cars.push(group);
  }

  removeRow(i: number) {
    this.cars.removeAt(i);
  }

  saveSeller(){
    // POSTS TO API, THIS WORKS
    alert('added/updated seller');
    console.log(this.formValues.value);
  }

  getSeller(id) {
    // This is an example response from my API
    const example: Seller = {
      _id: id,
      name: 'Used Ford salesman',
      margin: 22,
      create_date: {
        $date: '2022-05-20T19:45:22.715Z'
      },
      cars: [
        {
          model: 'Estate',
          value: 12345,
          state: 'new'
        },
        {
          model: 'Saloon',
          value: 12345,
          state: 'used'
        },
      ]
    };
    console.log(example);
    return example;
  }

}


export interface Cars {
  model: string;
  value: number;
  state: string;
}

export interface Seller {
  _id: string;
  name: string;
  margin: number;
  create_date: any;
  cars: Array<Cars>;
}

I am trying to make a form to submit data, then populate that form with data in order to edit it on a later stage. The page can be new page, but it would be nice to keep the form layout and validation either way.

I have tried using [(ngModel)] for each entry that works for everything except the list of cars that can multiple entries. Also this method gives me a message saying it's deprecated making me think that is not the way:
"It looks like you're using ngModel on the same form field as formControlName....."

Attached is a simplified page that show the issue I am having. How do I prefill the inputs with the data from the API ?

car.component.html

<div class="center_container">
  <mat-card>
    <mat-card-content>
      <form class="input_form" [formGroup]="formValues">
        <p>
          <mat-form-field style="padding-right: 10px;" class="two-third-width">
            <mat-label>Name</mat-label>
            <input
              matInput
              placeholder="Seller name"
              autocomplete="off"
              formControlName="name"
            >
            <mat-icon class="mat-primary" matSuffix
                      matTooltip="[REQUIRED] Please input">info
            </mat-icon>
          </mat-form-field>

          <mat-form-field class="third-width">
            <mat-label>Margin</mat-label>
            <input
              matInput
              placeholder="Seller Margin..."
              autocomplete="off"
              formControlName="margin"
            >
            <mat-icon class="mat-primary" matSuffix
                      matTooltip="[REQUIRED] Please input">info
            </mat-icon>
          </mat-form-field>
        </p>

        <table class="full-width" formArrayName="cars">
          <tr *ngFor="let _ of cars.controls; index as i; let first=first">
            <ng-container [formGroupName]="i">
              <td>
                <mat-icon (click)="addRow()">add</mat-icon>
              </td>
              <td>
                <mat-icon *ngIf="!first" (click)="removeRow(i)">remove</mat-icon>
              </td>
              <td class="model">
                <mat-form-field class="full-width">
                  <mat-label>Model</mat-label>
                  <input
                    matInput
                    placeholder="Car model"
                    autocomplete="off"
                    formControlName="model"
                  >
                  <mat-icon class="mat-primary" matSuffix
                            matTooltip="[REQUIRED] Please input">info
                  </mat-icon>
                </mat-form-field>
              </td>
              <td class="state">
                <mat-form-field class="full-width">
                  <mat-label>State</mat-label>
                  <mat-select formControlName="state">
                    <mat-option value="new">new</mat-option>
                    <mat-option value="used">used</mat-option>
                  </mat-select>
                  <mat-icon class="mat-primary" matSuffix
                            matTooltip="[REQUIRED] Select it">info
                  </mat-icon>
                </mat-form-field>
              </td>
              <td class="value">
                <mat-form-field class="full-width">
                  <mat-label>Value</mat-label>
                  <input
                    matInput
                    placeholder="The value..."
                    autocomplete="off"
                    formControlName="value"
                  >
                  <mat-icon class="mat-primary" matSuffix
                            matTooltip="[REQUIRED] Some reminder">info
                  </mat-icon>
                </mat-form-field>
              </td>
            </ng-container>
          </tr>
        </table>
      </form>
    </mat-card-content>
    <mat-card-actions align="end">
      <button mat-raised-button [disabled]="!formValues.valid" (click)="saveSeller()" color="primary">Save seller <i
        class="material-icons">send</i></button>
    </mat-card-actions>
    <mat-card-footer style="margin: 1px;">
      <i class="mat-small">create date here</i>
    </mat-card-footer>
  </mat-card>
</div>

car.component.ts

import {Component, OnInit} from '@angular/core';
import {FormControl, FormGroup, FormArray, Validators} from '@angular/forms';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-car',
  templateUrl: './car.component.html',
  styleUrls: ['./car.component.css']
})
export class CarComponent implements OnInit {
  formValues = new FormGroup({
    cars: new FormArray([
      new FormGroup({
        model: new FormControl('', [Validators.required]),
        value: new FormControl('', [Validators.required]),
        state: new FormControl('', [Validators.required])
      })
    ]),
    name: new FormControl('', [Validators.required]),
    margin: new FormControl('', [Validators.required]),
    create_date: new FormControl('')
  });

  cars = this.formValues.get('cars') as FormArray;
  sellerIdFromRoute: any;
  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    const routeParams = this.route.snapshot.paramMap;
    this.sellerIdFromRoute = String(routeParams.get('sellerId'));
    this.getSeller(this.sellerIdFromRoute);
  }

  // Form
  addRow() {
    const group = new FormGroup({
      model: new FormControl('', [Validators.required]),
      value: new FormControl('', [Validators.required]),
      state: new FormControl('', [Validators.required])
    });

    this.cars.push(group);
  }

  removeRow(i: number) {
    this.cars.removeAt(i);
  }

  saveSeller(){
    // POSTS TO API, THIS WORKS
    alert('added/updated seller');
    console.log(this.formValues.value);
  }

  getSeller(id) {
    // This is an example response from my API
    const example: Seller = {
      _id: id,
      name: 'Used Ford salesman',
      margin: 22,
      create_date: {
        $date: '2022-05-20T19:45:22.715Z'
      },
      cars: [
        {
          model: 'Estate',
          value: 12345,
          state: 'new'
        },
        {
          model: 'Saloon',
          value: 12345,
          state: 'used'
        },
      ]
    };
    console.log(example);
    return example;
  }

}


export interface Cars {
  model: string;
  value: number;
  state: string;
}

export interface Seller {
  _id: string;
  name: string;
  margin: number;
  create_date: any;
  cars: Array<Cars>;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

肩上的翅膀 2025-02-06 15:32:12

正如Angular警告您的那样,混合模板驱动的表单反应性形式这是一种不好的做法官方Angular Doc )。

要用反应性式(您的情况)填充表单控制值,您必须在组件初始化中设置值。您可以同时设置所有FormGroup值:

  ngOnInit() {
    ...
    this.formValues.setValue({
      name: "Test", 
      margin: "5",
      cars: [{ model: 'Mercedes', value: 'x', state: 'state'}],
      create_date: "01/01/2022"
    });
  }

使用PatchValue方法,而不是setValue如果您不想初始化所有FormControls,则在此处。

或者,也可以直接在FormControl上设置值:

this.formValues.get('name')?.setValue('newName'); // or patchValue

我个人更喜欢在不同领域中分别管理FormControls,它为您提供了更大的灵活性和更多的控制,以充分利用反应性表单API的丰富性。

As Angular warns you, it's a bad practice to mix Template driven form and Reactive form this can lead to a lot of unwanted behaviours (you can get a lot of informations about Angular forms in the official Angular doc).

To fill form control values with Reactive form (your case), you have to set values at your component initialization. You can set all formGroup values at the same time :

  ngOnInit() {
    ...
    this.formValues.setValue({
      name: "Test", 
      margin: "5",
      cars: [{ model: 'Mercedes', value: 'x', state: 'state'}],
      create_date: "01/01/2022"
    });
  }

Use patchValue method instead of setValue here if you don't want to initialise all formControls.

Or, it's also possible to set value directly on formControl :

this.formValues.get('name')?.setValue('newName'); // or patchValue

Personally, I prefer managing formControls separately in different fields, it gives you more flexibility and more control in order to take full advantage of the richness of the reactive forms api.

倾城花音 2025-02-06 15:32:12

不确定这是否是最好的方法,但最终使用了以下方式:

  ngOnInit() {
    const routeParams = this.route.snapshot.paramMap;
    this.sellerIdFromRoute = String(routeParams.get('sellerId'));
    const valuesFromAPI = this.getSeller(this.sellerIdFromRoute);
    this.addCars(valuesFromAPI.cars);
    this.formValues.patchValue({
      name: valuesFromAPI.name,
      margin: valuesFromAPI.margin,
      create_date: valuesFromAPI.cars
    });
  }

  addCars(cars: Cars[]) {
    cars.forEach(car => {
      const group = new FormGroup({
        model: new FormControl(car.model, [Validators.required]),
        value: new FormControl(car.value, [Validators.required]),
        state: new FormControl(car.state, [Validators.required])
      });
      this.cars.push(group);
    });
  }

Not sure if this is the best way, but ended up using this:

  ngOnInit() {
    const routeParams = this.route.snapshot.paramMap;
    this.sellerIdFromRoute = String(routeParams.get('sellerId'));
    const valuesFromAPI = this.getSeller(this.sellerIdFromRoute);
    this.addCars(valuesFromAPI.cars);
    this.formValues.patchValue({
      name: valuesFromAPI.name,
      margin: valuesFromAPI.margin,
      create_date: valuesFromAPI.cars
    });
  }

  addCars(cars: Cars[]) {
    cars.forEach(car => {
      const group = new FormGroup({
        model: new FormControl(car.model, [Validators.required]),
        value: new FormControl(car.value, [Validators.required]),
        state: new FormControl(car.state, [Validators.required])
      });
      this.cars.push(group);
    });
  }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文