当字段未被触及时,角度反应形式返回空值

问题描述 投票:1回答:1

在我的用例中,当用户单击编辑按钮时,Angular会对后端进行HTTP调用并检索该对象,然后在EDIT表单上填充这些值。用户可以更新或保持字段不变。单击update按钮时,Angular应该获取表单中存在的所有值并将它们发送到后端。因此,问题是,在将值加载到编辑页面表单并更新某些字段并保持一些字段不变时,将未触摸的值清空。这真的很奇怪

产品edit.component.html

<div *ngIf="productDataAvailable()">
  <h2>Update Product</h2>
  <br/>
  <form [formGroup]="productForm">
    <div class="form-group">
      <label for="id">Product Id</label>
      <input class="form-control" formControlName="id" id="id" type="text" value="{{product.id}}">

      <small class="form-text text-muted" id="emailHelp"></small>
    </div>
    <div class="form-group">
      <label for="name">Name</label>
      <input class="form-control" formControlName="name" id="name" type="text" value="{{product.name}}">
    </div>
    <div class="form-group">
      <label for="description">Description</label>
      <input class="form-control" formControlName="description" height="100" id="description" required type="text"
             [value]="product.description" width="200">
    </div>
    <div class="form-group">
      <label for="currency">Price</label> <br/>
      <label>
        <select (load)="loadCurrencies()" class="form-control"  [value]="product.price.currency.symbol" formControlName="currency" id="currency" name="currency">
          <option *ngFor="let currency of currencies" value={{currency.id}}>
            {{currency.name}}
          </option>
        </select>
      </label>
      <input formControlName="price" id="price" required style="margin: 10px; padding: 5px" type="text" [value]="product.price.amount">
    </div>

    <div class="form-group">
      <label>Category:
        <select (load)="loadCategories()" class="form-control" formControlName="category" name="category">
          <option  [value]="category.id" *ngFor="let category of categories">
            {{category.name}}
          </option>
        </select>
      </label>
    </div>

    <div class="form-group">
      <label>Manufacturer:
        <select (load)="loadManufacturers()" class="form-control" [value]="product.manufacturer.name" formControlName="manufacturer" name="manufacturer">
          <option [value]="manufacturer.id" *ngFor="let manufacturer of manufacturers" >
            {{manufacturer.name}}
          </option>
        </select>
      </label>
    </div>

    <button (click)="updateProduct()" class="btn btn-primary" type="submit">Submit</button>
    <button (click)="goBack()" class="btn btn-primary" style="margin-left: 30px" type="button">Cancel</button>

  </form>

</div>

ProductEditComponent

import {Component, OnInit} from '@angular/core';
import {Product} from '../model/product';
import {ProductService} from '../service/product.service';
import {ActivatedRoute, Router} from '@angular/router';
import {CATEGORY_API_URL, CURRENCY_API_URL, MANUFACTURER_API_URL, PRODUCT_API_URL, SERVER_URL} from '../../../app.constants';
import {FormControl, FormGroup, Validators} from '@angular/forms';
import {Price} from '../model/price';
import {Currency} from '../model/currency';
import {Category} from '../../category/model/category';
import {Manufacturer} from '../model/manufacturer';
import {CategoryService} from '../../category/service/category.service';

@Component( {
              selector: 'app-product-edit',
              templateUrl: './product-edit.component.html',
              styleUrls: ['./product-edit.component.css']
            } )
export class ProductEditComponent implements OnInit
{
  product: Product;
  categories: Array<Category>;
  currencies: Array<Currency>;
  manufacturers: Array<Manufacturer>;

  productForm=new FormGroup( {
                               id: new FormControl( {value: '', disabled: true}, Validators.minLength( 2 ) ),
                               name: new FormControl( '' ),
                               description: new FormControl( '' ),
                               price: new FormControl( '' ),
                               category: new FormControl( '' ),
                               currency: new FormControl( '' ),
                               manufacturer: new FormControl( '' )
                             } );

  constructor(private productService: ProductService,
              private categoryService: CategoryService,
              private route: ActivatedRoute,
              private router: Router)
  {
  }

  ngOnInit()
  {

    this.getProduct();
    this.loadCategories();
    this.loadCurrencies();
    this.loadManufacturers();
  }

  productDataAvailable(): boolean
  {
    return this.product!==undefined;
  }

  goBack()
  {
    this.router.navigate( ['/product'] );
  }

  private getProduct()
  {
    const id=this.route.snapshot.paramMap.get( 'id' );
    const url=SERVER_URL+PRODUCT_API_URL+'find/'+id;
    this.productService.getProductDetails( url ).pipe()
        .subscribe(
          data =>
          {
            this.product=data;
          },
          error =>
          {
            console.log( error );
          },
          () => console.log( 'getProduct() success' ) );
  }

