是否可以从外部 Controller (例如触发状态的 Controller )向 UI.Router $state
传递 promise ?
我知道 $state.go()
返回一个 promise ;是否可以用你自己的 promise 覆盖它自己直接解决这个 promise 或使用新的 promise 解决它?
另外,文档说 $state.go()
返回的 promise 可以被另一个 promise 拒绝(由 transition superseded
表示),但我不能找到表明如何从国家内部完成此操作的任何地方。
例如,在下面的代码中,我希望能够等待用户点击按钮 ($scope.buttonClicked()
),然后再继续 doSomethingElse ()
。
我知道我可以发出一个事件,但由于 promises 在 Angular 中的根基如此之深,我想知道是否有办法通过 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 的回答,因为它让我最接近我想要的。下面是一些代码,可以进一步充实这个答案的想法。我不认为我写这篇文章的方式是一个非常优雅的解决方案,如果有人可以提出更好的方法来解决触发状态的 promise ,我很高兴。
我选择的解决方案是像往常一样在 Controller 中链接您的 promise ,但将 $scope.next()
方法(或类似的方法)附加到解析的范围/拒绝 promise 。由于状态可以继承调用 Controller 的作用域,它将能够直接调用该方法,从而解决/拒绝 promise 。以下是它的工作原理:
首先,使用调用 $scope.next()
方法的按钮/ Controller 设置状态:
.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>'
});
})
接下来,您将设置您的 Controller 。在这种情况下,我使用本地服务方法 user.loadFromLocalStorage()
来启动(它返回一个 promise ),但任何 promise 都可以。在此工作流程中,如果 $scope.user
缺少任何内容,它将逐渐使用状态填充。如果它已完全填充,它会直接跳到主菜单。如果元素留空或处于无效状态,您将进入错误 View 。
.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);
});
});
这很冗长,还不是很干,但它显示了使用 promises 进行异步流控制的总体意图。
最佳答案
目的promises是保证结果...或处理失败。 Promise 可以链接起来,在函数中返回,从而进行扩展。
您不会对“覆盖” promise 感兴趣。但是,您可以做什么:
- 处理失败案例。这是文档中的示例:
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); });
- 在 promise 链中追加一个新的异步操作。你可以结合 promise 。如果链中调用的方法返回一个 promise ,一旦新 promise 得到解决, promise 的父级将隔离链的其余部分。
这是您可能正在寻找的模式:
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 :(");
});
};
}]);
关于javascript - 将 promise 传递给 Angular-UI 状态 Controller ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23492349/