Expression has changed after it was checked. Previ

2019-09-19 19:10发布

问题:

I have this html code, in this code I used [(ngModel)] for change my value input, and when I change I want to calculate automatic my Total, subtotal and Amound Paid. Maybe this is not good solution because I have some problem.

  1. When I add some data, my all data chnage value, all get the last set value. Like in photo,

Please I need help.

My html code

 <form [formGroup]="addsale" (ngSubmit)="onaddsale()">
      <table align="center" class="table table-bordered table-hover">
        <thead>
          <tr style="color:black;">
            <th>Unit_price</th>
            <th>Quantity</th>
            <th>Description</th>
            <th>Subtotal</th>
            <th>Actions</th>
          </tr>
        </thead>
        <tbody>
          <tr class="group" style="cursor: pointer" *ngFor="let item of products; let i = index">
             <td>
              <input formControlName="Unit_price" id="Unit_price " type="number" class="validate"[(ngModel)]="item.Unit_price">
            </td>
            <td>
              <input formControlName="Quantity" id="Quantity " type="number" class="validate"  [(ngModel)]="item.Quantity"> 
            </td>
            <td>
              <input type="text" formControlName="Description" id="Description" name="Description" [(ngModel)]="item.Description">
            </td>
            <td>
              <input formControlName="Subtotal" id="Subtotal" type="number" class="validate"  [(ngModel)]="item.Subtotal">
            </td>
            <td>
              <button type="button" class="fa" (click)="onDelete(i)">x</button>
            </td>
          </tr>
        </tbody>
      </table>
      <br>
      <br>
      <div class="row">
        <div class="input-field col s2" style="float: right;">
          <label for="total">Total {{total}} ALL</label>
          <input formControlName="total" id="total" type="text" class="validate"   [(ngModel)]="total">
        </div>
        <div class="input-field col s2" style="float: right;">
          <label for="amount_paid">Amount Paid:</label>
          <input formControlName="amount_paid" id="amount_paid" [(ngModel)]="total" type="text" class="validate">
        </div>
        <div class="input-field col s2" style="float: right;">
          <label for="total">Subtotal</label>
          <input formControlName="Subtotal" id="Subtotal" type="text" class="validate"  [(ngModel)]="total">
        </div>
      </div>

      <hr>
      <br>
      <div id="add_homebox_button_container" class="row" style="float: right;">
        <button id="add_client_button" type="submit" class="btn waves-effect waves-light">
          Register
        </button>
      </div>
    </form>

Code ts:

export class AddSaleFormComponent implements OnInit {
  addsale: FormGroup;
  loading: boolean = false;
  client: Client[];
  producttype: ProductType[];
  contrat: Contrat[];
  gpss: GPS[];
  homeboxp: HomeboxP[];
  sensors: Sensors[];
  homebox: Homebox[];
  products: Products[] = [];
  Price: number = 0;
  Total;
  variable: any;
  Quantity: number;
  Unit_price: number;

  selectedClient: Client = new Client('');
  @Input() selectedProduct: Products;

  constructor(
    private router: Router,
    private fb: FormBuilder,
    private ss: SalesService,
    private ps: ProductsService,
    private cs: ClientService,
    private pts: ProducttypeService,
    private css: ContratService,
    private gps: GpsService,
    private hbp: HomeboxpackageService,
    private sensor: SensorsService,
    private hb: HomeboxService

  ) {
    this.addsale = this.fb.group({
      'invoice_number': new FormControl('', [Validators.required, Validators.nullValidator]),
      'invoice_date': new FormControl('', Validators.required),
      'client_id': new FormControl('', Validators.required),
      'amount_paid': new FormControl('', Validators.required),
      'notes': new FormControl('', Validators.required),
      'Subtotal': new FormControl('', Validators.required),
      'products': this.fb.array([]),
      'total': new FormControl('', Validators.required),
      'contactNo': new FormControl('', Validators.required),
      'address': new FormControl('', Validators.required),
      'Unit_price': new FormControl('', Validators.required),
      'Quantity': new FormControl('', Validators.required),
      'Description': new FormControl('', Validators.required),
      'line_num': new FormControl('', Validators.required)
    });
  }

  ngOnInit() {

    this.products = this.ps.getProduct();
    console.log(this.products)

    this.pts.getAllProductType().subscribe(
      producttype => {
        this.producttype = producttype;
      }
    );
  }


  onaddsale() {
    this.loading = true;
    let sale = this.addsale.value
    sale.products = this.products

    let newSale = new Sale(sale);
    console.log(newSale)

    this.ss.saleitemcreate(newSale).subscribe(
      result => {
        if (result === true) {
          Materialize.toast('Sale saved successfully', 4000);
        } else {
          this.loading = false;
        }
      },
      error => {
        this.loading = false;
      }
    );
  }

  get total() {
    let Total = 0;
    for (let p of this.products) {
      Total += p.Unit_price * p.Quantity;
    }
    return Total;
  }
  Subtotal() {
    let subtotal = 0;
    for (let p of this.products) {
      subtotal = p.Unit_price * p.Quantity;
    }
    console.log(subtotal)
    return subtotal;
  }

}

回答1:

Your problem is the FormGroup. As you can see, you've got one FormControl for 'Unit_price'. However, in your template, you've got several rows, and in each row there is an input that points to that same FormControl (via the FormControlName directive). But they all belong to the same FormGroup, and that messes with the form, so all the inputs with the same name refer to the same FormControl, causing the odd behaviour you're suffering. You can't have duplicate controls under the same FormGroup directive.

What you've got to do is define a FormGroup for each row of the table. This way, you'll have an array of FormGroup. In each of them, there will be a FormControl with a unique name, and so your problems will disappear.

This means you've got to make a FormGroup for each product in the products array, and maybe a different FormGroup for the common controls (total, Subtotal...).

Of course, this means that the products array in the FormGroup no longer is necessary.

Another advice is to remove data binding from the 'total' fields, as you won't edit them. You can change [(ngModel)] to just [ngModel] for them.

In fact, though I'm not an expert in reactive forms, I think that, if you're using FormControl and FormGroup, you don't need ngModel at all.