简体   繁体   中英

AngularJS Controller As: Promise not updating view

Been banging my head against a wall trying to figure out how controller as and Promises work together with AngularJS. It seems that by wrapping a result in a promise, AngularJS does not see the controller updating until you trigger a different change.

HTML:

<body ng-app="MainModule" ng-controller="MainController as main">
    <div class="bar bar-header bar-calm">
        <h1 class="title">Test Bench</h1>
    </div>

    <div class="app">
        <h1>Test Bench</h1>

        <div class="text-center">
            <p>Last Fingerprint Result: {{main.lastAuthSuccessful}}</p>
        </div>
.....
</body>

The controller method in question:

doFingerprint() {
    this.auth
      .fingerprint()

      .then(success => {
        console.log(this, self, this.self);
        this.lastAuthSuccessful = success;
      })

      .catch(error => {
        console.error(error);
        return false;
      });
  }

And the fingerprint method in the auth service:

fingerprint(): Promise<boolean> {
    if (!this.finger) return Promise.reject("Fingerprint API not yet loaded.");

    return new Promise((resolve, reject) => {
      this.finger.show(
        {
          clientId: "Fingerprint-Demo",
          clientSecret: "password"
        },

        () => {
          resolve(true);
        },

        err => {
          resolve(false);
        }
      );
    });
  }

This feels pretty straightforward, but the updated lastAuthSuccessful from the promise refuses to update the view until you attempt to call the method again.

angular.js famously (or rather infamously) uses the digest loop for change detection. Builtin angular directives like ng-click will call $scope.$digest() for you, but here your change is being made asynchronously, when the promise resolves, and no one calls $digest() .

So, you can either call $scope.$digest() yourself in the then block of the promise, or you can use the $q , which is angular's implementation of Promises, where it calls $digest for you.

Angular has its own Promise implementation, which is $q . The ES6 Promise won't trigger Angular's digest cycle, you should use $q instead.

If you insist using ES6 Promise, you can call $scope.$apply(). $apply() is used to execute an expression in AngularJS from outside of the AngularJS framework. (For example from browser DOM events, setTimeout, XHR or third-party libraries).

Usually, you don't call $digest() directly in controllers or in directives. Instead, you should call $apply() (typically from within a directive), which will force a $digest().

fingerprint(): Promise<boolean> {
    let deferred = $q.defer();
    if (!this.finger) return $q.reject("Fingerprint API not yet loaded.");

    this.finger.show(
        {
          clientId: "Fingerprint-Demo",
          clientSecret: "password"
        },

        () => {
          deferred.resolve(true);
        },

        err => {
          deferred.resolve(false);
        }
      );

    return deferred.promise;
}

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