简体   繁体   中英

Angular custom directive scope not updating as expected

I have a few directives being used on a page in my TypeScript application, and each have their own controllers to manage their own scope. Everything has been working quite smoothly until this really strange bug.

I have a keypad directive being used in a payment directive which is called by my order controller. I'm going to keep my code samples minimal for simplicity.

My payment directive has a controller which has a constructor that has:

$scope.vm = this;

I use this pattern very frequently. My payment directive also has a function keypadOnChange() that is invoked from the keypad directive.

// In the payment directive:
public keypadOnChange(keypadDirective: IKeypadDirectiveController): void {

    console.log("keypad is changed");
    console.log(keypadDirective.selection);

    this.manualTenderInputValue = Number(keypadDirective.selectionToString());

    console.log(this.manualTenderInputValue);

}

The thing worth noting here is that the console displays everything I expect it to at exactly the right time.

My template for the payment directive contains:

<div ng-show="vm.keypadIsVisible" class="square_keypad">

    <div class="white"
         keypad
         onchange="vm.keypadOnChange"></div>

    </div>

<div class="item manual-tender-input-field item-white item-small-padding"
        ng-click="vm.toggleKeypadVisibility()">

    {{ vm.manualTenderInputValue }}

</div>

Again, the thing worth noting is that all functions and behavior are working as expected -- including vm.keypadIsVisible which is changed in a similar way to vm.manualTenderInputValue .

The problem is that vm.manualTenderInputValue is not getting updated. It displays the appropriate value on page load, and I can console.log() its value when it's changed, and it is correct, but I can't get the {{ }} to update.

Any thoughts on what I'm missing here?


Update

My keypad directive handles a change event, so it can tell anyone listening about the change and they can do as they please with the data. It looks like this (and spoiler: here in lies the problem...)

My keypad directive has a controller that has these noteworthy lines:

// a public variable:
public onChange: any = null;

// in the constructor:
$scope.$watch("onchange", (v) => { this.onChange = v; });

// in a function invoked by ng-click:
public selectNumber(digit: string, domId?: string): void {
    // ... some other stuff not relevant ...
    this.triggerOnChange();
}

// the triggerOnChange in the keypad directive controller:
public triggerOnChange(): void {
    if (this.onChange) {
        this.onChange(this);
    }
}

Now, back to my payment which had a template that had a line looked like:

<div class="white"
     keypad
     onchange="vm.keypadOnChange"></div>

And the payment directive controller has the keypadOnChange function included in the original post. I threw in a console.log(this) and discovered that in that context, this was referring to the instance of the keypad directive controller. And that is the problem!

But... how do I fix this ? (no pun intended)

How do I make it so that when keypadOnChange is invoked on the payment controller it still has the right this context (and can therefore update the scope)?

One final note, I'm using this keypad directive elsewhere and didn't run into this issue.

What solved my problem was ensuring that I could maintain the this context for the payment directive controller.


My payment controller constructor ended up including:

this.keypadChange = this.onKeypadChange.bind(this);

While onKeypadChange() remained the same:

public keypadOnChange(keypadDirective: IKeypadDirectiveController): void {

    // is now referring to the instance of the controller, NOT the keypad directive:
    console.log(this);
    this.manualTenderInputValue = Number(keypadDirective.selectionToString());

 }

Finally, the template that calls the keypad directive now looks like:

<div class="white" keypad onchange="vm.keypadChange"></div>

Based on comments and suggestions, I'll likely be changing some of my attribute names to avoid confusion, but this solution answers my original question.

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