2014-05-06 37 views
5

是否可以將承諾傳遞給來自外部控制器(例如觸發該狀態的控制器)的UI.Router $state將承諾傳遞給Angular-UI狀態控制器

我知道$state.go()返回一個承諾; 是否可以用您自己的承諾覆蓋該內容 直接自行解決此承諾或使用新承諾解決此承諾?

此外,文件說,通過$state.go()返回的承諾,可以與其他承諾(由transition superseded表示)被拒絕,但我到處都找不到,指示如何從國家自身內完成。

例如,在下面的代碼,我希望能夠等待用戶繼續到doSomethingElse()之前,點擊一個按鈕($scope.buttonClicked())。

我知道我可以發出一個事件,但由於承諾被烘烤到角度如此之深,我想知道是否有辦法通過promise.resolve/promise.reject這樣做。

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) 
}]); 

更新 我已經接受了@ blint的答案,因爲它已得到最接近我我想要的東西。下面是一些代碼,更豐富地回答了這個答案的想法。我不認爲我寫這篇文章的方式是一個非常優雅的解決方案,如果有人能夠提出更好的解決觸發狀態承諾的方法,我很高興。

我選擇的解決方案是按照通常在控制器中的方式鏈接您的承諾,但請附上一個$scope.next()方法(或類似的方法)來解決/拒絕承諾。由於狀態可以繼承調用控制器的作用域,因此它將能夠直接調用該方法,從而解決/拒絕承諾。下面是它如何工作的:

首先,設置你的狀態的按鈕/那叫一個$scope.next()方法控制器:

.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>' 
    }); 
}) 

接下來,您設置控制器。在這種情況下,我使用本地服務方法user.loadFromLocalStorage()來讓球滾動(它返回一個承諾),但是任何承諾都會執行。在此工作流程中,如果$scope.user缺少任何內容,它將逐漸使用狀態填充。如果它已完全填充,則會跳到主菜單的右側。如果元素爲空或處於無效狀態,則會進入錯誤視圖。

.controller('mainCtrl', function ($scope, $state, $q, User) { 
    $scope.user = new User(); 

    $scope.user.loadFromLocalStorage() 
    .then(function() { 
     var deferred; 

     if ($scope.user.language === null) { 
      deferred = $q.defer(); 

      $state.go('selectLanguage'); 

      $scope.next = function() { 
       $scope.next = undefined; 

       if ($scope.user.language === null) { 
        return deferred.reject('Language not selected somehow'); 
       } 

       deferred.resolve(); 
      }; 

      return deferred.promise; 
     } 
    }) 
    .then(function() { 
     var deferred; 

     if ($scope.user.name === null || $scope.user.email === null) { 
      deferred = $q.defer(); 

      $state.go('getUserInfo'); 
      $scope.next = function() { 
       $scope.next = undefined; 

       if ($scope.user.name === null || $scope.user.email === null) { 
        return deferred.reject('Could not get user name or email'); 
       } 

       deferred.resolve(); 
      }; 

      return deferred.promise; 
     } 


    }) 
    .then(function() { 
     $state.go('mainMenu'); 
    }) 
    .catch(function (err) { 
     $state.go('error', err); 
    }); 

}); 

這是相當詳細的,還不是非常乾燥,但它顯示了使用承諾異步流控制的總體意圖。

+0

你是什麼意思?是否有可能用你自己的承諾來重寫?'? – blint

+0

對不起。我想更好的方式是,你可以用你自己的承諾來解決一個「$ state」的承諾。 – Andrew

回答

3

promises的目的是保證結果......或處理失敗。承諾可以被鏈接,在功能中返回,從而得到延伸。

你對「壓倒一個」承諾沒有興趣。但是,您可以執行以下操作:

  • 處理故障案例。下面是從文檔的例子:
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); 
    }); 
  • 在承諾鏈附加一個新的異步操作。你可以結合承諾。如果鏈中調用的方法返回一個承諾,那麼一旦新承諾解決,承諾的父承諾將阻塞鏈的其餘部分。

這裏是你可能會尋找一個模式:

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

寫得非常好。一個評論,$ scope.buttenClicked解決延遲那裏,這隻會發生_once_和隨後的點擊將被忽略,我認爲這是值得澄清。 –

+0

@BenjaminGruenbaum引用了這個,但是從$ state.go('myState')返回的諾言立即返回(當狀態被觸發並且視圖更新時),而不是'$ scope'。 buttonClicked()'被調用。這是我希望能夠建立的聯繫。 – Andrew

+0

承諾立即返回,但您只能通過'.then'實際訪問它們的值(稱爲「展開」承諾) –

1

也許一個實現,這將是恢復從你的承諾的方式,國家resolve

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

還有一個例子在resolve docs可能有興趣

// 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

我剛剛試過這個,它非常接近我所需要的。不幸的是,在'resolve'塊中'$ scope'還不存在(我猜它相當於一個「config」塊)。因此,我無法將視圖模板或狀態控制器中的操作綁定到「resolve」依賴項。 – Andrew