5

Ist es möglich, ein Versprechen an einen UI.Router $state von einem externen Controller (z. B. dem Controller, der den Status ausgelöst hat) zu übergeben?Eine Zusage an einen Angular-UI-Statuscontroller übergeben

Ich weiß, dass $state.go() ein Versprechen zurückgibt; Ist es möglich, das mit Ihrem eigenen Versprechen zu überschreiben lösen Sie dieses Versprechen direkt selbst oder lösen Sie es mit einem neuen Versprechen?

Auch die Dokumentation sagt das Versprechen von $state.go() zurückgegeben kann mit einem anderen Versprechen abgelehnt werden (angezeigt durch transition superseded), aber ich kann nirgends finden, die angibt, wie dies aus dem Zustand selbst erfolgen kann.

Zum Beispiel möchte ich im folgenden Code warten auf den Benutzer auf eine Schaltfläche ($scope.buttonClicked()) klicken, bevor Sie fortfahren zu doSomethingElse().

Ich weiß, dass ich ein Ereignis abgeben kann, aber da Versprechungen in Schräg gebacken werden so tief, ich fragte mich, ob es ein Weg, dies durch promise.resolve/promise.reject zu tun.

angular.module('APP', ['ui.router']) 
.config(['$stateProvider', function ($stateProvider) { 
    $stateProvider 
    .state('myState', { 
     template: '<p>myState</p>', 
     controller: ['$state', '$scope', '$q', function ($state, $scope, $q) { 
      var deferred = $q.defer(); 

      $scope.buttonClicked = function() { 
       deferred.resolve(); 
      } 
     }] 
    }); 
}]) 
.controller('mainCtrl', ['$state', function ($state) { 
    $state.go('myState') 
    .then(doSomethingElse) 
}]); 

aktualisieren ich angenommen haben, die Antwort von @ Blint, wie es mir am nächsten bekommen hat, was ich wollte. Im Folgenden finden Sie einen Code, der die Idee dieser Antwort ein wenig erweitert. Ich denke nicht, dass die Art und Weise, wie ich das geschrieben habe, eine sehr elegante Lösung ist, und ich bin froh, wenn jemand einen besseren Weg vorschlagen kann, Versprechen aus einem ausgelösten Zustand zu lösen.

Die Lösung, die ich gewählt habe, ist, Ihre Versprechen zu verketten, wie Sie normalerweise in Ihrem Controller würden, aber eine $scope.next() Methode (oder etwas Ähnliches) diesem Bereich beigefügt, die das Versprechen löst/ablehnt. Da der Status den Geltungsbereich des aufrufenden Controllers erben kann, kann er diese Methode direkt aufrufen und somit das Versprechen auflösen/ablehnen. Hier ist, wie es funktionieren könnte:

Zuerst richten Sie Ihre Zustände mit den Tasten/Controller, die eine $scope.next() Methode aufrufen:

.config(function ($stateProvider) { 
    $stateProvider 
    .state('selectLanguage', { 
     template: '<p>Select language for app: \ 
      <select ng-model="user.language" ng-options="language.label for language in languages">\ 
       <option value="">Please select</option>\ 
      </select>\ 
      <button ng-click="next()">Next</button>\ 
      </p>', 
     controller: function ($scope) { 
      $scope.languages = [ 
       {label: 'Deutch', value: 'de'}, 
       {label: 'English', value: 'en'}, 
       {label: 'Français', value: 'fr'}, 
       {label: 'Error', value: null} 
      ]; 
     } 
    }) 
    .state('getUserInfo', { 
     template: '<p>Name: <input ng-model="user.name" /><br />\ 
      Email: <input ng-model="user.email" /><br />\ 
      <button ng-click="next()">Next</button>\ 
      </p>' 
    }) 
    .state('mainMenu', { 
     template: '<p>The main menu for {{user.name}} is in {{user.language.label}}</p>' 
    }) 
    .state('error', { 
     template: '<p>There was an error</p>' 
    }); 
}) 

Als nächstes richten Sie Ihren Controller. In diesem Fall verwende ich eine lokale Service-Methode, user.loadFromLocalStorage(), um den Ball ins Rollen zu bringen (es gibt ein Versprechen zurück), aber jede Zusage reicht aus. Wenn in diesem Workflow $scope.user nichts vorhanden ist, wird es fortlaufend mit Status gefüllt. Wenn es voll ist, springt es direkt zum Hauptmenü. Wenn Elemente leer bleiben oder sich in einem ungültigen Zustand befinden, gelangen Sie in eine Fehleransicht.

