未触摸时,Angular 7 选择下拉对象为空 [英] Angular 7 select dropdown object is empty when not touched

查看:24
本文介绍了未触摸时,Angular 7 选择下拉对象为空的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用程序中,ngOnInit() 加载货币、类别和制造商.我为此使用了 Angular 7 反应形式.数据按预期加载,下拉列表填充了值和第一个选择并显示给用户的选项.所以,问题来了,在完成表单并点击提交(使用默认下拉值)后,我看到 categorycurrency 的空对象制造商.

ProductNewComponent.ts

import {Component, OnInit} from '@angular/core';从 '@angular/router' 导入 {Router};从'../service/product.service'导入{ProductService};从@angular/forms"导入 {FormControl、FormGroup、Validators};从../../../app.constants"导入 {CATEGORY_API_URL, CURRENCY_API_URL, MANUFACTURER_API_URL, PRODUCT_API_URL, SERVER_URL};从'../model/product'导入{产品};从 '../model/price' 导入 {Price};从'../../category/service/category.service'导入{CategoryService};从../../category/model/category"导入{类别};从'../model/currency'导入{Currency};从'../model/manufacturer'导入{制造商};@成分({选择器:'app-product-new',templateUrl: './product-new.component.html',styleUrls: ['./product-new.component.css']})导出类 ProductNewComponent 实现 OnInit{类别:数组<类别>;货币:数组<货币>;制造商:Array<制造商>;productForm = new FormGroup({id: new FormControl({value:'',disabled:true}, Validators.minLength(2)),名称:new FormControl(''),描述:new FormControl(''),价格:new FormControl(''),数量:new FormControl(''),类别控件:新表单控件(''),货币控制:新的表单控制(''),制造商控制:新的表单控制(''),});构造函数(私有产品服务:产品服务,私有类别服务:类别服务,私有路由器:路由器){}ngOnInit(){this.loadCategories();this.loadCurrencies();this.loadManufacturers();}创建产品(){const product=new Product();product.name=this.productForm.value.name;product.description=this.productForm.value.description;product.price=new Price(this.productForm.value.currencyControl, this.productForm.value.price);product.category=this.productForm.value.categoryControl;product.manufacturer=this.productForm.value.manufacturerControl;product.createdBy='管理员';product.createdDate='';product.lastModifiedBy='管理员';product.lastModifiedDate='管理员';const url=SERVER_URL+PRODUCT_API_URL+'创建';this.productService.createProduct(url,product).subscribe(值 =>{console.log('产品创建成功');},error1 =>{console.log('创建产品失败');},()=>{this.router.navigate(['/product/list']);});}私人 loadCategories(){const url=SERVER_URL+CATEGORY_API_URL+'list';this.categoryService.getCategories(url).subscribe(类别 =>{this.categories=categories;},错误 1 ​​=>{},()=>{});}私人 loadCurrencies(){const url=SERVER_URL+CURRENCY_API_URL+'list';this.productService.getCurrencies( url ).subscribe(货币 =>{this.currencies=货币;},错误 1 ​​=>{},() =>{});}私有负载制造商(){const url=SERVER_URL+MANUFACTURER_API_URL+'list';this.productService.getManufacturers( url ).subscribe(制造商 =>{this.manufacturers=制造商;},错误 1 ​​=>{},() =>{});}制造商数据可用():布尔值{返回 this.manufacturers!==undefined;}categoryDataAvailable(): 布尔值{返回 this.categories!==undefined;}货币数据可用():布尔值{返回 this.currencies!==undefined;}回去() {this.router.navigate(['/product']);}}

product.component.html

<h2>创建新产品</h2><br/><form [formGroup]="productForm"><div class="form-group"><label for="id">产品 ID</label><input type="text" class="form-control" id="id" formControlName="id"><small id="emailHelp" class="form-text text-muted">系统自动生成</small>

<div class="form-group"><label for="name">Name</label><input type="text" class="form-control" id="name" formControlName="name" required placeholder="输入产品名称">

<div class="form-group"><label for="description">Description</label><input type="text" width="200" height="100" class="form-control" id="description" formControlName="description" required placeholder="输入产品描述">

<div class="form-group"><label for="currencyControl">价格</label><br/><标签><select class="form-control" formControlName="currencyControl" name="currencyControl" id="currencyControl"><option *ngFor="让货币的货币">{{货币名称}}</选项></选择><input formControlName="amount" id="amount" placeholder="Enter Product Price (in USD)" requiredstyle="margin: 10px; padding: 5px" type="text">

