Martin Crawley
Martin Crawley

Reputation: 487

Angular Binding Not Updating

I'm currently learning Angular 2+ and I'm having trouble understanding how bindings work. I've googled this for about half an hour but haven't found the thing I'm missing.

I have a page with a simple button and the text on this button is bound to a property in my TypeScript based on a bool. When I click the button it should flip the bool, but my isTrackingText property doesn't change.

Here's my button in the html:

<button ion-button block (click)="startTracking()">{{ isTrackingText }}</button>

And here's my bool and the bound text property in my TypeScript

isTracking: boolean = false;
isTrackingText: string = (!this.isTracking ? "Start Tracking" : "Stop Tracking");

And finally the actual click event:

startTracking() {
    console.log("Toggle Tracking!");
    this.isTracking = !this.isTracking;
}

Update: Sorry I should have mentioned that I know I can just change it in my button click event, my question is more how I could notify my isTrackingText that it's backing field had changed.

Upvotes: 3

Views: 8374

Answers (4)

Jota.Toledo
Jota.Toledo

Reputation: 28434

You cant "notify" the text property about the backing boolean one being changed. What you can do is to either:

  1. "Intercept" the setting of the boolean property and trigger the update of the text property.
  2. Define the text property as a "calculated" property, which depends exclusively of the boolean one.

Both of this approaches use the accessors feature of typescript

The 1st approach would look like this:

private _isTracking: boolean;
get isTracking(){return this._isTracking;}
set isTracking(v: boolean){
  this._isTracking = v;
  this.trackingText = v ? ....; // your logic
}
isTrackingText: string;

constructor(){
  this.isTracking = false;
}

As you can see, this is kinda verbose.

The 2nd approach is a lot cleaner IMO:

isTracking = false;
get trackingText(){
  return this.isTracking ? ....; //your logic;
}

Notice that in this approach, as it is, every time that you "access" the trackingText member, the value is recalculated. So if the calculation is cpu-intensive, this is not an optimal approach.

Upvotes: 1

Joe Clay
Joe Clay

Reputation: 35787

The other answers have all shown how to manually sync the values, but another option would be to use a getter instead of a proper field:

isTracking: boolean = false;

get isTrackingText(): string {
    return !this.isTracking ? "Start Tracking" : "Stop Tracking";
}

This can still be accessed from the template/other code as if it were a field (i.e. you don't need to call it like a function), and the template will re-evaluate it when change detection runs. Obviously this has a little (and I mean very little) overhead compared to updating the value manually, but the convenience factor might make that worth it.

All that said, if you only use this logic in one place, I'd strongly consider just putting it in the template itself, since it's purely presentational. That's just me, though :)

Upvotes: 2

Bm8
Bm8

Reputation: 33

I think you have to update the isTrackingText variable aswell.

startTracking() {
    console.log("Toggle Tracking!");
    this.isTracking = !this.isTracking;
    this.isTrackingText = !this.isTracking ? "Start Tracking" : "Stop Tracking";
}

Upvotes: 0

Mohamed Ali RACHID
Mohamed Ali RACHID

Reputation: 3297

this line:

isTrackingText: string = (!this.isTracking ? "Start Tracking" : "Stop Tracking");

is just declaring the variable isTrackingText and assigning to it the value (!this.isTracking ? "Start Tracking" : "Stop Tracking")

So when calling the method startTracking() , only the boolean isTracking is changed , to update the isTrackingText you have to assign its value again so the code will be :

startTracking() {
    console.log("Toggle Tracking!");
    this.isTracking = !this.isTracking;
    this.isTrackingText: string = (!this.isTracking ? "Start Tracking" : "Stop Tracking");
}

Regards,

Upvotes: 0

Related Questions