简体   繁体   中英

Angular2 NgStyle not updating on click

The component has a input from its parent and has an output event for this input object. The NgStyle takes a style object from the component for styling. The on click event changed the boolean but the style object is not updated accordingly, hence the color remain unchanged. I have to reassign a new object in order for the NgStyle to see the new boolean value. What am I doing wrong?

ProductItemComponent.ts:

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import {Product} from '../../model/product';

@Component({
selector: 'app-product-item',
templateUrl: './product-item.component.html',
styleUrls: ['./product-item.component.css']
})

export class ProductItemComponent implements OnInit {

  @Input() public product!:Product;
  @Output() private updateStatus: EventEmitter<Product>;

  public statusTextStyle:object;
  public classStyle:object;

  constructor() {
    this.updateStatus = new EventEmitter<Product>();
  }


 ngOnInit(): void {

    this.statusTextStyle = {
       // if true color green, if false red
      "color" : this.product.getStatus() ? "var(--ok)" : "var(--danger)"
    }

 }

  onUpdateStatus(event){
    this.updateStatus.emit(this.product);

    // have to reassign a new object to change style
    this.statusTextStyle = {
      "color" : this.product.getStatus() ? "var(--ok)" : "var(--danger)"
    }

    console.log(this.statusTextStyle);
  }

}

Product-item.component.html:

<div class="productContainer">
  <div class="overlay">
      <div [ngStyle]="statusTextStyle">{{product.name}}</div>
      <hr>
      <div>Status: {{product.getStatus()}}</div>
      <div>Price: {{product.price}}</div>
      <br>
      <div>Code: {{product.code}}</div>
      <br>
      <div>Stock: {{product.quantity}}</div>
      <hr>
      <button (click)="onUpdateStatus($event)">UPDATE STATUS</button>
  </div>
</div>

Parent:

import { Component, OnInit } from '@angular/core';
import { Product as ProductModel} from '../../model/product';

@Component({
   selector: 'app-product-list',
   templateUrl: './product-list.component.html',
   styleUrls: ['./product-list.component.css']
 })

  export class ProductListComponent implements OnInit {

  products: Array<ProductModel>;

  constructor() { }

   ngOnInit(): void {
     this.products = [
       new ProductModel('A','1',3,40),
       new ProductModel('B','2',4,50),
       new ProductModel('C','3',22,41),
       new ProductModel('E','4',342,433),
       new ProductModel('Z','5',33,477)
     ]
   }

   onUpdateStatus(product:ProductModel){

     product.setStatus(!product.getStatus());
     console.log(product.getStatus());

   }

 }

Parent html:

<div class="productList">
<div *ngFor="let product of products; let index as i;">

    <app-product-item [product]="product" (updateStatus)="onUpdateStatus($event)"></app-product-item>
    
</div>

Product Model:

  export class Product {

  private _status:boolean = false;

  constructor(public name:string,
              public code:string,
              public price:number,
              public quantity:number){}

  public getStatus():boolean{
      return this._status;
  }

  public setStatus(status:boolean):void{
      this._status = status;
    }
  }

if this.statusTextStyle is not reassigned with a new object (recall the getStatus()), the NgStyle use the style set in ngOnInit在此处输入图像描述

Stackblitz demo

@CloudWave, you need initialize the output: @Output() private updateStatus: EventEmitter<Product>=new EventEmitter<Product>();

But if you check your code, see that you don't change the statusTextStyle in any place else ngOnInit. The ngOnInit happens only when the component is painted at first time (or if you has a component under a *ngFor and change the array of under a *ngIf also).

You can check that work if you uncomment the lines under your //reassign object only works .

Another way is make a getter

get statusTextStyle(){
    return {
      color: this.product.getStatus() ? "var(--ok)" : "var(--danger)"
    };
  }

Your forked stackblitz using a getter

NOTE: When you pass an object to an Input, you need'nt pass to the @Output the product, any change in the object -in child or in parent- change the apaarence

Update well the note is not clear. I want to say that when you use as @Input with an object the object is the same always. You can change one property of the object anywhere -in parent or in child- So, we can do in child

  onUpdateStatus(event) {
    this.product.setStatus(!this.product.getStatus());
  }

We neend't change the "product" in parent and we needn't use an @Output.

Bro I want to add my piece of code here and its running perfectly maybe it will help you.

 Style1={
     fontSize: '3em',
     backgroundColor: this.CardData > 1 || this._ProductQuantity > 1 ? 'ivory' : "orange",
     color: this.CardData > 1 || this._ProductQuantity > 1 ? "green" : "red",
     'fa': true,
     'far fa-check-square': this.CardData > 1 || this._ProductQuantity > 1, 
     'far fa-times-circle': this.CardData < 1 || this._ProductQuantity < 1

   };

Instead of using the 'var(--ok)' I am using direct values of color. If it helps you please tick my answer

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM