1
  myApp.component('example', {
    template: '<button type="button" ng-click="$ctrl.click()">click me</button>',
    bindings: { value: '=', callback: '&' },
    controller: function () {
      this.click = function () {
        this.value = 'clicked';
        this.callback();
      }.bind(this);
    },
  });

  myApp.component('useExample', {
    template: '<example value="$ctrl.value" callback="$ctrl.callback()"></example>',
    controller: function () {
      this.callback = function () { alert(this.value); }.bind(this);
    },
  });

Here are two components, while the second one use the first one.

The first component change this.value and then call callback. But when the second one alert(this.value), it got empty value instead of 'clicked' first time. It seems that the this.value in useExample did not be updated when the callback is triggered.

I want get new value instead of old one.

I have attempted to change this.callback() in example to something like $timeout(function () { this.callback(); }.bind(this), 0), and it works. But I think there should be some better way to do so.

So, my question is what is the best way I should do to make useExample read new this.value in the callback.

-- Update 1 --

I would prefer not to change the given interface.

-- Update 2 --

aha, i just searched out this topic: AngularJS: Parent scope is not updated in directive (with isolated scope) two way binding . It seems that this question is duplicate to that one. and i have read posts on that question, it seems $timeout is the best(?) way, wt*.

tschumann
  • 2,776
  • 3
  • 26
  • 42
tsh
  • 4,263
  • 5
  • 28
  • 47
  • Possible duplicate of [AngularJS: Parent scope is not updated in directive (with isolated scope) two way binding](http://stackoverflow.com/questions/22557599/angularjs-parent-scope-is-not-updated-in-directive-with-isolated-scope-two-wa) – tsh Dec 29 '16 at 02:03

1 Answers1

0

The problem is that the watcher that binds the value from child scope to parent scope executes on a micro-thread (fiber) subsequent to the invocation of the function in the expression binding.

The solution is to expose the value as a local in the expression binding:

myApp.component('example', {
    template: '<button type="button" ng-click="$ctrl.click()">click me</button>',
    bindings: { 
        callback: '&' 
    },
    controller: function () {
      this.click =  () => {
        this.value = 'clicked';
        //EXPOSE this.value as $value
        this.callback({$value: this.value});
      };
    },
});

In the above example, the value is exposed as $value.

Use the exposed value as an argument of callback function:

myApp.component('useExample', {
    template: '<example callback="$ctrl.useCallback($value)"></example>',
    controller: function () {
      this.useCallback = (v) => { alert(v); };
    },
});

Because the value is provided as an argument of the callback, the value is immediately available.

The DEMO on JSFiddle.

georgeawg
  • 48,608
  • 13
  • 72
  • 95
  • pass the value in callback is not what i want. since it would change the interface of my component, and make caller more confuse. (caller is not written by me) – tsh Dec 29 '16 at 01:25
  • I just want it works as what `` do, which looks better to me and seems to be more 'stander' way. – tsh Dec 29 '16 at 01:30