Skip to content

Commit 0eb7406

Browse files
feat(UIRouterGlobals): Create UIRouterGlobals
Created an isolated place to hold the public global mutable state fix(UIRouterGlobals): Only store the last successful transition and the last attempted transition closes #2525
1 parent de252a0 commit 0eb7406

File tree

8 files changed

+137
-88
lines changed

8 files changed

+137
-88
lines changed

src/common/queue.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
/** @module common */ /** for typedoc */
22

33
export class Queue<T> {
4-
constructor(private _items: T[] = []) { }
4+
constructor(private _items: T[] = [], private _limit: number = null) { }
55

66
enqueue(item: T) {
7-
this._items.push(item);
7+
let items = this._items;
8+
items.push(item);
9+
if (this._limit && items.length > this._limit) items.shift();
810
return item;
911
}
1012

src/globals.ts

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/** @module common */ /** */
2+
import {StateParams} from "./params/stateParams";
3+
import {StateDeclaration} from "./state/interface";
4+
import {State} from "./state/stateObject";
5+
import {Transition} from "./transition/transition";
6+
import {Queue} from "./common/queue";
7+
import {TransitionService} from "./transition/transitionService";
8+
import {copy} from "./common/common";
9+
10+
/**
11+
* Global mutable state
12+
*/
13+
export class UIRouterGlobals {
14+
/**
15+
* Current parameter values
16+
*
17+
* The parameter values from the latest successful transition
18+
*/
19+
params: StateParams = new StateParams();
20+
/**
21+
* Current state
22+
*
23+
* The to-state from the latest successful transition
24+
*/
25+
current: StateDeclaration;
26+
/**
27+
* Current state
28+
*
29+
* The to-state from the latest successful transition
30+
*/
31+
$current: State;
32+
/**
33+
* The current transition (in progress)
34+
*/
35+
transition: Transition;
36+
/**
37+
* The transition history
38+
*
39+
* This queue's size is limited to a maximum number (default: 1)
40+
*/
41+
transitionHistory = new Queue<Transition>([], 1);
42+
/**
43+
* The history of successful transitions
44+
*
45+
* This queue's size is limited to a maximum number (default: 1)
46+
*/
47+
successfulTransitions = new Queue<Transition>([], 1);
48+
49+
constructor(transitionService: TransitionService) {
50+
const beforeNewTransition = ($transition$) => {
51+
52+
this.transition = $transition$;
53+
this.transitionHistory.enqueue($transition$);
54+
55+
const updateGlobalState = () => {
56+
this.successfulTransitions.enqueue($transition$);
57+
this.$current = $transition$.$to();
58+
this.current = this.$current.self;
59+
copy($transition$.params(), this.params);
60+
};
61+
62+
$transition$.onSuccess({}, updateGlobalState, {priority: 10000});
63+
64+
const clearCurrentTransition = () => { if (this.transition === $transition$) this.transition = null; };
65+
66+
$transition$.promise.finally(clearCurrentTransition)
67+
68+
};
69+
70+
transitionService.onBefore({}, ['$transition$', beforeNewTransition]);
71+
}
72+
}

src/ng1/services.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,8 @@ const resolveFactory = () => ({
226226
}
227227
});
228228

