Skip to content

Commit b6a2186

Browse files
committed
fix(ui-sref): handle state transition promise rejection in ui-sref directive
In the current world, ui-sref does nothing when the promise returned by $state.go is rejected. This PR gives ui-sref directive the ability to emit $stateChangeCancel when the transition promise rejects. The reason why I have chosen to implement this in the ui-sref directive and not in the definition for $state.go or $state.transitionTo are outlined in the issue below. angular-ui#3027.
1 parent 953235a commit b6a2186

File tree

2 files changed

+20
-10
lines changed

2 files changed

+20
-10
lines changed

src/stateDirectives.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,23 @@ function getTypeInfo(el) {
2626
};
2727
}
2828

29-
function clickHook(el, $state, $timeout, type, current) {
29+
function clickHook(scope, el, $state, $timeout, type, current) {
3030
return function(e) {
3131
var button = e.which || e.button, target = current();
3232

3333
if (!(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || el.attr('target'))) {
3434
// HACK: This is to allow ng-clicks to be processed before the transition is initiated:
3535
var transition = $timeout(function() {
36-
$state.go(target.state, target.params, target.options);
36+
var transitionPromise = $state.go(target.state, target.params, target.options);
37+
var noop = function() {};
38+
39+
// if there's an error since the state change is cancelled
40+
// emit $stateChangeCancel
41+
transitionPromise.then(noop, function(e) {
42+
if (scope) {
43+
scope.$emit('$stateChangeCancel', e);
44+
}
45+
});
3746
});
3847
e.preventDefault();
3948

@@ -144,7 +153,7 @@ function $StateRefDirective($state, $timeout) {
144153
update();
145154

146155
if (!type.clickable) return;
147-
hookFn = clickHook(element, $state, $timeout, type, function() { return def; });
156+
hookFn = clickHook(scope, element, $state, $timeout, type, function() { return def; });
148157
element.bind("click", hookFn);
149158
scope.$on('$destroy', function() {
150159
element.unbind("click", hookFn);
@@ -196,7 +205,7 @@ function $StateRefDynamicDirective($state, $timeout) {
196205
runStateRefLink(scope.$eval(watch));
197206

198207
if (!type.clickable) return;
199-
hookFn = clickHook(element, $state, $timeout, type, function() { return def; });
208+
hookFn = clickHook(scope, element, $state, $timeout, type, function() { return def; });
200209
element.bind("click", hookFn);
201210
scope.$on('$destroy', function() {
202211
element.unbind("click", hookFn);

test/stateDirectivesSpec.js

+7-6
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ describe('uiStateRef', function() {
141141
ctrlKey: undefined,
142142
shiftKey: undefined,
143143
altKey: undefined,
144-
button: undefined
144+
button: undefined
145145
});
146146
timeoutFlush();
147147
$q.flush();
@@ -156,7 +156,7 @@ describe('uiStateRef', function() {
156156

157157
timeoutFlush();
158158
$q.flush();
159-
159+
160160
expect($state.current.name).toEqual('top');
161161
expect($stateParams).toEqualData({ });
162162
}));
@@ -222,7 +222,7 @@ describe('uiStateRef', function() {
222222

223223
it('should allow passing params to current state', inject(function($compile, $rootScope, $state) {
224224
$state.current.name = 'contacts.item.detail';
225-
225+
226226
el = angular.element("<a ui-sref=\"{id: $index}\">Details</a>");
227227
$rootScope.$index = 3;
228228
$rootScope.$apply();
@@ -231,10 +231,10 @@ describe('uiStateRef', function() {
231231
$rootScope.$digest();
232232
expect(el.attr('href')).toBe('#/contacts/3');
233233
}));
234-
234+
235235
it('should allow multi-line attribute values when passing params to current state', inject(function($compile, $rootScope, $state) {
236236
$state.current.name = 'contacts.item.detail';
237-
237+
238238
el = angular.element("<a ui-sref=\"{\n\tid: $index\n}\">Details</a>");
239239
$rootScope.$index = 3;
240240
$rootScope.$apply();
@@ -368,7 +368,7 @@ describe('uiStateRef', function() {
368368
expect(angular.element(template[0]).attr('href')).toBe('#/contacts/10');
369369
}));
370370

371-
it('accepts option overrides', inject(function ($compile, $timeout, $state) {
371+
it('accepts option overrides', inject(function ($compile, $timeout, $state, $q) {
372372
var transitionOptions;
373373

374374
el = angular.element('<a ui-state="state" ui-state-opts="opts">state</a>');
@@ -379,6 +379,7 @@ describe('uiStateRef', function() {
379379

380380
spyOn($state, 'go').andCallFake(function(state, params, options) {
381381
transitionOptions = options;
382+
return $q(function(res, rej) { res(42); });
382383
});
383384

384385
triggerClick(template)

0 commit comments

Comments
 (0)