@angular-wave/angular.ts 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -15
- package/dist/angular-ts.esm.js +1 -1
- package/dist/angular-ts.umd.js +1 -1
- package/package.json +4 -1
- package/src/core/compile.js +0 -26
- package/src/exts/messages.md +30 -30
- package/src/public.js +2 -0
- package/src/router/adapter/directives/stateDirectives.js +695 -0
- package/src/router/adapter/directives/viewDirective.js +514 -0
- package/src/router/adapter/injectables.js +314 -0
- package/src/router/adapter/interface.js +1 -0
- package/src/router/adapter/locationServices.js +86 -0
- package/src/router/adapter/services.js +132 -0
- package/src/router/adapter/stateFilters.js +43 -0
- package/src/router/adapter/stateProvider.js +137 -0
- package/src/router/adapter/statebuilders/onEnterExitRetain.js +30 -0
- package/src/router/adapter/statebuilders/views.js +146 -0
- package/src/router/adapter/templateFactory.js +218 -0
- package/src/router/adapter/urlRouterProvider.js +196 -0
- package/src/router/adapter/viewScroll.js +31 -0
- package/src/router/core/common/common.js +506 -0
- package/src/router/core/common/coreservices.js +15 -0
- package/src/router/core/common/glob.js +75 -0
- package/src/router/core/common/hof.js +194 -0
- package/src/router/core/common/predicates.js +44 -0
- package/src/router/core/common/queue.js +41 -0
- package/src/router/core/common/safeConsole.js +38 -0
- package/src/router/core/common/strings.js +141 -0
- package/src/router/core/common/trace.js +232 -0
- package/src/router/core/globals.js +29 -0
- package/src/router/core/hooks/coreResolvables.js +33 -0
- package/src/router/core/hooks/ignoredTransition.js +25 -0
- package/src/router/core/hooks/invalidTransition.js +14 -0
- package/src/router/core/hooks/lazyLoad.js +102 -0
- package/src/router/core/hooks/onEnterExitRetain.js +55 -0
- package/src/router/core/hooks/redirectTo.js +36 -0
- package/src/router/core/hooks/resolve.js +57 -0
- package/src/router/core/hooks/updateGlobals.js +30 -0
- package/src/router/core/hooks/url.js +25 -0
- package/src/router/core/hooks/views.js +39 -0
- package/src/router/core/interface.js +3 -0
- package/src/router/core/params/README.md +8 -0
- package/src/router/core/params/param.js +232 -0
- package/src/router/core/params/paramType.js +139 -0
- package/src/router/core/params/paramTypes.js +163 -0
- package/src/router/core/params/stateParams.js +35 -0
- package/src/router/core/path/pathNode.js +77 -0
- package/src/router/core/path/pathUtils.js +200 -0
- package/src/router/core/resolve/interface.js +10 -0
- package/src/router/core/resolve/resolvable.js +124 -0
- package/src/router/core/resolve/resolveContext.js +211 -0
- package/src/router/core/router.js +201 -0
- package/src/router/core/state/README.md +21 -0
- package/src/router/core/state/stateBuilder.js +333 -0
- package/src/router/core/state/stateMatcher.js +66 -0
- package/src/router/core/state/stateObject.js +116 -0
- package/src/router/core/state/stateQueueManager.js +89 -0
- package/src/router/core/state/stateRegistry.js +175 -0
- package/src/router/core/state/stateService.js +592 -0
- package/src/router/core/state/targetState.js +159 -0
- package/src/router/core/transition/hookBuilder.js +127 -0
- package/src/router/core/transition/hookRegistry.js +182 -0
- package/src/router/core/transition/interface.js +14 -0
- package/src/router/core/transition/rejectFactory.js +122 -0
- package/src/router/core/transition/transition.js +739 -0
- package/src/router/core/transition/transitionEventType.js +27 -0
- package/src/router/core/transition/transitionHook.js +199 -0
- package/src/router/core/transition/transitionService.js +311 -0
- package/src/router/core/url/interface.js +1 -0
- package/src/router/core/url/urlConfig.js +165 -0
- package/src/router/core/url/urlMatcher.js +548 -0
- package/src/router/core/url/urlMatcherFactory.js +123 -0
- package/src/router/core/url/urlRouter.js +115 -0
- package/src/router/core/url/urlRule.js +202 -0
- package/src/router/core/url/urlRules.js +348 -0
- package/src/router/core/url/urlService.js +268 -0
- package/src/router/core/vanilla/baseLocationService.js +31 -0
- package/src/router/core/vanilla/browserLocationConfig.js +42 -0
- package/src/router/core/vanilla/hashLocationService.js +19 -0
- package/src/router/core/vanilla/injector.js +98 -0
- package/src/router/core/vanilla/interface.js +1 -0
- package/src/router/core/vanilla/memoryLocationConfig.js +20 -0
- package/src/router/core/vanilla/memoryLocationService.js +13 -0
- package/src/router/core/vanilla/plugins.js +35 -0
- package/src/router/core/vanilla/pushStateLocationService.js +69 -0
- package/src/router/core/vanilla/q.js +54 -0
- package/src/router/core/vanilla/utils.js +63 -0
- package/src/router/core/view/interface.js +1 -0
- package/src/router/core/view/view.js +312 -0
- package/src/router/router.js +52 -0
- package/test/ng/compile.spec.js +43 -61
- package/test/ng/directive/init.spec.js +1 -1
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
/** @publicapi @module directives */ /** */
|
|
2
|
+
import {
|
|
3
|
+
extend,
|
|
4
|
+
filter,
|
|
5
|
+
isDefined,
|
|
6
|
+
isFunction,
|
|
7
|
+
isString,
|
|
8
|
+
kebobString,
|
|
9
|
+
noop,
|
|
10
|
+
parse,
|
|
11
|
+
ResolveContext,
|
|
12
|
+
tail,
|
|
13
|
+
trace,
|
|
14
|
+
unnestR,
|
|
15
|
+
} from "../../core/index";
|
|
16
|
+
|
|
17
|
+
import { getLocals } from "../services";
|
|
18
|
+
import { Ng1ViewConfig } from "../statebuilders/views";
|
|
19
|
+
/**
|
|
20
|
+
* `ui-view`: A viewport directive which is filled in by a view from the active state.
|
|
21
|
+
*
|
|
22
|
+
* ### Attributes
|
|
23
|
+
*
|
|
24
|
+
* - `name`: (Optional) A view name.
|
|
25
|
+
* The name should be unique amongst the other views in the same state.
|
|
26
|
+
* You can have views of the same name that live in different states.
|
|
27
|
+
* The ui-view can be targeted in a View using the name ([[Ng1StateDeclaration.views]]).
|
|
28
|
+
*
|
|
29
|
+
* - `autoscroll`: an expression. When it evaluates to true, the `ui-view` will be scrolled into view when it is activated.
|
|
30
|
+
* Uses [[$uiViewScroll]] to do the scrolling.
|
|
31
|
+
*
|
|
32
|
+
* - `onload`: Expression to evaluate whenever the view updates.
|
|
33
|
+
*
|
|
34
|
+
* #### Example:
|
|
35
|
+
* A view can be unnamed or named.
|
|
36
|
+
* ```html
|
|
37
|
+
* <!-- Unnamed -->
|
|
38
|
+
* <div ui-view></div>
|
|
39
|
+
*
|
|
40
|
+
* <!-- Named -->
|
|
41
|
+
* <div ui-view="viewName"></div>
|
|
42
|
+
*
|
|
43
|
+
* <!-- Named (different style) -->
|
|
44
|
+
* <ui-view name="viewName"></ui-view>
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* You can only have one unnamed view within any template (or root html). If you are only using a
|
|
48
|
+
* single view and it is unnamed then you can populate it like so:
|
|
49
|
+
*
|
|
50
|
+
* ```html
|
|
51
|
+
* <div ui-view></div>
|
|
52
|
+
* $stateProvider.state("home", {
|
|
53
|
+
* template: "<h1>HELLO!</h1>"
|
|
54
|
+
* })
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* The above is a convenient shortcut equivalent to specifying your view explicitly with the
|
|
58
|
+
* [[Ng1StateDeclaration.views]] config property, by name, in this case an empty name:
|
|
59
|
+
*
|
|
60
|
+
* ```js
|
|
61
|
+
* $stateProvider.state("home", {
|
|
62
|
+
* views: {
|
|
63
|
+
* "": {
|
|
64
|
+
* template: "<h1>HELLO!</h1>"
|
|
65
|
+
* }
|
|
66
|
+
* }
|
|
67
|
+
* })
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* But typically you'll only use the views property if you name your view or have more than one view
|
|
71
|
+
* in the same template. There's not really a compelling reason to name a view if its the only one,
|
|
72
|
+
* but you could if you wanted, like so:
|
|
73
|
+
*
|
|
74
|
+
* ```html
|
|
75
|
+
* <div ui-view="main"></div>
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* ```js
|
|
79
|
+
* $stateProvider.state("home", {
|
|
80
|
+
* views: {
|
|
81
|
+
* "main": {
|
|
82
|
+
* template: "<h1>HELLO!</h1>"
|
|
83
|
+
* }
|
|
84
|
+
* }
|
|
85
|
+
* })
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* Really though, you'll use views to set up multiple views:
|
|
89
|
+
*
|
|
90
|
+
* ```html
|
|
91
|
+
* <div ui-view></div>
|
|
92
|
+
* <div ui-view="chart"></div>
|
|
93
|
+
* <div ui-view="data"></div>
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* ```js
|
|
97
|
+
* $stateProvider.state("home", {
|
|
98
|
+
* views: {
|
|
99
|
+
* "": {
|
|
100
|
+
* template: "<h1>HELLO!</h1>"
|
|
101
|
+
* },
|
|
102
|
+
* "chart": {
|
|
103
|
+
* template: "<chart_thing/>"
|
|
104
|
+
* },
|
|
105
|
+
* "data": {
|
|
106
|
+
* template: "<data_thing/>"
|
|
107
|
+
* }
|
|
108
|
+
* }
|
|
109
|
+
* })
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* #### Examples for `autoscroll`:
|
|
113
|
+
* ```html
|
|
114
|
+
* <!-- If autoscroll present with no expression,
|
|
115
|
+
* then scroll ui-view into view -->
|
|
116
|
+
* <ui-view autoscroll/>
|
|
117
|
+
*
|
|
118
|
+
* <!-- If autoscroll present with valid expression,
|
|
119
|
+
* then scroll ui-view into view if expression evaluates to true -->
|
|
120
|
+
* <ui-view autoscroll='true'/>
|
|
121
|
+
* <ui-view autoscroll='false'/>
|
|
122
|
+
* <ui-view autoscroll='scopeVariable'/>
|
|
123
|
+
* ```
|
|
124
|
+
*
|
|
125
|
+
* Resolve data:
|
|
126
|
+
*
|
|
127
|
+
* The resolved data from the state's `resolve` block is placed on the scope as `$resolve` (this
|
|
128
|
+
* can be customized using [[Ng1ViewDeclaration.resolveAs]]). This can be then accessed from the template.
|
|
129
|
+
*
|
|
130
|
+
* Note that when `controllerAs` is being used, `$resolve` is set on the controller instance *after* the
|
|
131
|
+
* controller is instantiated. The `$onInit()` hook can be used to perform initialization code which
|
|
132
|
+
* depends on `$resolve` data.
|
|
133
|
+
*
|
|
134
|
+
* #### Example:
|
|
135
|
+
* ```js
|
|
136
|
+
* $stateProvider.state('home', {
|
|
137
|
+
* template: '<my-component user="$resolve.user"></my-component>',
|
|
138
|
+
* resolve: {
|
|
139
|
+
* user: function(UserService) { return UserService.fetchUser(); }
|
|
140
|
+
* }
|
|
141
|
+
* });
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
export let uiView;
|
|
145
|
+
// eslint-disable-next-line prefer-const
|
|
146
|
+
uiView = [
|
|
147
|
+
"$view",
|
|
148
|
+
"$animate",
|
|
149
|
+
"$uiViewScroll",
|
|
150
|
+
"$interpolate",
|
|
151
|
+
"$q",
|
|
152
|
+
function $ViewDirective($view, $animate, $uiViewScroll, $interpolate, $q) {
|
|
153
|
+
function getRenderer() {
|
|
154
|
+
return {
|
|
155
|
+
enter: function (element, target, cb) {
|
|
156
|
+
if (angular.version.minor > 2) {
|
|
157
|
+
$animate.enter(element, null, target).then(cb);
|
|
158
|
+
} else {
|
|
159
|
+
$animate.enter(element, null, target, cb);
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
leave: function (element, cb) {
|
|
163
|
+
if (angular.version.minor > 2) {
|
|
164
|
+
$animate.leave(element).then(cb);
|
|
165
|
+
} else {
|
|
166
|
+
$animate.leave(element, cb);
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function configsEqual(config1, config2) {
|
|
172
|
+
return config1 === config2;
|
|
173
|
+
}
|
|
174
|
+
const rootData = {
|
|
175
|
+
$cfg: { viewDecl: { $context: $view._pluginapi._rootViewContext() } },
|
|
176
|
+
$uiView: {},
|
|
177
|
+
};
|
|
178
|
+
const directive = {
|
|
179
|
+
count: 0,
|
|
180
|
+
restrict: "ECA",
|
|
181
|
+
terminal: true,
|
|
182
|
+
priority: 400,
|
|
183
|
+
transclude: "element",
|
|
184
|
+
compile: function (tElement, tAttrs, $transclude) {
|
|
185
|
+
return function (scope, $element, attrs) {
|
|
186
|
+
const onloadExp = attrs["onload"] || "",
|
|
187
|
+
autoScrollExp = attrs["autoscroll"],
|
|
188
|
+
renderer = getRenderer(),
|
|
189
|
+
inherited = $element.inheritedData("$uiView") || rootData,
|
|
190
|
+
name =
|
|
191
|
+
$interpolate(attrs["uiView"] || attrs["name"] || "")(scope) ||
|
|
192
|
+
"$default";
|
|
193
|
+
let previousEl, currentEl, currentScope, viewConfig;
|
|
194
|
+
const activeUIView = {
|
|
195
|
+
$type: "ng1",
|
|
196
|
+
id: directive.count++, // Global sequential ID for ui-view tags added to DOM
|
|
197
|
+
name: name, // ui-view name (<div ui-view="name"></div>
|
|
198
|
+
fqn: inherited.$uiView.fqn
|
|
199
|
+
? inherited.$uiView.fqn + "." + name
|
|
200
|
+
: name, // fully qualified name, describes location in DOM
|
|
201
|
+
config: null, // The ViewConfig loaded (from a state.views definition)
|
|
202
|
+
configUpdated: configUpdatedCallback, // Called when the matching ViewConfig changes
|
|
203
|
+
get creationContext() {
|
|
204
|
+
// The context in which this ui-view "tag" was created
|
|
205
|
+
const fromParentTagConfig = parse("$cfg.viewDecl.$context")(
|
|
206
|
+
inherited,
|
|
207
|
+
);
|
|
208
|
+
// Allow <ui-view name="foo"><ui-view name="bar"></ui-view></ui-view>
|
|
209
|
+
// See https://github.com/angular-ui/ui-router/issues/3355
|
|
210
|
+
const fromParentTag = parse("$uiView.creationContext")(inherited);
|
|
211
|
+
return fromParentTagConfig || fromParentTag;
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
trace.traceUIViewEvent("Linking", activeUIView);
|
|
215
|
+
function configUpdatedCallback(config) {
|
|
216
|
+
if (config && !(config instanceof Ng1ViewConfig)) return;
|
|
217
|
+
if (configsEqual(viewConfig, config)) return;
|
|
218
|
+
trace.traceUIViewConfigUpdated(
|
|
219
|
+
activeUIView,
|
|
220
|
+
config && config.viewDecl && config.viewDecl.$context,
|
|
221
|
+
);
|
|
222
|
+
viewConfig = config;
|
|
223
|
+
updateView(config);
|
|
224
|
+
}
|
|
225
|
+
$element.data("$uiView", { $uiView: activeUIView });
|
|
226
|
+
updateView();
|
|
227
|
+
const unregister = $view.registerUIView(activeUIView);
|
|
228
|
+
scope.$on("$destroy", function () {
|
|
229
|
+
trace.traceUIViewEvent("Destroying/Unregistering", activeUIView);
|
|
230
|
+
unregister();
|
|
231
|
+
});
|
|
232
|
+
function cleanupLastView() {
|
|
233
|
+
if (previousEl) {
|
|
234
|
+
trace.traceUIViewEvent(
|
|
235
|
+
"Removing (previous) el",
|
|
236
|
+
previousEl.data("$uiView"),
|
|
237
|
+
);
|
|
238
|
+
previousEl.remove();
|
|
239
|
+
previousEl = null;
|
|
240
|
+
}
|
|
241
|
+
if (currentScope) {
|
|
242
|
+
trace.traceUIViewEvent("Destroying scope", activeUIView);
|
|
243
|
+
currentScope.$destroy();
|
|
244
|
+
currentScope = null;
|
|
245
|
+
}
|
|
246
|
+
if (currentEl) {
|
|
247
|
+
const _viewData = currentEl.data("$uiViewAnim");
|
|
248
|
+
trace.traceUIViewEvent("Animate out", _viewData);
|
|
249
|
+
renderer.leave(currentEl, function () {
|
|
250
|
+
_viewData.$$animLeave.resolve();
|
|
251
|
+
previousEl = null;
|
|
252
|
+
});
|
|
253
|
+
previousEl = currentEl;
|
|
254
|
+
currentEl = null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function updateView(config) {
|
|
258
|
+
const newScope = scope.$new();
|
|
259
|
+
const animEnter = $q.defer(),
|
|
260
|
+
animLeave = $q.defer();
|
|
261
|
+
const $uiViewData = {
|
|
262
|
+
$cfg: config,
|
|
263
|
+
$uiView: activeUIView,
|
|
264
|
+
};
|
|
265
|
+
const $uiViewAnim = {
|
|
266
|
+
$animEnter: animEnter.promise,
|
|
267
|
+
$animLeave: animLeave.promise,
|
|
268
|
+
$$animLeave: animLeave,
|
|
269
|
+
};
|
|
270
|
+
/**
|
|
271
|
+
* @ngdoc event
|
|
272
|
+
* @name ui.router.state.directive:ui-view#$viewContentLoading
|
|
273
|
+
* @eventOf ui.router.state.directive:ui-view
|
|
274
|
+
* @eventType emits on ui-view directive scope
|
|
275
|
+
* @description
|
|
276
|
+
*
|
|
277
|
+
* Fired once the view **begins loading**, *before* the DOM is rendered.
|
|
278
|
+
*
|
|
279
|
+
* @param {Object} event Event object.
|
|
280
|
+
* @param {string} viewName Name of the view.
|
|
281
|
+
*/
|
|
282
|
+
newScope.$emit("$viewContentLoading", name);
|
|
283
|
+
const cloned = $transclude(newScope, function (clone) {
|
|
284
|
+
clone.data("$uiViewAnim", $uiViewAnim);
|
|
285
|
+
clone.data("$uiView", $uiViewData);
|
|
286
|
+
renderer.enter(clone, $element, function onUIViewEnter() {
|
|
287
|
+
animEnter.resolve();
|
|
288
|
+
if (currentScope)
|
|
289
|
+
currentScope.$emit("$viewContentAnimationEnded");
|
|
290
|
+
if (
|
|
291
|
+
(isDefined(autoScrollExp) && !autoScrollExp) ||
|
|
292
|
+
scope.$eval(autoScrollExp)
|
|
293
|
+
) {
|
|
294
|
+
$uiViewScroll(clone);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
cleanupLastView();
|
|
298
|
+
});
|
|
299
|
+
currentEl = cloned;
|
|
300
|
+
currentScope = newScope;
|
|
301
|
+
/**
|
|
302
|
+
* @ngdoc event
|
|
303
|
+
* @name ui.router.state.directive:ui-view#$viewContentLoaded
|
|
304
|
+
* @eventOf ui.router.state.directive:ui-view
|
|
305
|
+
* @eventType emits on ui-view directive scope
|
|
306
|
+
* @description *
|
|
307
|
+
* Fired once the view is **loaded**, *after* the DOM is rendered.
|
|
308
|
+
*
|
|
309
|
+
* @param {Object} event Event object.
|
|
310
|
+
*/
|
|
311
|
+
currentScope.$emit("$viewContentLoaded", config || viewConfig);
|
|
312
|
+
currentScope.$eval(onloadExp);
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
return directive;
|
|
318
|
+
},
|
|
319
|
+
];
|
|
320
|
+
$ViewDirectiveFill.$inject = [
|
|
321
|
+
"$compile",
|
|
322
|
+
"$controller",
|
|
323
|
+
"$transitions",
|
|
324
|
+
"$view",
|
|
325
|
+
"$q",
|
|
326
|
+
];
|
|
327
|
+
/** @hidden */
|
|
328
|
+
function $ViewDirectiveFill($compile, $controller, $transitions, $view, $q) {
|
|
329
|
+
const getControllerAs = parse("viewDecl.controllerAs");
|
|
330
|
+
const getResolveAs = parse("viewDecl.resolveAs");
|
|
331
|
+
return {
|
|
332
|
+
restrict: "ECA",
|
|
333
|
+
priority: -400,
|
|
334
|
+
compile: function (tElement) {
|
|
335
|
+
const initial = tElement.html();
|
|
336
|
+
tElement.empty();
|
|
337
|
+
return function (scope, $element) {
|
|
338
|
+
const data = $element.data("$uiView");
|
|
339
|
+
if (!data) {
|
|
340
|
+
$element.html(initial);
|
|
341
|
+
$compile($element.contents())(scope);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const cfg = data.$cfg || { viewDecl: {}, getTemplate: noop };
|
|
345
|
+
const resolveCtx = cfg.path && new ResolveContext(cfg.path);
|
|
346
|
+
$element.html(cfg.getTemplate($element, resolveCtx) || initial);
|
|
347
|
+
trace.traceUIViewFill(data.$uiView, $element.html());
|
|
348
|
+
const link = $compile($element.contents());
|
|
349
|
+
const controller = cfg.controller;
|
|
350
|
+
const controllerAs = getControllerAs(cfg);
|
|
351
|
+
const resolveAs = getResolveAs(cfg);
|
|
352
|
+
const locals = resolveCtx && getLocals(resolveCtx);
|
|
353
|
+
scope[resolveAs] = locals;
|
|
354
|
+
if (controller) {
|
|
355
|
+
const controllerInstance = $controller(
|
|
356
|
+
controller,
|
|
357
|
+
extend({}, locals, { $scope: scope, $element: $element }),
|
|
358
|
+
);
|
|
359
|
+
if (controllerAs) {
|
|
360
|
+
scope[controllerAs] = controllerInstance;
|
|
361
|
+
scope[controllerAs][resolveAs] = locals;
|
|
362
|
+
}
|
|
363
|
+
// TODO: Use $view service as a central point for registering component-level hooks
|
|
364
|
+
// Then, when a component is created, tell the $view service, so it can invoke hooks
|
|
365
|
+
// $view.componentLoaded(controllerInstance, { $scope: scope, $element: $element });
|
|
366
|
+
// scope.$on('$destroy', () => $view.componentUnloaded(controllerInstance, { $scope: scope, $element: $element }));
|
|
367
|
+
$element.data("$ngControllerController", controllerInstance);
|
|
368
|
+
$element
|
|
369
|
+
.children()
|
|
370
|
+
.data("$ngControllerController", controllerInstance);
|
|
371
|
+
registerControllerCallbacks(
|
|
372
|
+
$q,
|
|
373
|
+
$transitions,
|
|
374
|
+
controllerInstance,
|
|
375
|
+
scope,
|
|
376
|
+
cfg,
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
// Wait for the component to appear in the DOM
|
|
380
|
+
if (isString(cfg.component)) {
|
|
381
|
+
const kebobName = kebobString(cfg.component);
|
|
382
|
+
const tagRegexp = new RegExp(`^(x-|data-)?${kebobName}$`, "i");
|
|
383
|
+
const getComponentController = () => {
|
|
384
|
+
const directiveEl = [].slice
|
|
385
|
+
.call($element[0].children)
|
|
386
|
+
.filter((el) => el && el.tagName && tagRegexp.exec(el.tagName));
|
|
387
|
+
return (
|
|
388
|
+
directiveEl &&
|
|
389
|
+
angular.element(directiveEl).data(`$${cfg.component}Controller`)
|
|
390
|
+
);
|
|
391
|
+
};
|
|
392
|
+
const deregisterWatch = scope.$watch(
|
|
393
|
+
getComponentController,
|
|
394
|
+
function (ctrlInstance) {
|
|
395
|
+
if (!ctrlInstance) return;
|
|
396
|
+
registerControllerCallbacks(
|
|
397
|
+
$q,
|
|
398
|
+
$transitions,
|
|
399
|
+
ctrlInstance,
|
|
400
|
+
scope,
|
|
401
|
+
cfg,
|
|
402
|
+
);
|
|
403
|
+
deregisterWatch();
|
|
404
|
+
},
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
link(scope);
|
|
408
|
+
};
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
/** @hidden */
|
|
413
|
+
const hasComponentImpl =
|
|
414
|
+
typeof angular.module("ui.router")["component"] === "function";
|
|
415
|
+
/** @hidden incrementing id */
|
|
416
|
+
let _uiCanExitId = 0;
|
|
417
|
+
/** @hidden TODO: move these callbacks to $view and/or `/hooks/components.ts` or something */
|
|
418
|
+
function registerControllerCallbacks(
|
|
419
|
+
$q,
|
|
420
|
+
$transitions,
|
|
421
|
+
controllerInstance,
|
|
422
|
+
$scope,
|
|
423
|
+
cfg,
|
|
424
|
+
) {
|
|
425
|
+
// Call $onInit() ASAP
|
|
426
|
+
if (
|
|
427
|
+
isFunction(controllerInstance.$onInit) &&
|
|
428
|
+
!(
|
|
429
|
+
(cfg.viewDecl.component || cfg.viewDecl.componentProvider) &&
|
|
430
|
+
hasComponentImpl
|
|
431
|
+
)
|
|
432
|
+
) {
|
|
433
|
+
controllerInstance.$onInit();
|
|
434
|
+
}
|
|
435
|
+
const viewState = tail(cfg.path).state.self;
|
|
436
|
+
const hookOptions = { bind: controllerInstance };
|
|
437
|
+
// Add component-level hook for onUiParamsChanged
|
|
438
|
+
if (isFunction(controllerInstance.uiOnParamsChanged)) {
|
|
439
|
+
const resolveContext = new ResolveContext(cfg.path);
|
|
440
|
+
const viewCreationTrans = resolveContext.getResolvable("$transition$").data;
|
|
441
|
+
// Fire callback on any successful transition
|
|
442
|
+
const paramsUpdated = ($transition$) => {
|
|
443
|
+
// Exit early if the $transition$ is the same as the view was created within.
|
|
444
|
+
// Exit early if the $transition$ will exit the state the view is for.
|
|
445
|
+
if (
|
|
446
|
+
$transition$ === viewCreationTrans ||
|
|
447
|
+
$transition$.exiting().indexOf(viewState) !== -1
|
|
448
|
+
)
|
|
449
|
+
return;
|
|
450
|
+
const toParams = $transition$.params("to");
|
|
451
|
+
const fromParams = $transition$.params("from");
|
|
452
|
+
const getNodeSchema = (node) => node.paramSchema;
|
|
453
|
+
const toSchema = $transition$
|
|
454
|
+
.treeChanges("to")
|
|
455
|
+
.map(getNodeSchema)
|
|
456
|
+
.reduce(unnestR, []);
|
|
457
|
+
const fromSchema = $transition$
|
|
458
|
+
.treeChanges("from")
|
|
459
|
+
.map(getNodeSchema)
|
|
460
|
+
.reduce(unnestR, []);
|
|
461
|
+
// Find the to params that have different values than the from params
|
|
462
|
+
const changedToParams = toSchema.filter((param) => {
|
|
463
|
+
const idx = fromSchema.indexOf(param);
|
|
464
|
+
return (
|
|
465
|
+
idx === -1 ||
|
|
466
|
+
!fromSchema[idx].type.equals(toParams[param.id], fromParams[param.id])
|
|
467
|
+
);
|
|
468
|
+
});
|
|
469
|
+
// Only trigger callback if a to param has changed or is new
|
|
470
|
+
if (changedToParams.length) {
|
|
471
|
+
const changedKeys = changedToParams.map((x) => x.id);
|
|
472
|
+
// Filter the params to only changed/new to params. `$transition$.params()` may be used to get all params.
|
|
473
|
+
const newValues = filter(
|
|
474
|
+
toParams,
|
|
475
|
+
(val, key) => changedKeys.indexOf(key) !== -1,
|
|
476
|
+
);
|
|
477
|
+
controllerInstance.uiOnParamsChanged(newValues, $transition$);
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
$scope.$on(
|
|
481
|
+
"$destroy",
|
|
482
|
+
$transitions.onSuccess({}, paramsUpdated, hookOptions),
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
// Add component-level hook for uiCanExit
|
|
486
|
+
if (isFunction(controllerInstance.uiCanExit)) {
|
|
487
|
+
const id = _uiCanExitId++;
|
|
488
|
+
const cacheProp = "_uiCanExitIds";
|
|
489
|
+
// Returns true if a redirect transition already answered truthy
|
|
490
|
+
const prevTruthyAnswer = (trans) =>
|
|
491
|
+
!!trans &&
|
|
492
|
+
((trans[cacheProp] && trans[cacheProp][id] === true) ||
|
|
493
|
+
prevTruthyAnswer(trans.redirectedFrom()));
|
|
494
|
+
// If a user answered yes, but the transition was later redirected, don't also ask for the new redirect transition
|
|
495
|
+
const wrappedHook = (trans) => {
|
|
496
|
+
let promise;
|
|
497
|
+
const ids = (trans[cacheProp] = trans[cacheProp] || {});
|
|
498
|
+
if (!prevTruthyAnswer(trans)) {
|
|
499
|
+
promise = $q.when(controllerInstance.uiCanExit(trans));
|
|
500
|
+
promise.then((val) => (ids[id] = val !== false));
|
|
501
|
+
}
|
|
502
|
+
return promise;
|
|
503
|
+
};
|
|
504
|
+
const criteria = { exiting: viewState.name };
|
|
505
|
+
$scope.$on(
|
|
506
|
+
"$destroy",
|
|
507
|
+
$transitions.onBefore(criteria, wrappedHook, hookOptions),
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
window.angular
|
|
512
|
+
.module("ui.router.state")
|
|
513
|
+
.directive("uiView", uiView)
|
|
514
|
+
.directive("uiView", $ViewDirectiveFill);
|