229-
function $stateParamsFactory(ng1UIRouter, $rootScope) {
230-
return router.stateParams;
229+
function $stateParamsFactory(ng1UIRouter) {
230+
return ng1UIRouter.globals.params;
231231
}
232232

233233
// The 'ui.router' ng1 module depends on 'ui.router.init' module.
@@ -265,7 +265,8 @@ angular.module('ui.router.state').provider('$state', ['ng1UIRouterProvider', get
265265
angular.module('ui.router.state').run(['$state', function($state) { }]);
266266

267267
// $stateParams service
268-
angular.module('ui.router.state').factory('$stateParams', ['ng1UIRouter', '$rootScope', $stateParamsFactory]);
268+
angular.module('ui.router.state').factory('$stateParams', ['ng1UIRouter', (ng1UIRouter) =>
269+
ng1UIRouter.globals.params]);
269270

270271
// $transitions service and $transitionsProvider
271272
function getTransitionsProvider() {

src/params/stateParams.ts

-3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,3 @@ export class StateParams {
3232
};
3333
}
3434

35-
export function stateParamsFactory() {
36-
return new StateParams();
37-
}

src/router.ts

+11-9
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
import {UrlMatcherFactory} from "./url/urlMatcherFactory";
33
import {UrlRouterProvider} from "./url/urlRouter";
44
import {StateProvider} from "./state/state";
5-
import {stateParamsFactory, StateParams} from "./params/stateParams";
65
import {UrlRouter} from "./url/urlRouter";
76
import {TransitionService} from "./transition/transitionService";
87
import {ViewService} from "./view/view";
98
import {StateRegistry} from "./state/stateRegistry";
109
import {StateService} from "./state/stateService";
10+
import {UIRouterGlobals} from "./globals";
1111

1212
/**
1313
* The master class used to instantiate an instance of UI-Router.
@@ -19,27 +19,29 @@ import {StateService} from "./state/stateService";
1919
* the URL by calling `urlRouter.listen()` ([[URLRouter.listen]])
2020
*/
2121
export class UIRouter {
22+
viewService = new ViewService();
23+
24+
transitionService: TransitionService = new TransitionService(this.viewService);
2225

23-
stateParams: StateParams = stateParamsFactory();
26+
globals: UIRouterGlobals = new UIRouterGlobals(this.transitionService);
2427

2528
urlMatcherFactory: UrlMatcherFactory = new UrlMatcherFactory();
2629

27-
urlRouterProvider: UrlRouterProvider = new UrlRouterProvider(this.urlMatcherFactory, this.stateParams);
30+
urlRouterProvider: UrlRouterProvider = new UrlRouterProvider(this.urlMatcherFactory, this.globals.params);
2831

2932
urlRouter: UrlRouter = new UrlRouter(this.urlRouterProvider);
3033

31-
viewService = new ViewService();
32-
33-
transitionService: TransitionService = new TransitionService(this.viewService);
34-
3534
stateRegistry: StateRegistry = new StateRegistry(this.urlMatcherFactory, this.urlRouterProvider);
3635

37-
// TODO: move this to ng1.ts
36+
/** @hidden TODO: move this to ng1.ts */
3837
stateProvider = new StateProvider(this.stateRegistry);
3938

40-
stateService = new StateService(this.viewService, this.stateParams, this.urlRouter, this.transitionService, this.stateRegistry, this.stateProvider);
39+
stateService = new StateService(this.viewService, this.urlRouter, this.transitionService, this.stateRegistry, this.stateProvider, this.globals);
4140

4241
constructor() {
4342
this.viewService.rootContext(this.stateRegistry.root());
43+
this.globals.$current = this.stateRegistry.root();
44+
this.globals.current = this.globals.$current.self;
4445
}
4546
}
47+

src/state/hooks/transitionManager.ts

+18-40
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
/** @module state */ /** for typedoc */
2-
import {copy} from "../../common/common";
32
import {prop} from "../../common/hof";
4-
import {Queue} from "../../common/queue";
53
import {Param} from "../../params/param";
64

75
import {TreeChanges} from "../../transition/interface";
@@ -15,6 +13,8 @@ import {ViewHooks} from "./viewHooks";
1513
import {EnterExitHooks} from "./enterExitHooks";
1614
import {ResolveHooks} from "./resolveHooks";
1715
import {UrlRouter} from "../../url/urlRouter";
16+
import {services} from "../../common/coreservices";
17+
import {UIRouterGlobals} from "../../globals";
1818

1919
/**
2020
* This class:
@@ -38,18 +38,17 @@ export class TransitionManager {
3838
private enterExitHooks: EnterExitHooks;
3939
private viewHooks: ViewHooks;
4040
private resolveHooks: ResolveHooks;
41+
private $q;
4142

4243
constructor(
4344
private transition: Transition,
4445
private $transitions,
4546
private $urlRouter: UrlRouter,
4647
private $view, // service
4748
private $state: StateService,
48-
private $stateParams, // service/obj
49-
private $q, // TODO: get from runtime.$q
50-
private activeTransQ: Queue<Transition>,
51-
private changeHistory: Queue<TreeChanges>
49+
private globals: UIRouterGlobals
5250
) {
51+
this.$q = services.$q;
5352
this.viewHooks = new ViewHooks(transition, $view);
5453
this.enterExitHooks = new EnterExitHooks(transition);
5554
this.resolveHooks = new ResolveHooks(transition);
@@ -63,45 +62,26 @@ export class TransitionManager {
6362
}
6463

6564
runTransition(): Promise<any> {
66-
this.activeTransQ.clear(); // TODO: nuke this
67-
this.activeTransQ.enqueue(this.transition);
68-
this.$state.transition = this.transition;
69-
let promise = this.transition.run()
65+
this.globals.transitionHistory.enqueue(this.transition);
66+
return this.transition.run()
7067
.then((trans: Transition) => trans.to()) // resolve to the final state (TODO: good? bad?)
7168
.catch(error => this.transRejected(error)); // if rejected, handle dynamic and redirect
72-
73-
let always = () => {
74-
this.activeTransQ.remove(this.transition);
75-
if (this.$state.transition === this.transition) this.transition = null;
76-
};
77-
78-
promise.then(always, always);
79-
80-
return promise;
8169
}
8270

8371
registerUpdateGlobalState() {
84-
this.transition.onFinish({}, this.updateGlobalState.bind(this), {priority: -10000});
85-
}
86-
87-
updateGlobalState() {
88-
let {treeChanges, transition, $state, changeHistory} = this;
89-
// Update globals in $state
90-
$state.$current = transition.$to();
91-
$state.current = $state.$current.self;
92-
changeHistory.enqueue(treeChanges);
93-
this.updateStateParams();
72+
// After globals.current is updated at priority: 10000
73+
this.transition.onSuccess({}, this.updateUrl.bind(this), {priority: 9999});
9474
}
9575

9676
transRejected(error): (StateDeclaration|Promise<any>) {
97-
let {transition, $state, $stateParams, $q} = this;
77+
let {transition, $state, $q} = this;
9878
// Handle redirect and abort
9979
if (error instanceof TransitionRejection) {
10080
if (error.type === RejectType.IGNORED) {
10181
// Update $stateParmas/$state.params/$location.url if transition ignored, but dynamic params have changed.
10282
let dynamic = $state.$current.parameters().filter(prop('dynamic'));
103-
if (!Param.equals(dynamic, $stateParams, transition.params())) {
104-
this.updateStateParams();
83+
if (!Param.equals(dynamic, $state.params, transition.params())) {
84+
this.updateUrl();
10585
}
10686
return $state.current;
10787
}
@@ -120,22 +100,20 @@ export class TransitionManager {
120100
return $q.reject(error);
121101
}
122102

123-
updateStateParams() {
103+
updateUrl() {
124104
let transition = this.transition;
125-
let {$urlRouter, $state, $stateParams} = this;
105+
let {$urlRouter, $state} = this;
126106
let options = transition.options();
127-
copy(transition.params(), $state.params);
128-
copy($state.params, $stateParams);
107+
var toState = transition.$to();
129108

130109
if (options.location && $state.$current.navigable) {
131-
$urlRouter.push($state.$current.navigable.url, $stateParams, { replace: options.location === 'replace' });
110+
$urlRouter.push($state.$current.navigable.url, $state.params, { replace: options.location === 'replace' });
132111
}
133-
134112
$urlRouter.update(true);
135113
}
136114

137115
private _redirectMgr(redirect: Transition): TransitionManager {
138-
let {$transitions, $urlRouter, $view, $state, $stateParams, $q, activeTransQ, changeHistory} = this;
139-
return new TransitionManager(redirect, $transitions, $urlRouter, $view, $state, $stateParams, $q, activeTransQ, changeHistory);
116+
let {$transitions, $urlRouter, $view, $state, globals} = this;
117+
return new TransitionManager(redirect, $transitions, $urlRouter, $view, $state, globals);
140118
}
141119
}

0 commit comments

Comments
 (0)