21

I'm setting the the value of an input calculating two other ngModels, and that seems to be working fine. But if I inspect my ngModel, it doesn't change at all. Let me show you:

<ion-item>
    <ion-label>Total price: {{product.totalPrice}}</ion-label>
    <ion-input 
        type="number"
        [value]="product.quantity * product.price"
        [(ngModel)]="product.totalPrice" 
        [ngModelOptions]="{ standalone: true }"></ion-input>
</ion-item>

So here {{product.totalPrice}} shows the initial value, which is fine. If I change manually that input, the changes will be reflected on the expression, that is also fine. But that input will be readonly and it will be set by changing two other inputs. When I change them, I see the value on the input is updating just fine, but not the expression in the label. What's wrong there?

It's really weird because the value in the input GETS UPDATED, but not the expression {{product.totalPrice}}, I guess the value is updating because the other fields are, but those value changes never actually hit the ngModel

By the way, I'm using Ionic 2

Dino
  • 7,779
  • 12
  • 46
  • 85
Agustín
  • 1,546
  • 7
  • 22
  • 41

5 Answers5

14

Actually [] means bind data and () mean emit changes / or let say raise an event with these changes form this UI control <ion-input>. So [()] doesn't mean two way data binding. It means:

  • bind data using []
  • raise input changes ().

Check this example it shows many ways of binding data with input and how to raise changes.

bunjeeb
  • 1,096
  • 1
  • 17
  • 32
  • 3
    yeah I get that, besides, here that is working, as I just edited the post, the [value] of the input gets updated successfully, but that change never hits the ngModel. If I edit the input, the ngModel gets updated, that's what you are showing me, that works. But I need it to be updated by other inputs, like a consecuence of other input value changes – Agustín Jan 11 '17 at 22:56
  • I believe that you need to deal with totalPrice as a readonly or calculated property .. in the example I'm using a method to calculate the total. In the same method you can set the totalPrice and the change will be broadcasted to other components, expressions ...I hope that it helps! :) – bunjeeb Jan 11 '17 at 23:11
14

So I know that I'm late to the party, but seeing as none of the other answers are correct, I thought I'd add the solution in case anyone else ends up on this page.

When creating a custom component that accepts ngModel, the component must implement the ControlValueAccessor interface. Which is detailed below

interface ControlValueAccessor {  
  writeValue(obj: any): void
  registerOnChange(fn: any): void
  registerOnTouched(fn: any): void
  setDisabledState(isDisabled: boolean): void
}

In order to register a change made by the component, the component must call the onChange method provided by the registerOnChange method.

For example, if we register our onChange method like so:

 onChange: any = () => { };
 registerOnChange(fn) {
   this.onChange = (obj) => fn(obj);
 }

When ever our component makes a change to the value, we must execute the following line

this.onChange(this.value)

Hopes this helps.

Edit

When I first answered this question, for some reason I was under the impression that a custom child component was not updating the parent component. Rereading it now, it seems that either there was an issue with the ionic-input or the OP was incorrectly using it.

This answer better suits how one would create a component and have it update a property of it's parent using NgModel

Lewis Campbell
  • 343
  • 2
  • 11
  • This is actually this correct answer to **Bidirectional ngModel bindings for updating a parent value**. – Cody Jun 22 '18 at 15:45
  • Can you solve the question the OP asked using your method to illustrate how the OP would use it? – Patrick Feb 04 '20 at 23:49
  • registerOnChange only register a function to be called when the model is changed by the component. But here we need something to change the model when it's values is changed by the parent component. I have the same problem in my code. I make change in the component, it's applied. I change the model in the parent component, nothing happens – RezKesh Jan 12 '21 at 12:47
5

You could try using (ngModelChange) to effectively watch for value changes and updating the product.totalPrice. It would looke something like this.

<ion-item>
    <ion-label>Total price: {{product.totalPrice}}</ion-label>
    <ion-input 
        type="number"
        [value]="product.quantity * product.price"
        [(ngModel)]="product.totalPrice" 
        [ngModelOptions]="{ standalone: true }"></ion-input>
        (ngModelChange)="product.totalPrice = $event"
</ion-item>

If this element is wrapped in <form> element and ngForm exportAs #someForm="ngForm" you can use template reference variable and use that in the label instead. Something like this:

<ion-item>
    <ion-label>Total price: {{product.totalPrice}}</ion-label>
    <ion-input 
        type="number"
        [value]="product.quantity * product.price"
        [(ngModel)]="product.totalPrice" 
        [ngModelOptions]="{ standalone: true }"
        name="totalPrice"
        #totalPrice="ngModel"></ion-input>
</ion-item>

Hopefully this helps.

Alexander Staroselsky
  • 37,209
  • 15
  • 79
  • 91
  • 1
    The ngModelChange thing doesn't work either. It's really weird because the value in the input GETS UPDATED, but not the expression {{product.totalPrice}}, I guess the value is updating because the other fields are, but those value changes never actually hit the ngModel – Agustín Jan 11 '17 at 22:54
  • I updated the second example my answer. Try adding a ngModel exportAs attribute statement and removing the template reference variable. This is assuming this element is within some type of
    with ngForm directive in play. Let me know if that works.
    – Alexander Staroselsky Jan 11 '17 at 23:00
5

I also ran into the problem that the two-way-binding had not updated the model when changing the input of the input field. In my case I found out that the property I was binding to was a read only property. To find this out I had to split up the two-way-binding:

[ngModel]="data.name" (ngModelChange)="testMethod($event)"

and the method:

testMethod($event) {data.name = $event}

Then I got the read only exception.

Gerros
  • 688
  • 11
  • 20
0
@ViewChild('nameOfField') nameOfField: any; 

this.nameOfField.valueChanges.subscribe((v: any) => {
    this.onChange(v);
    }
rav_kom
  • 1
  • 1