  private updateProduct()
  {
    const id=this.route.snapshot.paramMap.get( 'id' );
    const url=SERVER_URL+PRODUCT_API_URL+'update';

    const product=new Product();
    product.id=Number( id );
    product.name=this.productForm.get( 'name' ).value;
    product.description=this.productForm.get( 'description' ).value;
    const currency=new Currency( this.productForm.get( 'currency' ).value, 'USD', '$' );
    product.price=new Price(currency , this.productForm.get( 'price' ).value );
    product.category=new Category( this.productForm.get( 'category' ).value );
    product.manufacturer=new Manufacturer( this.productForm.get( 'manufacturer' ).value );
    product.lastModifiedBy='Admin';
    product.lastModifiedDate='Admin';

    this.productService.updateProduct( url, product ).subscribe(
      value =>
      {
        console.log( 'Successfully updated product' );
      }, error1 =>
      {
        console.log( 'Failed to update product' );
      },
      () =>
      {
        this.router.navigate( ['/product/list'] );
      } );
  }

  private loadCategories()
  {
    const url=SERVER_URL+CATEGORY_API_URL+'list';

    this.categoryService.getCategories( url ).subscribe(
      categories =>
      {
        // @ts-ignore
        this.categories=categories;
        console.log( 'Successfully loaded categories' );
      },
      error1 =>
      {
        console.log( 'Failed to load categories' );
      },
      () =>
      {
      } );
  }

  private loadCurrencies()
  {
    const url=SERVER_URL+CURRENCY_API_URL+'list';

    this.productService.getCurrencies( url ).subscribe(
      currencies =>
      {
        this.currencies=currencies;
      },
      error1 =>
      {
        console.log( 'Failed to load currencies' );
      },
      () =>
      {
      } );
  }

  private loadManufacturers()
  {
    const url=SERVER_URL+MANUFACTURER_API_URL+'list';

    this.productService.getManufacturers( url ).subscribe(
      manufacturers =>
      {
        this.manufacturers=manufacturers;
        console.log( 'Successfully loaded manufacturers' );
      },
      error1 =>
      {
        console.log( 'Failed to load manufacturers' );
      },
      () =>
      {
      } );
  }
}

角度版本

Angular CLI: 7.3.8
Node: 10.15.0
OS: darwin x64
Angular: 7.2.12
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.13.8
@angular-devkit/build-angular     0.13.8
@angular-devkit/build-optimizer   0.13.8
@angular-devkit/build-webpack     0.13.8
@angular-devkit/core              7.3.8
@angular-devkit/schematics        7.3.8
@angular/cli                      7.3.8
@ngtools/webpack                  7.3.8
@schematics/angular               7.3.8
@schematics/update                0.13.8
rxjs                              6.4.0
typescript                        3.2.4
webpack                           4.29.0
angular angular6 angular7 angular-httpclient
1个回答
3
投票

据我所知,您已经发出HTTP请求以从您的服务器获取数据,但您没有以正确的方式填充您的productForm FormGroup。由于您使用的是反应形式,我强烈建议您使用patchValuesetValue来更新FormControls。

对于你的情况,我会推荐patchValue,因为它比setValue更灵活。 patchValue不要求在参数中指定所有FormControls以更新/设置表单控件的值。

这是你如何使用patchValue。在你的getProduct()方法中,你可以通过这样做将datagetProductDetails()响应中的属性传递给你的FormGroup;

getProduct() {
  const id = this.route.snapshot.paramMap.get('id');
  const url = SERVER_URL + PRODUCT_API_URL + 'find/' + id;
  this.productService.getProductDetails(url).pipe()
    .subscribe(
      data => {
        this.productForm.patchValue({
          id: data.id
          name: data.name
          description: data.description
          // other form fields
        })
      },
      error => {
        console.log(error);
      },
      () => console.log('getProduct() success'));
}

另外,在你的模板html上,没有必要在每个value<input>上绑定你的<select>属性。您可以删除所有这些。这是因为,您已经使用patchValue更新了值。

<div class="form-group">
  <label for="name">Name</label>
  <input class="form-control" formControlName="name" id="name" type="text">
</div>
<div class="form-group">
  <label for="description">Description</label>
  <input class="form-control" formControlName="description" height="100" id="description" required type="text" width="200">
</div>

当您需要从productForm获取数据时,可以使用在FormGroup和FormControls上公开的value属性。

updateProduct() {
  const id = this.route.snapshot.paramMap.get('id');
  const url = SERVER_URL + PRODUCT_API_URL + 'update';

  //console.log(this.productFrom.value) 
  const product = this.productForm.value

  this.productService.updateProduct(url, product).subscribe(
    value => {
      console.log('Successfully updated product');
    }, error1 => {
      console.log('Failed to update product');
    },
    () => {
      this.router.navigate(['/product/list']);
    });
}
© www.soinside.com 2019 - 2024. All rights reserved.