<div class="form-group"><标签>类别:<select class="form-control" name="categoryControl" formControlName="categoryControl"><option *ngFor="让类别的类别">{{分类名称}}</选项></选择>

<div class="form-group"><标签>制造商:<select class="form-control" formControlName="manufacturerControl" name="manufacturerControl"><option *ngFor="让制造商的制造商">{{生产商名称}}</选项></选择>

<button type="submit" class="btn btn-primary" (click)="createProduct()">提交</button><button type="button" class="btn btn-primary" style="margin-left: 30px" (click)="goBack()">取消</button></表单>

如果我将 select 语句更改为使用 [ngValue]="category" 并且当从服务器加载数据时,我在下拉列表中没有看到默认值并抛出错误 property ng value未由任何适用指令或选项元素提供

解决方案

我找到了解决方案.出于某种原因,当通过 patchValue() 方法应用更新时,Angular 不会更新类别选项(类别对象).基于 本文档,我实现了 compareCategoryFncompareManufacturerFn.这将旧值与新值进行比较,并使用通过 patchValue() 方法接收的新值更新下拉列表

product-edit.component.html

 

<h2>更新产品</h2><br/><form [formGroup]="productForm"><div class="form-group"><label for="id">产品 ID</label><input class="form-control" formControlName="id" id="id" name="id"type="text"><small class="form-text text-muted" id="emailHelp"></small>

<div class="form-group"><label for="name">Name</label><input class="form-control" formControlName="name" id="name" type="text">

<div class="form-group"><label for="description">Description</label><input type="text" class="form-control" formControlName="description" id="description" required>

<div class="form-group"><label for="amount">价格(美元)</label><br/><input type="text" formControlName="amount" id="amount" required >

<div class="form-group"><标签>类别:<select [compareWith]="compareCategoryFn" class="form-control"formControlName="categoryControl" name="categoryControl"><option *ngFor="让类别的类别" [ngValue]="类别">{{分类名称}}</选项></选择>

<div class="form-group"><标签>制造商:<select [compareWith]="compareManufacturerFn" class="form-control" formControlName="manufacturerControl" name="manufacturerControl"><option *ngFor="让制造商的制造商" [ngValue]="制造商">{{生产商名称}}</选项></选择>

<button (click)="updateProduct()" class="btn btn-primary" type="submit">Update</button><button (click)="goBack()" class="btn btn-primary" style="margin-left: 30px" type="button">取消</button></表单>

product-edit.component.ts

 import {Component, OnInit} from '@angular/core';从'../model/product'导入{产品};从'../service/product.service'导入{ProductService};从@angular/router"导入 {ActivatedRoute, Router};从../../../app.constants"导入 {CATEGORY_API_URL, CURRENCY_API_URL, MANUFACTURER_API_URL, PRODUCT_API_URL, SERVER_URL};从@angular/forms"导入 {FormControl, FormGroup};从'../model/currency'导入{Currency};从../../category/model/category"导入{类别};从'../../manufacturer/model/manufacturer'导入{制造商};从'../../category/service/category.service'导入{CategoryService};@成分( {选择器:'app-product-edit',templateUrl: './product-edit.component.html',styleUrls: ['./product-edit.component.css']})导出类 ProductEditComponent 实现 OnInit{产品:产品;类别:数组<类别>;货币:数组<货币>;制造商:Array<制造商>;产品表格=新表格组({id: new FormControl( {value: '', disabled: true} ),名称:new FormControl( '' ),描述:new FormControl( '' ),价格:新的FormControl(''),数量:新的FormControl(''),类别控件:新的表单控件(空),货币:new FormControl(''),制造商控制:新的表单控制(空)});构造函数(私有产品服务:产品服务,私有类别服务:类别服务,私人路线:ActivatedRoute,专用路由器:路由器){}ngOnInit(){this.loadCategories();this.loadCurrencies();this.loadManufacturers();this.getProduct();}私有 getProduct(){const id=this.route.snapshot.paramMap.get('id');const url=SERVER_URL+PRODUCT_API_URL+'find/'+id;this.productService.getProductDetails( url ).pipe().订阅(数据 =>{this.product=数据;this.productForm.patchValue({id:data.id,名称:data.name,描述:data.description,价格:数据价格,金额:数据.价格.金额,货币:data.price.currency,类别控制:data.category,制造商控制:data.manufacturer,});},错误 =>{控制台日志(错误);},() =>console.log('getProduct() 成功'));}私人更新产品(){const id=this.route.snapshot.paramMap.get('id');const url=SERVER_URL+PRODUCT_API_URL+'更新';const product = new Product();product.id=Number( id );product.name=this.productForm.value.name;product.description=this.productForm.value.description;product.category=this.productForm.value.categoryControl;product.manufacturer=this.productForm.value.manufacturerControl;product.price=this.productForm.value.price;product.price.amount=this.productForm.value.amount;product.lastModifiedBy='管理员';product.lastModifiedDate='管理员';this.productService.updateProduct( url, product ).subscribe(值 =>{console.log('产品更新成功');}, error1 =>{console.log('更新产品失败');},() =>{this.router.navigate( ['/product/list'] );});}私人 loadCategories(){const url=SERVER_URL+CATEGORY_API_URL+'list';this.categoryService.getCategories( url ).subscribe(类别 =>{this.categories=categories;console.log( '成功加载类别' );},错误 1 ​​=>{console.log('加载分类失败');},() =>{});}私人 loadCurrencies(){const url=SERVER_URL+CURRENCY_API_URL+'list';this.productService.getCurrencies( url ).subscribe(货币 =>{this.currencies=货币;},错误 1 ​​=>{console.log('加载货币失败');},() =>{});}私有负载制造商(){const url=SERVER_URL+MANUFACTURER_API_URL+'list';this.productService.getManufacturers( url ).subscribe(制造商 =>{this.manufacturers=制造商;console.log('成功加载制造商');},错误 1 ​​=>{console.log('加载制造商失败');},() =>{});}productDataAvailable(): 布尔值{返回 this.product!==undefined;}compareCategoryFn(c1: Category, c2: Category): 布尔值{返回 c1 &&c2 ?c1.id === c2.id : c1 === c2;}compareManufacturerFn(c1:制造商,c2:制造商):布尔值{返回 c1 &&c2 ?c1.id === c2.id : c1 === c2;}回去(){this.router.navigate( ['/product/list'] );}}

In my application,ngOnInit() loads currencies, categories, and manufacturers. I am using Angular 7 reactive forms for this. Data loads as expected and dropdowns populated with values and the first option selected and showed to the user. So, here is the problem, after finishing form and clicking on submit (using default dropdown values), I see an empty object for the category, currency, and manufacturer.

ProductNewComponent.ts

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

@Component({
  selector: 'app-product-new',
  templateUrl: './product-new.component.html',
  styleUrls: ['./product-new.component.css']
})
export class ProductNewComponent implements OnInit
{
  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(''),
    amount: new FormControl(''),
    categoryControl: new FormControl(''),
    currencyControl: new FormControl( '' ),
    manufacturerControl: new FormControl( '' ),
  });
  constructor(private productService:ProductService, private categoryService:CategoryService,private router:Router) {}

  ngOnInit()
  {
    this.loadCategories();
    this.loadCurrencies();
    this.loadManufacturers();
  }


  createProduct()
  {
    const product=new Product();
    product.name=this.productForm.value.name;
    product.description=this.productForm.value.description;
    product.price=new Price(this.productForm.value.currencyControl, this.productForm.value.price);
    product.category=this.productForm.value.categoryControl;
    product.manufacturer=this.productForm.value.manufacturerControl;

    product.createdBy='Admin';
    product.createdDate='';
    product.lastModifiedBy='Admin';
    product.lastModifiedDate='Admin';


    const url=SERVER_URL+PRODUCT_API_URL+'create';

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


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

    this.categoryService.getCategories(url).subscribe(
      categories =>
      {
        this.categories=categories;
      },
        error1 =>
      {
      },
      ()=>{
      });
  }

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

    this.productService.getCurrencies( url ).subscribe(
      currencies =>
      {
        this.currencies=currencies;
      },
      error1 =>
      {
      },
      () =>
      {
      } );
  }


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

    this.productService.getManufacturers( url ).subscribe(
      manufacturers =>
      {
        this.manufacturers=manufacturers;
      },
      error1 =>
      {
      },
      () =>
      {
      } );
  }

  manufacturersDataAvailable(): boolean
  {
    return this.manufacturers!==undefined;
  }

  categoriesDataAvailable(): boolean
  {
    return this.categories!==undefined;
  }

  currenciesDataAvailable(): boolean
  {
    return this.currencies!==undefined;
  }

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

product.component.html

<div>
<h2>Create New Product</h2>

