Search code examples
javascriptangularjsangular-promisees6-promise

ES6 Promise not Updating AngularJS DOM


I'm having trouble understanding angular components scope. If I do something like:

function myComponent(){
  this.data = 'Hello World';
}

let myModule = angular.module('myModule', []);

myModule.component('myComponent', {
  template: `<div>{{$ctrl.data}}</div>`,
  controller: myComponent
});
    <script data-require="[email protected]" data-semver="1.5.8" src="https://opensource.keycdn.com/angularjs/1.5.8/angular.min.js"></script>
<div ng-app="myModule">
  <my-component></my-component>
</div>

It prints it just fine... Now, if I do a small modification and make it async:

function myComponent(){
  Promise.resolve().then(_ => {
    this.data = 'Hello World';
  });
}

let myModule = angular.module('myModule', []);

myModule.component('myComponent', {
  template: `<div>{{$ctrl.data}}</div>`,
  controller: myComponent
});
<script data-require="[email protected]" data-semver="1.5.8" src="https://opensource.keycdn.com/angularjs/1.5.8/angular.min.js"></script>
<div ng-app="myModule">
  <my-component></my-component>
</div>

It doesn't print anything. I can change the value with click handlers thouogh, but for http and other async operations it won't work.


Solution

  • When you run asynchronous code, you need to let Angular know that something has updated. This makes angular run a $digest cycle, checking if any bindings need updating.

    To do this, wrap your assignment in a call to $scope.$apply().

    function myComponent($scope){
      Promise.resolve().then(_ => {
        $scope.$apply(() => {
          this.data = 'Hello World';
        });  
      });
    }
    
    let myModule = angular.module('myModule', []);
    
    myModule.component('myComponent', {
      template: `<div>{{$ctrl.data}}</div>`,
      controller: myComponent
    });
    <script data-require="[email protected]" data-semver="1.5.8" src="https://opensource.keycdn.com/angularjs/1.5.8/angular.min.js"></script>
    <div ng-app="myModule">
      <my-component></my-component>
    </div>

    Notice that I added $scope not only in the function body, but also as a function parameter.

    Read more about $scope.$apply and $scope.digest