Search code examples
angularangular2-templateangular2-directives

Component template expression not updating


In my main view I inject a component, on initial load this shows the correct integer, but if I were to update that value later on in my main view the console log message attached to the function displays the correct integer but the actual html value does not update. Whats going on here?

main.js:

@Component({
    selector: 'step-one',
    directives: [priceBox],
    templateUrl: 'step-one/step-one.html'
})

export class stepOne {

    constructor(@Inject(Router) router, @Inject(Http) http, @Inject(RouteParams) params, @Inject(priceBox) pbox) {
        let cid = parseInt(params.get('country'));

        pbox.price = 200;

        ...

price-box.js

import {Component} from 'angular2/core';

@Component({
    selector: 'price-box',
    template: `
        <div>
            <h1>PRICEBOX</h1>
            <p>{{totalPrice}}</p>
        </div>
        `
})

export class priceBox {
    constructor() {
        this.totalPrice = '130';
    }

    set price(val){
        this.totalPrice = val;
        console.log('SET price box: price = ' + this.totalPrice);
    }
}

So just to reiterate: On load of the page my price box element shows the price as being 130... when I try and set a new value via pbox.price = 200 the value stays at 130 but I get the console log message saying SET price box: price = 200

Thanks!


Solution

  • The way you inject the child component (priceBox) into the parent one is a bit strange.

    If you want to reference a component within a parent one, you should leverage the ViewChild decorator:

    @Component({
      selector: 'someDir',
      templateUrl: 'someTemplate',
      directives: [ItemDirective]
    })
    class stepOne {
      @ViewChild(ItemDirective) viewChild:priceBox;
      ngAfterViewInit() {
        // viewChild is set
      }
    

    }

    I could also use interpolation to provide the price to your priceBox component. The latter defines an input parameter:

    @Component({
      selector: 'price-box',
      template: `
          <div>
              <h1>PRICEBOX</h1>
              <p>{{totalPrice}}</p>
          </div>
          `
    })
    export class priceBox {
      @Input('val') totalPrice = '130';
    }
    

    You can use this from your parent component:

    @Component({
      selector: 'price-box',
      template: `
        <price-box [val]="price"></price-box>
      `
    })
    export class stepOne {
      price = '200';
    }
    

    You can notice that you can also leverage two way binding for custom component. You just need to add a corresponding event:

    @Component({
      selector: 'price-box',
      template: `
          <div>
              <h1>PRICEBOX</h1>
              <p>{{totalPrice}}</p>
          </div>
          `
    })
    export class priceBox {
      @Input('val') totalPrice = '130';
      @Output('valChanged') totalPriceChanged:EventEmitter;
    
      constructor() {
        this.totalPriceChanged:EventEmitter = new EventEmitter();
      }
    
      updateTotalPrice() {
        this.totalPriceChanged.emit(this.totalPrice);
      }
    }
    

    and now in the parent component:

    @Component({
      selector: 'price-box',
      template: `
        <price-box [(val)]="price"></price-box>
      `
    })
    export class stepOne {
      price = '200';
    }
    

    Hope it helps you, Thierry