<br/>
<form  [formGroup]="productForm">
  <div class="form-group">
    <label for="id">Product Id</label>
    <input type="text" class="form-control"  id="id" formControlName="id">
    <small id="emailHelp" class="form-text text-muted">Automatically generated by system</small>
  </div>
  <div class="form-group">
    <label for="name">Name</label>
    <input type="text" class="form-control"  id="name" formControlName="name" required placeholder="Enter Product Name">
  </div>
  <div class="form-group">
    <label for="description">Description</label>
    <input type="text" width="200" height="100" class="form-control"  id="description" formControlName="description" required placeholder="Enter Product Description">
  </div>
  <div class="form-group">
    <label for="currencyControl">Price</label> <br/>
    <label>
      <select class="form-control" formControlName="currencyControl" name="currencyControl" id="currencyControl">
        <option *ngFor="let currency of currencies">
          {{currency.name}}
        </option>
      </select>
    </label>
    <input formControlName="amount" id="amount" placeholder="Enter Product Price (in USD)" required
           style="margin: 10px; padding: 5px" type="text">
  </div>

  <div class="form-group">
    <label>Category:
      <select class="form-control" name="categoryControl" formControlName="categoryControl">
        <option *ngFor="let category of categories">
          {{category.name}}
        </option>
      </select>
    </label>
  </div>

  <div class="form-group">
    <label>Manufacturer:
      <select class="form-control" formControlName="manufacturerControl" name="manufacturerControl">
        <option *ngFor="let manufacturer of manufacturers">
          {{manufacturer.name}}
        </option>
      </select>
    </label>
  </div>

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

</form>

</div>

If I change the select statement to use [ngValue]="category" and when the data loaded from the server I see no default value in dropdowns and throws an error property ng value is not provided by any applicable directives nor by option element

解决方案

I found the solution. For some reason, Angular does not update the category option (category object) when the update applied through patchValue() method. Based on this documentation , I implemented compareCategoryFn and compareManufacturerFn. This compares old values with new values and updates the dropdown with the new value which is received through patchValue() method

product-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" name="id"type="text">
      <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">
    </div>
    <div class="form-group">
      <label for="description">Description</label>
      <input  type="text" class="form-control" formControlName="description" id="description" required>
    </div>
    <div class="form-group">
      <label for="amount">Price (In $)</label> <br/>
      <input type="text" formControlName="amount" id="amount"  required >
    </div>

    <div class="form-group">
      <label>Category:
        <select [compareWith]="compareCategoryFn"  class="form-control"formControlName="categoryControl" name="categoryControl">
          <option *ngFor="let category of categories" [ngValue]="category">
            {{category.name}}
          </option>
        </select>
      </label>
    </div>

    <div class="form-group">
      <label>Manufacturer:
        <select  [compareWith]="compareManufacturerFn"  class="form-control" formControlName="manufacturerControl" name="manufacturerControl">
          <option *ngFor="let manufacturer of manufacturers" [ngValue]="manufacturer">
            {{manufacturer.name}}
          </option>
        </select>
      </label>
    </div>

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

  </form>

</div>

product-edit.component.ts

 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} from '@angular/forms';
import {Currency} from '../model/currency';
import {Category} from '../../category/model/category';
import {Manufacturer} from '../../manufacturer/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} ),
               name: new FormControl( '' ),
               description: new FormControl( '' ),
               price: new FormControl( '' ),
               amount: new FormControl( '' ),
               categoryControl: new FormControl(null ),
               currency: new FormControl( '' ),
               manufacturerControl: new FormControl( null )
             } );


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

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

  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;
            this.productForm.patchValue(
              {
                      id: data.id,
                      name: data.name,
                      description: data.description,
                      price: data.price,
                      amount: data.price.amount,
                      currency: data.price.currency,
                      categoryControl: data.category,
                      manufacturerControl: data.manufacturer,
                    });
          },
          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.value.name;
    product.description=this.productForm.value.description;

    product.category=this.productForm.value.categoryControl;
    product.manufacturer=this.productForm.value.manufacturerControl;
    product.price=this.productForm.value.price;
    product.price.amount=this.productForm.value.amount;

    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 =>
      {
        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' );
      },
      () =>
      {
      } );
  }

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


  compareCategoryFn(c1: Category, c2: Category): boolean
  {
    return c1 && c2 ? c1.id === c2.id : c1 === c2;
  }

  compareManufacturerFn(c1: Manufacturer, c2: Manufacturer): boolean
  {
    return c1 && c2 ? c1.id === c2.id : c1 === c2;
  }

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

这篇关于未触摸时,Angular 7 选择下拉对象为空的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