Dies ist ziemlich ausführlich und noch nicht sehr trocken, aber es zeigt die allgemeine Absicht der asynchronen Flusskontrolle mit Versprechungen.

+0

Was meinst du mit 'ist es möglich, das mit deinem eigenen Versprechen zu überschreiben?'? – blint

+0

Entschuldigung. Ich denke, der bessere Weg, das zu sagen, ist, können Sie ein '$ State' Versprechen mit Ihrem eigenen Versprechen lösen. – Andrew

Antwort

3

Der Zweck von promises ist, ein Ergebnis zu garantieren ... oder einen Fehler zu behandeln. Versprechen können verkettet, in Funktionen zurückgegeben und somit erweitert werden.

Sie hätten kein Interesse daran, ein Versprechen "außer Kraft zu setzen". Was Sie jedoch tun können:

  • Behandeln Sie den Fehlerfall.Hier ist das Beispiel aus der Dokumentation:
promiseB = promiseA.then(function(result) { 
    // success: do something and resolve promiseB 
    //   with the old or a new result 
    return result; 
    }, function(reason) { 
    // error: handle the error if possible and 
    //  resolve promiseB with newPromiseOrValue, 
    //  otherwise forward the rejection to promiseB 
    if (canHandle(reason)) { 
    // handle the error and recover 
    return newPromiseOrValue; 
    } 
    return $q.reject(reason); 
    }); 
  • Anfügen einen neuen asynchronen Betrieb in der Verheißung Kette. Sie können Versprechen kombinieren. Wenn eine Methode, die in der Kette aufgerufen wird, eine Verheißung zurückgibt, wird der verheiratete Elternteil den Rest der Kette ummauern, sobald das neue Versprechen gelöst ist.

Hier ist das Muster, das Sie für vielleicht suchen:

angular.module('APP', ['ui.router']) 
.config(['$stateProvider', function ($stateProvider) { 
    $stateProvider 
    .state('myState', { 
     template: '<p>myState</p>', 
     controller: 'myCtrl' 
    }); 
}]) 
.controller('myCtrl', ['$scope', '$state', '$q', '$http', 'someAsyncServiceWithCallback', 
    function ($scope, $state, $q, $http, myService) { 
    $scope.buttonClicked = function() { 
     $state.go('myState') 
     .then(function() { 
      // You can return a promise... 
      // From a method that returns a promise 
      // return $http.get('/myURL'); 

      // Or from an old-school method taking a callback: 
      var deferred = $q.defer(); 
      myService(function(data) { 
       deferred.resolve(data); 
      }); 

      return deferred.promise; 
     }, 
     function() { 
      console.log("$state.go() failed :("); 
     }); 
    }; 
}]); 
+0

Sehr schön geschrieben. Ein Kommentar allerdings, $ scope.buttenClicked löst das Zurückgestellte dort auf, dies kann nur _once_ passieren und nachfolgende Klicks werden einfach ignoriert, ich denke, es ist es wert, klarzustellen. –

+0

@BenjaminGruenbaum bezieht sich auch darauf, aber die von '$ state.go ('myState') zurückgegebene Antwort kehrt sofort zurück (also, sobald der Status ausgelöst und die Ansicht aktualisiert wurde), nicht wenn $ scope. buttonClicked() 'wird aufgerufen. Das ist die Verbindung, die ich zu machen hoffe. – Andrew

+0

Versprechungen kehren sofort zurück, aber Sie können nur tatsächlich auf ihren Wert zugreifen ("Entpacken" das Versprechen genannt) durch '.then ' –

1

Vielleicht ein Weg, dies zu erreichen, wäre Ihr Versprechen zurückzukehren, von der staatlichen resolve

resolve: { 
    myResolve: function($scope, $q) { 
     var deferred = $q.defer(); 
     $scope.buttonClicked = function() { 
      deferred.resolve(); 
     } 
     return deferred.promise; 
    } 
} 

Es ist auch ein Beispiel in resolve docs das könnte von Interesse sein

// Another promise example. If you need to do some 
// processing of the result, use .then, and your 
// promise is chained in for free. This is another 
// typical use case of resolve. 
promiseObj2: function($http){ 
    return $http({method: 'GET', url: '/someUrl'}) 
     .then (function (data) { 
      return doSomeStuffFirst(data); 
     }); 
}, 
+0

Ich habe es gerade ausprobiert, und es ist sehr nah an dem, was ich brauche. Leider existiert '$ scope' noch nicht im' resolve' Block (ich denke es ist äquivalent zu einem 'config' Block). Daher kann ich eine Aktion in der Ansichtsvorlage oder dem Statuscontroller nicht an eine 'Auflösungsabhängigkeit' binden. – Andrew