Angular 7 select dropdown object is empty when not touched

两盒软妹~` 提交于 2019-12-09 18:43:44

问题


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


回答1:


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'] );
  }
}



回答2:


I think this is a duplicte of Angular 4 - Select default value in dropdown [Reactive Forms]

Problem is that you set the default value to '' at:

 categoryControl: new FormControl(''),
currencyControl: new FormControl( '' ),
manufacturerControl: new FormControl( '' ),


来源:https://stackoverflow.com/questions/55670665/angular-7-select-dropdown-object-is-empty-when-not-touched

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!