@angular-wave/angular.ts 0.0.21 → 0.0.23

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.
Files changed (134) hide show
  1. package/Makefile +1 -1
  2. package/TODO.md +14 -0
  3. package/dist/angular-ts.esm.js +1 -1
  4. package/dist/angular-ts.umd.js +1 -1
  5. package/index.html +8 -2
  6. package/package.json +1 -1
  7. package/src/animations/animate-css-driver.js +2 -2
  8. package/src/animations/animate-css.js +15 -6
  9. package/src/animations/animate-js.js +1 -1
  10. package/src/animations/animate-queue.js +1 -1
  11. package/src/animations/shared.js +0 -14
  12. package/src/core/compile.js +6 -3
  13. package/src/directive/if.js +0 -79
  14. package/src/directive/if.md +80 -0
  15. package/src/directive/include.js +0 -82
  16. package/src/directive/include.md +86 -0
  17. package/src/directive/repeat.js +0 -1
  18. package/src/filters/order-by.js +8 -8
  19. package/src/loader.js +0 -1
  20. package/src/router/common/trace.js +1 -1
  21. package/src/router/directives/stateDirectives.js +16 -14
  22. package/src/router/directives/viewDirective.js +5 -13
  23. package/src/router/hooks/resolve.js +3 -4
  24. package/src/router/hooks/views.js +3 -2
  25. package/src/router/state/stateService.js +1 -2
  26. package/src/router/stateProvider.js +8 -0
  27. package/src/router/templateFactory.js +13 -7
  28. package/src/router/transition/interface.js +14 -14
  29. package/src/router/transition/rejectFactory.js +29 -20
  30. package/src/router/transition/transition.js +5 -1
  31. package/src/router/transition/transitionHook.js +5 -5
  32. package/src/router/url/urlMatcher.js +1 -2
  33. package/src/router/url/urlRule.js +10 -2
  34. package/src/router/url/urlRules.js +1 -1
  35. package/src/services/browser.js +3 -18
  36. package/src/shared/common.js +0 -1
  37. package/test/module-test.html +44 -12
  38. package/test/router/ng-state-builder.spec.js +81 -0
  39. package/test/router/services.spec.js +70 -0
  40. package/test/router/state-directives.spec.js +1182 -0
  41. package/test/router/template-factory.spec.js +146 -0
  42. package/test/router/url-matcher-factory.spec.js +1313 -0
  43. package/test/router/view-directive.spec.js +2013 -0
  44. package/test/router/view-hook.spec.js +217 -0
  45. package/test/router/view-scroll.spec.js +77 -0
  46. package/test/router/view.spec.js +117 -0
  47. package/types/angular.d.ts +132 -124
  48. package/types/index.d.ts +2350 -2187
  49. package/types/jqlite.d.ts +463 -418
  50. package/types/router/core/common/common.d.ts +70 -24
  51. package/types/router/core/common/coreservices.d.ts +30 -32
  52. package/types/router/core/common/glob.d.ts +9 -9
  53. package/types/router/core/common/hof.d.ts +12 -4
  54. package/types/router/core/common/index.d.ts +8 -8
  55. package/types/router/core/common/predicates.d.ts +1 -1
  56. package/types/router/core/common/queue.d.ts +13 -13
  57. package/types/router/core/common/safeConsole.d.ts +3 -3
  58. package/types/router/core/common/strings.d.ts +4 -2
  59. package/types/router/core/common/trace.d.ts +94 -82
  60. package/types/router/core/globals.d.ts +37 -37
  61. package/types/router/core/hooks/coreResolvables.d.ts +5 -3
  62. package/types/router/core/hooks/ignoredTransition.d.ts +4 -2
  63. package/types/router/core/hooks/invalidTransition.d.ts +4 -2
  64. package/types/router/core/hooks/lazyLoad.d.ts +10 -5
  65. package/types/router/core/hooks/onEnterExitRetain.d.ts +10 -4
  66. package/types/router/core/hooks/redirectTo.d.ts +4 -2
  67. package/types/router/core/hooks/resolve.d.ts +10 -4
  68. package/types/router/core/hooks/updateGlobals.d.ts +4 -2
  69. package/types/router/core/hooks/url.d.ts +4 -2
  70. package/types/router/core/hooks/views.d.ts +7 -3
  71. package/types/router/core/index.d.ts +11 -12
  72. package/types/router/core/interface.d.ts +83 -81
  73. package/types/router/core/params/index.d.ts +5 -5
  74. package/types/router/core/params/interface.d.ts +439 -439
  75. package/types/router/core/params/param.d.ts +72 -60
  76. package/types/router/core/params/paramType.d.ts +40 -40
  77. package/types/router/core/params/paramTypes.d.ts +169 -165
  78. package/types/router/core/params/stateParams.d.ts +13 -13
  79. package/types/router/core/path/index.d.ts +2 -2
  80. package/types/router/core/path/pathNode.d.ts +49 -49
  81. package/types/router/core/path/pathUtils.d.ts +100 -74
  82. package/types/router/core/resolve/index.d.ts +3 -3
  83. package/types/router/core/resolve/interface.d.ts +137 -137
  84. package/types/router/core/resolve/resolvable.d.ts +60 -54
  85. package/types/router/core/resolve/resolveContext.d.ts +84 -79
  86. package/types/router/core/router.d.ts +95 -86
  87. package/types/router/core/state/index.d.ts +8 -8
  88. package/types/router/core/state/interface.d.ts +667 -643
  89. package/types/router/core/state/stateBuilder.d.ts +41 -38
  90. package/types/router/core/state/stateMatcher.d.ts +11 -9
  91. package/types/router/core/state/stateObject.d.ts +154 -139
  92. package/types/router/core/state/stateQueueManager.d.ts +26 -21
  93. package/types/router/core/state/stateRegistry.d.ts +124 -121
  94. package/types/router/core/state/stateService.d.ts +380 -343
  95. package/types/router/core/state/targetState.d.ts +74 -69
  96. package/types/router/core/transition/hookBuilder.d.ts +34 -30
  97. package/types/router/core/transition/hookRegistry.d.ts +96 -74
  98. package/types/router/core/transition/index.d.ts +8 -8
  99. package/types/router/core/transition/interface.d.ts +652 -609
  100. package/types/router/core/transition/rejectFactory.d.ts +97 -97
  101. package/types/router/core/transition/transition.d.ts +565 -517
  102. package/types/router/core/transition/transitionEventType.d.ts +20 -11
  103. package/types/router/core/transition/transitionHook.d.ts +90 -82
  104. package/types/router/core/transition/transitionService.d.ts +228 -161
  105. package/types/router/core/url/index.d.ts +8 -8
  106. package/types/router/core/url/interface.d.ts +100 -87
  107. package/types/router/core/url/urlConfig.d.ts +130 -126
  108. package/types/router/core/url/urlMatcher.d.ts +132 -127
  109. package/types/router/core/url/urlMatcherFactory.d.ts +46 -42
  110. package/types/router/core/url/urlRouter.d.ts +91 -75
  111. package/types/router/core/url/urlRule.d.ts +123 -100
  112. package/types/router/core/url/urlRules.d.ts +240 -232
  113. package/types/router/core/url/urlService.d.ts +201 -201
  114. package/types/router/core/view/index.d.ts +2 -2
  115. package/types/router/core/view/interface.d.ts +26 -26
  116. package/types/router/core/view/view.d.ts +152 -143
  117. package/types/router/directives/viewDirective.d.ts +12 -11
  118. package/types/router/index.d.ts +11 -12
  119. package/types/router/interface.d.ts +361 -351
  120. package/types/router/legacy/resolveService.d.ts +44 -40
  121. package/types/router/legacy/stateEvents.d.ts +1 -1
  122. package/types/router/locationServices.d.ts +45 -37
  123. package/types/router/services.d.ts +9 -9
  124. package/types/router/stateFilters.d.ts +3 -3
  125. package/types/router/stateProvider.d.ts +240 -235
  126. package/types/router/statebuilders/onEnterExitRetain.d.ts +4 -2
  127. package/types/router/statebuilders/views.d.ts +35 -22
  128. package/types/router/templateFactory.d.ts +99 -79
  129. package/types/router/viewScroll.d.ts +7 -7
  130. package/src/directive/a.js +0 -37
  131. package/types/router/angular.d.ts +0 -1
  132. package/types/router/core/vanilla.d.ts +0 -1
  133. package/types/router/directives/stateDirectives.d.ts +0 -3
  134. package/types/router/injectables.d.ts +0 -1
@@ -0,0 +1,2013 @@
1
+ // function animateFlush($animate) {
2
+ // if ($animate && $animate.flush) {
3
+ // $animate.flush(); // 1.4
4
+ // } else if ($animate && $animate.triggerCallbacks) {
5
+ // $animate.triggerCallbacks(); // 1.2-1.3
6
+ // }
7
+ // }
8
+
9
+ import { dealoc, jqLite } from "../../src/jqLite";
10
+ import { Angular } from "../../src/loader";
11
+ import { publishExternalAPI } from "../../src/public";
12
+ import { wait } from "../test-utils";
13
+
14
+ describe("uiView", () => {
15
+ let $stateProvider,
16
+ scope,
17
+ $compile,
18
+ elem,
19
+ log,
20
+ app,
21
+ $injector,
22
+ $state,
23
+ $q,
24
+ $timeout,
25
+ $uiViewScroll;
26
+
27
+ const aState = {
28
+ name: "a",
29
+ template: "aState template",
30
+ },
31
+ bState = {
32
+ name: "b",
33
+ template: "bState template",
34
+ },
35
+ cState = {
36
+ name: "c",
37
+ views: {
38
+ cview: {
39
+ template: "cState cview template",
40
+ },
41
+ },
42
+ },
43
+ dState = {
44
+ name: "d",
45
+ views: {
46
+ dview1: {
47
+ template: "dState dview1 template",
48
+ },
49
+ dview2: {
50
+ template: "dState dview2 template",
51
+ },
52
+ },
53
+ },
54
+ eState = {
55
+ name: "e",
56
+ template: '<div ui-view="eview" class="eview"></div>',
57
+ },
58
+ fState = {
59
+ name: "e.f",
60
+ views: {
61
+ eview: {
62
+ template: "fState eview template",
63
+ },
64
+ },
65
+ },
66
+ gState = {
67
+ name: "g",
68
+ template: '<div ui-view="inner"><span>{{content}}</span></div>',
69
+ },
70
+ hState = {
71
+ name: "g.h",
72
+ views: {
73
+ inner: {
74
+ template: "hState inner template",
75
+ },
76
+ },
77
+ },
78
+ iState = {
79
+ name: "i",
80
+ template:
81
+ "<div ui-view>" +
82
+ '<ul><li ng-repeat="item in items">{{item}}</li></ul>' +
83
+ "</div>",
84
+ },
85
+ jState = {
86
+ name: "j",
87
+ template: "jState",
88
+ },
89
+ kState = {
90
+ name: "k",
91
+ controller: function () {
92
+ this.someProperty = "value";
93
+ },
94
+ template: "{{vm.someProperty}}",
95
+ controllerAs: "vm",
96
+ },
97
+ lState = {
98
+ name: "l",
99
+ views: {
100
+ view1: {
101
+ template: "view1",
102
+ },
103
+ view2: {
104
+ template: "view2",
105
+ },
106
+ view3: {
107
+ template: "view3",
108
+ },
109
+ },
110
+ },
111
+ mState = {
112
+ name: "m",
113
+ template: "mState",
114
+ controller: function ($scope, $element) {
115
+ $scope.elementId = $element.attr("id");
116
+ },
117
+ },
118
+ nState = {
119
+ name: "n",
120
+ template: "nState",
121
+ controller: function ($scope, $element) {
122
+ const data = $element.data("$uiViewAnim");
123
+ $scope.$on("$destroy", () => {
124
+ log += "destroy;";
125
+ });
126
+ data.$animEnter.then(() => {
127
+ log += "animEnter;";
128
+ });
129
+ data.$animLeave.then(() => {
130
+ log += "animLeave;";
131
+ });
132
+ },
133
+ };
134
+
135
+ beforeEach(() => {
136
+ dealoc(document.getElementById("dummy"));
137
+ window.angular = new Angular();
138
+ publishExternalAPI();
139
+ log = "";
140
+ app = window.angular
141
+ .module("defaultModule", ["ui.router"])
142
+ .config(($provide, _$stateProvider_) => {
143
+ $provide.decorator("$uiViewScroll", () => {
144
+ return jasmine.createSpy("$uiViewScroll");
145
+ });
146
+
147
+ _$stateProvider_
148
+ .state(aState)
149
+ .state(bState)
150
+ .state(cState)
151
+ .state(dState)
152
+ .state(eState)
153
+ .state(fState)
154
+ .state(gState)
155
+ .state(hState)
156
+ .state(iState)
157
+ .state(jState)
158
+ .state(kState)
159
+ .state(lState)
160
+ .state(mState)
161
+ .state(nState);
162
+
163
+ $stateProvider = _$stateProvider_;
164
+ });
165
+
166
+ $injector = window.angular.bootstrap(document.getElementById("dummy"), [
167
+ "defaultModule",
168
+ ]);
169
+
170
+ $injector.invoke(
171
+ (_$state_, _$q_, _$timeout_, $rootScope, _$compile_, _$uiViewScroll_) => {
172
+ scope = $rootScope.$new();
173
+ $compile = _$compile_;
174
+ $state = _$state_;
175
+ $q = _$q_;
176
+ $timeout = _$timeout_;
177
+ elem = jqLite("<div>");
178
+ $uiViewScroll = _$uiViewScroll_;
179
+ },
180
+ );
181
+ });
182
+
183
+ describe("linking ui-directive", () => {
184
+ it("anonymous ui-view should be replaced with the template of the current $state", async () => {
185
+ elem.append($compile("<div><ui-view></ui-view></div>")(scope));
186
+
187
+ expect(elem.find("ui-view").text()).toBe("");
188
+
189
+ $state.transitionTo(aState);
190
+ await wait(10);
191
+
192
+ expect(elem.find("ui-view").text()).toBe(aState.template);
193
+ });
194
+
195
+ it("named ui-view should be replaced with the template of the current $state", async () => {
196
+ elem.append(
197
+ $compile('<div><ui-view name="cview"></ui-view></div>')(scope),
198
+ );
199
+
200
+ $state.transitionTo(cState);
201
+ await wait(10);
202
+
203
+ expect(elem.find("ui-view").text()).toBe(cState.views.cview.template);
204
+ });
205
+
206
+ it("ui-view should be updated after transition to another state", async () => {
207
+ elem.append($compile("<div><ui-view></ui-view></div>")(scope));
208
+ expect(elem.find("ui-view").text()).toBe("");
209
+
210
+ $state.transitionTo(aState);
211
+ await wait(10);
212
+
213
+ expect(elem.find("ui-view").text()).toBe(aState.template);
214
+
215
+ $state.transitionTo(bState);
216
+ await wait(10);
217
+
218
+ expect(elem.find("ui-view").text()).toBe(bState.template);
219
+ });
220
+
221
+ it("should handle NOT nested ui-views", async () => {
222
+ elem.append(
223
+ $compile(
224
+ '<div><ui-view name="dview1" class="dview1"></ui-view><ui-view name="dview2" class="dview2"></ui-view></div>',
225
+ )(scope),
226
+ );
227
+ expect(elem.find("ui-view").eq(0).text()).toBe("");
228
+ expect(elem.find("ui-view").eq(1).text()).toBe("");
229
+
230
+ $state.transitionTo(dState);
231
+ await wait(10);
232
+
233
+ expect(elem.find("ui-view").eq(0).text()).toBe(
234
+ dState.views.dview1.template,
235
+ );
236
+ expect(elem.find("ui-view").eq(1).text()).toBe(
237
+ dState.views.dview2.template,
238
+ );
239
+ });
240
+
241
+ it("should handle nested ui-views (testing two levels deep)", async () => {
242
+ $compile(elem.append("<div><ui-view></ui-view></div>"))(scope);
243
+ expect(elem.find("ui-view").text()).toBe("");
244
+
245
+ $state.transitionTo(fState);
246
+ await wait(10);
247
+
248
+ expect(elem.find("ui-view").text()).toBe(fState.views.eview.template);
249
+ });
250
+ });
251
+
252
+ describe("handling initial view", () => {
253
+ it("initial view should be compiled if the view is empty", async () => {
254
+ const content = "inner content";
255
+ scope.content = content;
256
+ elem.append($compile("<div><ui-view></ui-view></div>")(scope));
257
+
258
+ $state.transitionTo(gState);
259
+ await wait(10);
260
+
261
+ expect(elem.find("ui-view").text()).toBe(content);
262
+ });
263
+
264
+ it("initial view should be put back after removal of the view", async () => {
265
+ const content = "inner content";
266
+ scope.content = content;
267
+ elem.append($compile("<div><ui-view></ui-view></div>")(scope));
268
+
269
+ $state.go(hState);
270
+ await wait(10);
271
+
272
+ expect(elem.find("ui-view").text()).toBe(hState.views.inner.template);
273
+
274
+ // going to the parent state which makes the inner view empty
275
+ $state.go(gState);
276
+ await wait(10);
277
+
278
+ expect(elem.find("ui-view").text()).toBe(content);
279
+ });
280
+
281
+ // related to issue #435
282
+ it("initial view should be transcluded once to prevent breaking other directives", async () => {
283
+ scope.items = ["I", "am", "a", "list", "of", "items"];
284
+
285
+ elem.append($compile("<div><ui-view></ui-view></div>")(scope));
286
+
287
+ // transition to state that has an initial view
288
+ $state.transitionTo(iState);
289
+ await wait(10);
290
+
291
+ // verify if ng-repeat has been compiled
292
+ expect(elem.find("li").length).toBe(scope.items.length);
293
+
294
+ // transition to another state that replace the initial content
295
+ $state.transitionTo(jState);
296
+ await wait(10);
297
+
298
+ expect(elem.find("ui-view").text()).toBe(jState.template);
299
+
300
+ // transition back to the state with empty subview and the initial view
301
+ $state.transitionTo(iState);
302
+ await wait(10);
303
+
304
+ // verify if the initial view is correct
305
+ expect(elem.find("li").length).toBe(scope.items.length);
306
+
307
+ // change scope properties
308
+ scope.$apply(() => {
309
+ scope.items.push(".", "Working?");
310
+ });
311
+
312
+ // verify if the initial view has been updated
313
+ expect(elem.find("li").length).toBe(scope.items.length);
314
+ });
315
+ });
316
+
317
+ describe("autoscroll attribute", () => {
318
+ it("should NOT autoscroll when unspecified", async () => {
319
+ elem.append($compile("<div><ui-view></ui-view></div>")(scope));
320
+
321
+ $state.transitionTo(aState);
322
+ await wait(10);
323
+ expect($uiViewScroll).not.toHaveBeenCalled();
324
+ });
325
+
326
+ it("should autoscroll when expression is missing", async () => {
327
+ elem.append($compile("<div><ui-view autoscroll></ui-view></div>")(scope));
328
+ await $state.transitionTo(aState);
329
+ await wait(20);
330
+
331
+ // animateFlush($animate);
332
+
333
+ expect($uiViewScroll).toHaveBeenCalledWith(elem.find("ui-view"));
334
+ });
335
+
336
+ it("should autoscroll based on expression", async () => {
337
+ scope.doScroll = false;
338
+
339
+ elem.append(
340
+ $compile('<div><ui-view autoscroll="doScroll"></ui-view></div>')(scope),
341
+ );
342
+
343
+ $state.transitionTo(aState);
344
+ await wait(10);
345
+
346
+ expect($uiViewScroll).not.toHaveBeenCalled();
347
+
348
+ scope.doScroll = true;
349
+ $state.transitionTo(bState);
350
+ await wait(100);
351
+
352
+ let target,
353
+ index = -1,
354
+ uiViews = elem.find("ui-view");
355
+
356
+ while (index++ < uiViews.length) {
357
+ const uiView = jqLite(uiViews[index]);
358
+ if (uiView.text() === bState.template) target = uiView;
359
+ }
360
+
361
+ expect($uiViewScroll).toHaveBeenCalledWith(target);
362
+ });
363
+ });
364
+
365
+ it("should instantiate a controller with controllerAs", async () => {
366
+ elem.append($compile("<div><ui-view></ui-view></div>")(scope));
367
+ await $state.transitionTo(kState);
368
+ expect(elem.text()).toBe("value");
369
+ });
370
+
371
+ it("should instantiate a controller with both $scope and $element injections", async () => {
372
+ elem.append(
373
+ $compile('<div><ui-view id="mState">{{elementId}}</ui-view></div>')(
374
+ scope,
375
+ ),
376
+ );
377
+ $state.transitionTo(mState);
378
+ await wait(10);
379
+
380
+ expect(elem.text()).toBe("mState");
381
+ });
382
+
383
+ describe("(resolved data)", () => {
384
+ let _scope;
385
+ function controller($scope) {
386
+ _scope = $scope;
387
+ }
388
+
389
+ let _state = {
390
+ name: "resolve",
391
+ resolve: {
392
+ user: function () {
393
+ return $timeout(() => {
394
+ return "joeschmoe";
395
+ }, 100);
396
+ },
397
+ },
398
+ };
399
+
400
+ it("should put the resolved data on the controllerAs", async () => {
401
+ const state = Object.assign(_state, {
402
+ template: "{{$ctrl.$resolve.user}}",
403
+ controllerAs: "$ctrl",
404
+ controller: function ($scope) {
405
+ _scope = $scope;
406
+ },
407
+ });
408
+ $stateProvider.state(state);
409
+ elem.append($compile("<div><ui-view></ui-view></div>")(scope));
410
+
411
+ await $state.transitionTo("resolve");
412
+ await wait(100);
413
+
414
+ expect(elem.text()).toBe("joeschmoe");
415
+ expect(_scope.$resolve).toBeDefined();
416
+ expect(_scope.$ctrl).toBeDefined();
417
+ expect(_scope.$ctrl.$resolve).toBeDefined();
418
+ expect(_scope.$ctrl.$resolve.user).toBe("joeschmoe");
419
+ });
420
+
421
+ it("should provide the resolved data on the $scope", async () => {
422
+ const state = Object.assign(_state, {
423
+ template: "{{$resolve.user}}",
424
+ controller: controller,
425
+ });
426
+
427
+ $stateProvider.state(state);
428
+ elem.append($compile("<div><ui-view></ui-view></div>")(scope));
429
+
430
+ await $state.transitionTo("resolve");
431
+ await wait(10);
432
+
433
+ expect(elem.text()).toBe("joeschmoe");
434
+ expect(_scope.$resolve).toBeDefined();
435
+ expect(_scope.$resolve.user).toBe("joeschmoe");
436
+ });
437
+
438
+ // Test for #2626
439
+ it("should provide the resolved data on the $scope even if there is no controller", async () => {
440
+ const state = Object.assign(_state, {
441
+ template: "{{$resolve.user}}",
442
+ });
443
+ $stateProvider.state(state);
444
+ elem.append($compile("<div><ui-view></ui-view></div>")(scope));
445
+ expect(elem.text()).toBe("");
446
+
447
+ await $state.transitionTo("resolve");
448
+ await wait(10);
449
+
450
+ expect(elem.text()).toBe("joeschmoe");
451
+ });
452
+
453
+ it("should put the resolved data on the resolveAs variable", async () => {
454
+ const state = Object.assign(_state, {
455
+ template: "{{$$$resolve.user}}",
456
+ resolveAs: "$$$resolve",
457
+ controller: controller,
458
+ });
459
+ $stateProvider.state(state);
460
+ elem.append($compile("<div><ui-view></ui-view></div>")(scope));
461
+
462
+ await $state.transitionTo("resolve");
463
+ await wait(10);
464
+
465
+ expect(elem.text()).toBe("joeschmoe");
466
+ expect(_scope.$$$resolve).toBeDefined();
467
+ expect(_scope.$$$resolve.user).toBe("joeschmoe");
468
+ });
469
+
470
+ it("should not allow both view-level resolveAs and state-level resolveAs on the same state", async () => {
471
+ const views = {
472
+ $default: {
473
+ controller: controller,
474
+ template: "{{$$$resolve.user}}",
475
+ resolveAs: "$$$resolve",
476
+ },
477
+ };
478
+ const state = Object.assign(_state, {
479
+ resolveAs: "foo",
480
+ views: views,
481
+ });
482
+ expect(() => $stateProvider.state(state)).toThrowError(/resolveAs/);
483
+ });
484
+ });
485
+
486
+ it("should call the existing $onInit after instantiating a controller", async () => {
487
+ const $onInit = jasmine.createSpy();
488
+ $stateProvider.state({
489
+ name: "onInit",
490
+ controller: function () {
491
+ this.$onInit = $onInit;
492
+ },
493
+ template: "hi",
494
+ controllerAs: "vm",
495
+ });
496
+ elem.append($compile("<div><ui-view></ui-view></div>")(scope));
497
+ await $state.transitionTo("onInit");
498
+ await wait(10);
499
+
500
+ expect($onInit).toHaveBeenCalled();
501
+ });
502
+
503
+ // TargetNode not found error
504
+ xit("should default the template to a <ui-view>", async () => {
505
+ $stateProvider.state({ name: "abstract", abstract: true });
506
+ $stateProvider.state({ name: "abstract.foo", template: "hello" });
507
+ elem.append($compile("<div><ui-view></ui-view></div>")(scope));
508
+ $state.transitionTo("abstract.foo");
509
+ await wait(10);
510
+
511
+ expect(elem.text()).toBe("hello");
512
+ });
513
+
514
+ describe("play nicely with other directives", () => {
515
+ // related to issue #857
516
+ it("should work with ngIf", async () => {
517
+ scope.someBoolean = false;
518
+ elem.append(
519
+ $compile('<div ng-if="someBoolean"><ui-view></ui-view></div>')(scope),
520
+ );
521
+
522
+ $state.transitionTo(aState);
523
+ await wait(10);
524
+
525
+ // Verify there is no ui-view in the DOM
526
+ expect(elem.find("ui-view").length).toBe(0);
527
+
528
+ // Turn on the div that holds the ui-view
529
+ scope.someBoolean = true;
530
+ scope.$digest();
531
+
532
+ // Verify that the ui-view is there and it has the correct content
533
+ expect(elem.find("ui-view").text()).toBe(aState.template);
534
+
535
+ // Turn off the ui-view
536
+ scope.someBoolean = false;
537
+ scope.$digest();
538
+
539
+ // Verify there is no ui-view in the DOM
540
+ expect(elem.find("ui-view").length).toBe(0);
541
+
542
+ // Turn on the div that holds the ui-view once again
543
+ scope.someBoolean = true;
544
+ scope.$digest();
545
+
546
+ // Verify that the ui-view is there and it has the correct content
547
+ expect(elem.find("ui-view").text()).toBe(aState.template);
548
+ });
549
+
550
+ it("should work with ngClass", async () => {
551
+ const classes = (elem) => Array.prototype.slice.call(elem[0].classList);
552
+
553
+ scope.showClass = false;
554
+ elem.append(
555
+ $compile(
556
+ "<div><ui-view ng-class=\"{'someClass': showClass}\"></ui-view></div>",
557
+ )(scope),
558
+ );
559
+
560
+ expect(classes(elem.find("ui-view"))).not.toContain("someClass");
561
+
562
+ scope.showClass = true;
563
+ scope.$digest();
564
+
565
+ expect(classes(elem.find("ui-view"))).toContain("someClass");
566
+
567
+ scope.showClass = false;
568
+ scope.$digest();
569
+
570
+ expect(classes(elem.find("ui-view"))).not.toContain("someClass");
571
+ });
572
+
573
+ describe("working with ngRepeat", () => {
574
+ it("should have correct number of uiViews", async () => {
575
+ elem.append(
576
+ $compile(
577
+ '<div><ui-view ng-repeat="view in views" name="{{view}}"></ui-view></div>',
578
+ )(scope),
579
+ );
580
+
581
+ // Should be no ui-views in DOM
582
+ expect(elem.find("ui-view").length).toBe(0);
583
+
584
+ // Lets add 3
585
+ scope.views = ["view1", "view2", "view3"];
586
+ scope.$digest();
587
+
588
+ // Should be 3 ui-views in the DOM
589
+ expect(elem.find("ui-view").length).toBe(scope.views.length);
590
+
591
+ // Lets add one more - yay two-way binding
592
+ scope.views.push("view4");
593
+ scope.$digest();
594
+
595
+ // Should have 4 ui-views
596
+ expect(elem.find("ui-view").length).toBe(scope.views.length);
597
+
598
+ // Lets remove 2 ui-views from the DOM
599
+ scope.views.pop();
600
+ scope.views.pop();
601
+ scope.$digest();
602
+
603
+ // Should have 2 ui-views
604
+ expect(elem.find("ui-view").length).toBe(scope.views.length);
605
+ });
606
+
607
+ it("should populate each view with content", async () => {
608
+ elem.append(
609
+ $compile(
610
+ '<div><ui-view ng-repeat="view in views" name="{{view}}">defaultcontent</ui-view></div>',
611
+ )(scope),
612
+ );
613
+
614
+ $state.transitionTo(lState);
615
+ await wait(10);
616
+
617
+ expect(elem.find("ui-view").length).toBe(0);
618
+
619
+ scope.views = ["view1", "view2"];
620
+
621
+ scope.$digest();
622
+
623
+ let uiViews = elem.find("ui-view");
624
+
625
+ expect(uiViews.eq(0).text()).toBe(lState.views.view1.template);
626
+ expect(uiViews.eq(1).text()).toBe(lState.views.view2.template);
627
+ expect(uiViews.eq(2).length).toBe(0);
628
+
629
+ scope.views.push("view3");
630
+ scope.$digest();
631
+
632
+ uiViews = elem.find("ui-view");
633
+
634
+ expect(uiViews.eq(0).text()).toBe(lState.views.view1.template);
635
+ expect(uiViews.eq(1).text()).toBe(lState.views.view2.template);
636
+ expect(uiViews.eq(2).text()).toBe(lState.views.view3.template);
637
+ });
638
+
639
+ it("should interpolate ui-view names", async () => {
640
+ elem.append(
641
+ $compile(
642
+ '<div ng-repeat="view in views">' +
643
+ '<ui-view name="view{{$index + 1}}">hallo</ui-view>' +
644
+ "</div>",
645
+ )(scope),
646
+ );
647
+
648
+ $state.transitionTo(lState);
649
+ await wait(10);
650
+
651
+ expect(elem.find("ui-view").length).toBe(0);
652
+
653
+ scope.views = ["view1", "view2"];
654
+
655
+ scope.$digest();
656
+
657
+ let uiViews = elem.find("ui-view");
658
+
659
+ expect(uiViews.eq(0).text()).toBe(lState.views.view1.template);
660
+ expect(uiViews.eq(1).text()).toBe(lState.views.view2.template);
661
+ expect(uiViews.eq(2).length).toBe(0);
662
+
663
+ scope.views.push("view3");
664
+ scope.$digest();
665
+
666
+ uiViews = elem.find("ui-view");
667
+
668
+ expect(uiViews.eq(0).text()).toBe(lState.views.view1.template);
669
+ expect(uiViews.eq(1).text()).toBe(lState.views.view2.template);
670
+ expect(uiViews.eq(2).text()).toBe(lState.views.view3.template);
671
+ });
672
+ });
673
+ });
674
+
675
+ // describe("AngularJS Animations", () => {
676
+ // it("should do transition animations", async () => {
677
+ // let content = "Initial Content",
678
+ // animation;
679
+ // elem.append(
680
+ // $compile("<div><ui-view>" + content + "</ui-view></div>")(scope),
681
+ // );
682
+
683
+ // // Enter Animation
684
+ // animation = $animate.queue.shift();
685
+ // expect(animation.event).toBe("enter");
686
+ // expect(animation.element.text() + "-1").toBe(content + "-1");
687
+
688
+ // $state.transitionTo(aState);
689
+ // await wait(10);
690
+
691
+ // // Enter Animation
692
+ // animation = $animate.queue.shift();
693
+ // expect(animation.event).toBe("enter");
694
+ // expect(animation.element.text() + "-2").toBe(aState.template + "-2");
695
+ // // Leave Animation
696
+ // animation = $animate.queue.shift();
697
+ // expect(animation.event).toBe("leave");
698
+ // expect(animation.element.text() + "-3").toBe(content + "-3");
699
+
700
+ // $state.transitionTo(bState);
701
+ // await wait(10);
702
+
703
+ // // Enter Animation
704
+ // animation = $animate.queue.shift();
705
+ // expect(animation.event).toBe("enter");
706
+ // expect(animation.element.text() + "-4").toBe(bState.template + "-4");
707
+ // // Leave Animation
708
+ // animation = $animate.queue.shift();
709
+ // expect(animation.event).toBe("leave");
710
+ // expect(animation.element.text() + "-5").toBe(aState.template + "-5");
711
+
712
+ // // No more animations
713
+ // expect($animate.queue.length).toBe(0);
714
+ // });
715
+
716
+ // it("should do ngClass animations", async () => {
717
+ // scope.classOn = false;
718
+ // let content = "Initial Content",
719
+ // className = "yay",
720
+ // animation;
721
+ // elem.append(
722
+ // $compile(
723
+ // "<div><ui-view ng-class=\"{'" +
724
+ // className +
725
+ // "': classOn}\">" +
726
+ // content +
727
+ // "</ui-view></div>",
728
+ // )(scope),
729
+ // );
730
+ // // Don't care about enter class
731
+ // $animate.queue.shift();
732
+
733
+ // scope.classOn = true;
734
+ // scope.$digest();
735
+
736
+ // animation = $animate.queue.shift();
737
+ // expect(animation.event).toBe("addClass");
738
+ // expect(animation.element.text()).toBe(content);
739
+
740
+ // scope.classOn = false;
741
+ // scope.$digest();
742
+
743
+ // animation = $animate.queue.shift();
744
+ // expect(animation.event).toBe("removeClass");
745
+ // expect(animation.element.text()).toBe(content);
746
+
747
+ // // No more animations
748
+ // expect($animate.queue.length).toBe(0);
749
+ // });
750
+
751
+ // it("should do ngIf animations", async () => {
752
+ // scope.shouldShow = false;
753
+ // let content = "Initial Content",
754
+ // animation;
755
+ // elem.append(
756
+ // $compile(
757
+ // '<div><ui-view ng-if="shouldShow">' + content + "</ui-view></div>",
758
+ // )(scope),
759
+ // );
760
+
761
+ // // No animations yet
762
+ // expect($animate.queue.length).toBe(0);
763
+
764
+ // scope.shouldShow = true;
765
+ // scope.$digest();
766
+
767
+ // // $ViewDirective enter animation - Basically it's just the <!-- uiView --> comment
768
+ // animation = $animate.queue.shift();
769
+ // expect(animation.event).toBe("enter");
770
+ // expect(animation.element.text()).toBe("");
771
+
772
+ // // $ViewDirectiveFill enter animation - The second uiView directive that files in the content
773
+ // animation = $animate.queue.shift();
774
+ // expect(animation.event).toBe("enter");
775
+ // expect(animation.element.text()).toBe(content);
776
+
777
+ // scope.shouldShow = false;
778
+ // scope.$digest();
779
+
780
+ // // uiView leave animation
781
+ // animation = $animate.queue.shift();
782
+ // expect(animation.event).toBe("leave");
783
+ // expect(animation.element.text()).toBe(content);
784
+
785
+ // // No more animations
786
+ // expect($animate.queue.length).toBe(0);
787
+ // });
788
+
789
+ // it("should expose animation promises to controllers", async () => {
790
+ // $transitions.onStart({}, function ($transition$) {
791
+ // log += "start:" + $transition$.to().name + ";";
792
+ // });
793
+ // $transitions.onFinish({}, function ($transition$) {
794
+ // log += "finish:" + $transition$.to().name + ";";
795
+ // });
796
+ // $transitions.onSuccess({}, function ($transition$) {
797
+ // log += "success:" + $transition$.to().name + ";";
798
+ // });
799
+
800
+ // const content = "Initial Content";
801
+ // elem.append(
802
+ // $compile("<div><ui-view>" + content + "</ui-view></div>")(scope),
803
+ // );
804
+ // $state.transitionTo("n");
805
+ // await wait(10);
806
+
807
+ // expect($state.current.name).toBe("n");
808
+ // expect(log).toBe("start:n;finish:n;success:n;");
809
+
810
+ // // animateFlush($animate);
811
+ // await wait(10);
812
+ // expect(log).toBe("start:n;finish:n;success:n;animEnter;");
813
+
814
+ // $state.transitionTo("a");
815
+ // await wait(10);
816
+ // expect($state.current.name).toBe("a");
817
+ // expect(log).toBe(
818
+ // "start:n;finish:n;success:n;animEnter;start:a;finish:a;destroy;success:a;",
819
+ // );
820
+
821
+ // // animateFlush($animate);
822
+ // await wait(10);
823
+ // expect(log).toBe(
824
+ // "start:n;finish:n;success:n;animEnter;start:a;finish:a;destroy;success:a;animLeave;",
825
+ // );
826
+ // });
827
+ // });
828
+ });
829
+
830
+ describe("UiView", () => {
831
+ let $stateProvider,
832
+ scope,
833
+ $compile,
834
+ elem,
835
+ log,
836
+ app,
837
+ $injector,
838
+ $state,
839
+ $q,
840
+ $timeout,
841
+ $rootScope,
842
+ $uiViewScroll;
843
+
844
+ beforeEach(() => {
845
+ dealoc(document.getElementById("dummy"));
846
+ window.angular = new Angular();
847
+ publishExternalAPI();
848
+ log = "";
849
+ app = window.angular
850
+ .module("defaultModule", ["ui.router"])
851
+ .config((_$stateProvider_) => {
852
+ $stateProvider = _$stateProvider_;
853
+ $stateProvider
854
+ .state({ name: "main", abstract: true, views: { main: {} } })
855
+ .state({
856
+ name: "main.home",
857
+ views: { content: { template: "HOME" } },
858
+ })
859
+ .state({ name: "test", views: { nest: { template: "TEST" } } });
860
+ });
861
+
862
+ $injector = window.angular.bootstrap(document.getElementById("dummy"), [
863
+ "defaultModule",
864
+ ]);
865
+
866
+ $injector.invoke(
867
+ (
868
+ _$state_,
869
+ _$q_,
870
+ _$timeout_,
871
+ _$rootScope_,
872
+ _$compile_,
873
+ _$uiViewScroll_,
874
+ ) => {
875
+ $rootScope = _$rootScope_;
876
+ scope = $rootScope.$new();
877
+ $compile = _$compile_;
878
+ $state = _$state_;
879
+ $q = _$q_;
880
+ $timeout = _$timeout_;
881
+ elem = jqLite("<div>");
882
+ $uiViewScroll = _$uiViewScroll_;
883
+ },
884
+ );
885
+ });
886
+
887
+ // TODO targetNode not found
888
+ xit("shouldn't puke on weird nested view setups", async () => {
889
+ $compile('<div ui-view="main"><div ui-view="content"></div></div>')(
890
+ $rootScope,
891
+ );
892
+
893
+ await $state.go("main.home");
894
+ await wait(10);
895
+
896
+ expect($state.current.name).toBe("main.home");
897
+ });
898
+
899
+ // Test for https://github.com/angular-ui/ui-router/issues/3355
900
+ it("should target weird nested view setups using the view's simple name", async () => {
901
+ const tpl = `
902
+ <div>
903
+ <div ui-view="main">
904
+ MAIN-DEFAULT-
905
+ <div ui-view="content">
906
+ <div ui-view="nest"></div>
907
+ </div>
908
+ </div>
909
+ </div>
910
+ `;
911
+ const el = $compile(tpl)($rootScope);
912
+
913
+ $state.go("test");
914
+ await wait(10);
915
+
916
+ expect($state.current.name).toBe("test");
917
+ expect(el.text().replace(/\s*/g, "")).toBe("MAIN-DEFAULT-TEST");
918
+ });
919
+ });
920
+
921
+ describe("uiView transclusion", () => {
922
+ let scope, $compile, elem, $injector, $rootScope, $state;
923
+
924
+ beforeEach(() => {
925
+ dealoc(document.getElementById("dummy"));
926
+ window.angular = new Angular();
927
+ publishExternalAPI();
928
+ window.angular
929
+ .module("defaultModule", ["ui.router"])
930
+ .directive("scopeObserver", () => {
931
+ return {
932
+ restrict: "E",
933
+ link: function (scope) {
934
+ scope.$emit("directiveCreated");
935
+ scope.$on("$destroy", () => {
936
+ scope.$emit("directiveDestroyed");
937
+ });
938
+ },
939
+ };
940
+ })
941
+ .config(function ($stateProvider) {
942
+ $stateProvider
943
+ .state({
944
+ name: "a",
945
+ template: "<ui-view><scope-observer></scope-observer></ui-view>",
946
+ })
947
+ .state({ name: "a.b", template: "anything" });
948
+ });
949
+ $injector = window.angular.bootstrap(document.getElementById("dummy"), [
950
+ "defaultModule",
951
+ ]);
952
+
953
+ $injector.invoke(
954
+ (
955
+ _$state_,
956
+ _$q_,
957
+ _$timeout_,
958
+ _$rootScope_,
959
+ _$compile_,
960
+ _$uiViewScroll_,
961
+ ) => {
962
+ $rootScope = _$rootScope_;
963
+ scope = $rootScope.$new();
964
+ $compile = _$compile_;
965
+ $state = _$state_;
966
+ elem = jqLite("<div>");
967
+ },
968
+ );
969
+ });
970
+
971
+ it("should not link the initial view and leave its scope undestroyed when a subview is activated", async () => {
972
+ let aliveCount = 0;
973
+ scope.$on("directiveCreated", () => {
974
+ aliveCount++;
975
+ });
976
+ scope.$on("directiveDestroyed", () => {
977
+ aliveCount--;
978
+ });
979
+ elem.append($compile("<div><ui-view></ui-view></div>")(scope));
980
+ $state.transitionTo("a.b");
981
+ await wait(10);
982
+ expect(aliveCount).toBe(0);
983
+ });
984
+ });
985
+
986
+ describe("uiView controllers or onEnter handlers", () => {
987
+ let el, template, scope, count, $rootScope, $compile, $state, elem;
988
+
989
+ beforeEach(() => {
990
+ dealoc(document.getElementById("dummy"));
991
+ window.angular = new Angular();
992
+ publishExternalAPI();
993
+ window.angular
994
+ .module("defaultModule", ["ui.router"])
995
+ .config(function ($stateProvider) {
996
+ count = 0;
997
+ $stateProvider
998
+ .state({
999
+ name: "aside",
1000
+ url: "/aside",
1001
+ template: '<div class="aside"></div>',
1002
+ })
1003
+ .state({
1004
+ name: "A",
1005
+ url: "/A",
1006
+ template: '<div class="A" ui-view="fwd"></div>',
1007
+ })
1008
+ .state({
1009
+ name: "A.fwd",
1010
+ url: "/fwd",
1011
+ views: {
1012
+ fwd: {
1013
+ template: '<div class="fwd" ui-view>',
1014
+ controller: function ($state) {
1015
+ if (count++ < 20 && $state.current.name == "A.fwd")
1016
+ $state.go(".nest");
1017
+ },
1018
+ },
1019
+ },
1020
+ })
1021
+ .state({
1022
+ name: "A.fwd.nest",
1023
+ url: "/nest",
1024
+ template: '<div class="nest"></div>',
1025
+ });
1026
+ });
1027
+
1028
+ let $injector = window.angular.bootstrap(document.getElementById("dummy"), [
1029
+ "defaultModule",
1030
+ ]);
1031
+
1032
+ $injector.invoke(
1033
+ (
1034
+ _$state_,
1035
+ _$q_,
1036
+ _$timeout_,
1037
+ _$rootScope_,
1038
+ _$compile_,
1039
+ _$uiViewScroll_,
1040
+ ) => {
1041
+ $rootScope = _$rootScope_;
1042
+ scope = $rootScope.$new();
1043
+ $compile = _$compile_;
1044
+ $state = _$state_;
1045
+ elem = jqLite("<div>");
1046
+ },
1047
+ );
1048
+ });
1049
+
1050
+ it("should not go into an infinite loop when controller uses $state.go", async () => {
1051
+ el = jqLite("<div><ui-view></ui-view></div>");
1052
+ template = $compile(el)($rootScope);
1053
+ $rootScope.$digest();
1054
+
1055
+ await $state.transitionTo("aside");
1056
+ await wait(10);
1057
+ expect(template[0].querySelector(".aside")).toBeDefined();
1058
+ expect(template[0].querySelector(".fwd")).toBeNull();
1059
+
1060
+ await $state.transitionTo("A");
1061
+ await wait(10);
1062
+ expect(template[0].querySelector(".A")).not.toBeNull();
1063
+ expect(template[0].querySelector(".fwd")).toBeNull();
1064
+
1065
+ await $state.transitionTo("A.fwd");
1066
+ await wait(10);
1067
+ expect(template[0].querySelector(".A")).not.toBeNull();
1068
+ expect(template[0].querySelector(".fwd")).not.toBeNull();
1069
+ expect(template[0].querySelector(".nest")).not.toBeNull();
1070
+ expect(count).toBe(1);
1071
+ });
1072
+ });
1073
+
1074
+ describe("angular 1.5+ style .component()", () => {
1075
+ let el, app, scope, log, svcs, $stateProvider, $templateCache, $rootScope;
1076
+
1077
+ beforeEach(() => {
1078
+ dealoc(document.getElementById("dummy"));
1079
+ window.angular = new Angular();
1080
+ publishExternalAPI();
1081
+ window.angular
1082
+ .module("defaultModule", ["ui.router"])
1083
+ .directive("ng12Directive", () => {
1084
+ return {
1085
+ restrict: "E",
1086
+ scope: { data: "=" },
1087
+ templateUrl: "/comp_tpl.html",
1088
+ controller: function ($scope) {
1089
+ this.data = $scope.data;
1090
+ },
1091
+ controllerAs: "$ctrl",
1092
+ };
1093
+ })
1094
+ .directive("ng13Directive", () => {
1095
+ return {
1096
+ scope: { data: "=" },
1097
+ templateUrl: "/comp_tpl.html",
1098
+ controller: function () {
1099
+ this.$onInit = function () {
1100
+ log += "onInit;";
1101
+ };
1102
+ },
1103
+ bindToController: true,
1104
+ controllerAs: "$ctrl",
1105
+ };
1106
+ })
1107
+ .directive("ng12DynamicDirective", () => {
1108
+ return {
1109
+ restrict: "E",
1110
+ template: "dynamic directive",
1111
+ };
1112
+ })
1113
+ .component("ngComponent", {
1114
+ bindings: { data: "<", data2: "<" },
1115
+ templateUrl: "/comp_tpl.html",
1116
+ controller: function () {
1117
+ this.$onInit = function () {
1118
+ log += "onInit;";
1119
+ };
1120
+ },
1121
+ })
1122
+ .component("header", {
1123
+ bindings: { status: "<" },
1124
+ template: "#{{ $ctrl.status }}#",
1125
+ })
1126
+ .component("bindingTypes", {
1127
+ bindings: { oneway: "<oneway", twoway: "=", attribute: "@attr" },
1128
+ template:
1129
+ "-{{ $ctrl.oneway }},{{ $ctrl.twoway }},{{ $ctrl.attribute }}-",
1130
+ })
1131
+ .component("optionalBindingTypes", {
1132
+ bindings: { oneway: "<?oneway", twoway: "=?", attribute: "@?attr" },
1133
+ template:
1134
+ "-{{ $ctrl.oneway }},{{ $ctrl.twoway }},{{ $ctrl.attribute }}-",
1135
+ })
1136
+ .component("eventComponent", {
1137
+ bindings: { evt: "&" },
1138
+ template: "eventCmp",
1139
+ })
1140
+ .component("mydataComponent", {
1141
+ bindings: { dataUser: "<" },
1142
+ template: "-{{ $ctrl.dataUser }}-",
1143
+ })
1144
+ .component("dataComponent", {
1145
+ template: "DataComponent",
1146
+ })
1147
+ .component("parentCallbackComponent", {
1148
+ controller: function ($rootScope) {
1149
+ this.handleEvent = function (foo, bar) {
1150
+ $rootScope.log.push(foo);
1151
+ $rootScope.log.push(bar);
1152
+ };
1153
+ },
1154
+ template: `
1155
+ <h1>parentCmp</h1>
1156
+ <ui-view on-event="$ctrl.handleEvent(foo, bar)"></ui-view>
1157
+ `,
1158
+ })
1159
+ .component("childEventComponent", {
1160
+ bindings: { onEvent: "&" },
1161
+ template: `
1162
+ <h1>childCmp</h1>
1163
+ <button id="eventbtn" ng-click="$ctrl.onEvent({ foo: 123, bar: 456 })">Button</button>
1164
+ `,
1165
+ })
1166
+ .component("dynamicComponent", {
1167
+ template: "dynamicComponent {{ $ctrl.param }}",
1168
+ controller: function () {
1169
+ this.uiOnParamsChanged = function (params) {
1170
+ this.param = params.param;
1171
+ };
1172
+ },
1173
+ })
1174
+ .config(function (_$stateProvider_) {
1175
+ $stateProvider = _$stateProvider_;
1176
+ });
1177
+
1178
+ let $injector = window.angular.bootstrap(document.getElementById("dummy"), [
1179
+ "defaultModule",
1180
+ ]);
1181
+
1182
+ $injector.invoke(
1183
+ (
1184
+ _$rootScope_,
1185
+ _$httpBackend_,
1186
+ _$compile_,
1187
+ _$state_,
1188
+ _$q_,
1189
+ _$templateCache_,
1190
+ ) => {
1191
+ svcs = {
1192
+ $httpBackend: _$httpBackend_,
1193
+ $compile: _$compile_,
1194
+ $state: _$state_,
1195
+ $q: _$q_,
1196
+ };
1197
+ $rootScope = _$rootScope_;
1198
+ scope = $rootScope.$new();
1199
+ log = "";
1200
+ el = jqLite("<div><ui-view></ui-view></div>");
1201
+ svcs.$compile(el)(scope);
1202
+ $templateCache = _$templateCache_;
1203
+ },
1204
+ );
1205
+ });
1206
+
1207
+ describe("routing using component templates", () => {
1208
+ beforeEach(() => {
1209
+ $stateProvider.state({
1210
+ name: "cmp_tpl",
1211
+ url: "/cmp_tpl",
1212
+ templateUrl: "/state_tpl.html",
1213
+ controller: function () {},
1214
+ resolve: {
1215
+ data: function () {
1216
+ return "DATA!";
1217
+ },
1218
+ },
1219
+ });
1220
+ });
1221
+
1222
+ it("should work with directives which themselves have templateUrls", async () => {
1223
+ const $state = svcs.$state;
1224
+
1225
+ $templateCache.put(
1226
+ "/state_tpl.html",
1227
+ 'x<ng12-directive data="$resolve.data"></ng12-directive>x',
1228
+ );
1229
+ $templateCache.put("/comp_tpl.html", "-{{ $ctrl.data }}-");
1230
+
1231
+ await $state.transitionTo("cmp_tpl");
1232
+
1233
+ expect($state.current.name).toBe("cmp_tpl");
1234
+ expect(el[0].querySelector("ui-view").innerHTML).toEqual(
1235
+ 'x<ng12-directive data="$resolve.data">-DATA!-</ng12-directive>x',
1236
+ );
1237
+ });
1238
+
1239
+ it("should work with bindToController directives", async () => {
1240
+ const $state = svcs.$state;
1241
+
1242
+ $templateCache.put(
1243
+ "/state_tpl.html",
1244
+ 'x<ng13-directive data="$resolve.data"></ng13-directive>x',
1245
+ );
1246
+ $templateCache.put("/comp_tpl.html", "-{{ $ctrl.data }}-");
1247
+
1248
+ await $state.transitionTo("cmp_tpl");
1249
+ await wait(10);
1250
+
1251
+ expect($state.current.name).toBe("cmp_tpl");
1252
+ expect(el[0].querySelector("ui-view").innerHTML).toEqual(
1253
+ 'x<ng13-directive data="$resolve.data">-DATA!-</ng13-directive>x',
1254
+ );
1255
+ });
1256
+
1257
+ it("should work with .component()s", async () => {
1258
+ const $state = svcs.$state;
1259
+
1260
+ $templateCache.put(
1261
+ "/state_tpl.html",
1262
+ 'x<ng-component data="$resolve.data"></ng-component>x',
1263
+ );
1264
+ $templateCache.put("/comp_tpl.html", "-{{ $ctrl.data }}-");
1265
+
1266
+ $state.transitionTo("cmp_tpl");
1267
+ await wait(10);
1268
+
1269
+ expect($state.current.name).toBe("cmp_tpl");
1270
+ expect(el[0].querySelector("ui-view").innerHTML).toEqual(
1271
+ 'x<ng-component data="$resolve.data">-DATA!-</ng-component>x',
1272
+ );
1273
+ });
1274
+ });
1275
+
1276
+ describe("+ component: declaration", () => {
1277
+ it("should disallow controller/template configuration", () => {
1278
+ const stateDef = {
1279
+ name: "route2cmp",
1280
+ url: "/route2cmp",
1281
+ component: "ng12Directive",
1282
+ resolve: {
1283
+ data: function () {
1284
+ return "DATA!";
1285
+ },
1286
+ },
1287
+ };
1288
+
1289
+ expect(() => {
1290
+ $stateProvider.state(
1291
+ Object.assign({ name: "route2cmp", template: "fail" }, stateDef),
1292
+ );
1293
+ }).toThrow();
1294
+ expect(() => {
1295
+ $stateProvider.state(
1296
+ Object.assign(
1297
+ { name: "route2cmp", templateUrl: "fail.html" },
1298
+ stateDef,
1299
+ ),
1300
+ );
1301
+ }).toThrow();
1302
+ expect(() => {
1303
+ $stateProvider.state(
1304
+ Object.assign(
1305
+ { name: "route2cmp", templateProvider: () => {} },
1306
+ stateDef,
1307
+ ),
1308
+ );
1309
+ }).toThrow();
1310
+ expect(() => {
1311
+ $stateProvider.state(
1312
+ Object.assign({ name: "route2cmp", controllerAs: "fail" }, stateDef),
1313
+ );
1314
+ }).toThrow();
1315
+ expect(() => {
1316
+ $stateProvider.state(
1317
+ Object.assign(
1318
+ { name: "route2cmp", controller: "FailCtrl" },
1319
+ stateDef,
1320
+ ),
1321
+ );
1322
+ }).toThrow();
1323
+ expect(() => {
1324
+ $stateProvider.state(
1325
+ Object.assign(
1326
+ { name: "route2cmp", controllerProvider: function () {} },
1327
+ stateDef,
1328
+ ),
1329
+ );
1330
+ }).toThrow();
1331
+
1332
+ expect(() => {
1333
+ $stateProvider.state(stateDef);
1334
+ }).not.toThrow();
1335
+ });
1336
+
1337
+ it("should work with angular 1.2+ directives", async () => {
1338
+ $stateProvider.state({
1339
+ name: "route2cmp",
1340
+ url: "/route2cmp",
1341
+ component: "ng12Directive",
1342
+ resolve: {
1343
+ data: () => {
1344
+ return "DATA!";
1345
+ },
1346
+ },
1347
+ });
1348
+
1349
+ const $state = svcs.$state;
1350
+
1351
+ $templateCache.put("/comp_tpl.html", "-{{ $ctrl.data }}-");
1352
+ $state.transitionTo("route2cmp");
1353
+ await wait(10);
1354
+
1355
+ const directiveEl = el[0].querySelector("div ui-view ng12-directive");
1356
+ expect(directiveEl).toBeDefined();
1357
+ expect($state.current.name).toBe("route2cmp");
1358
+ expect(el.text()).toBe("-DATA!-");
1359
+ });
1360
+
1361
+ it("should work with angular 1.3+ bindToComponent directives", async () => {
1362
+ $stateProvider.state({
1363
+ name: "route2cmp",
1364
+ url: "/route2cmp",
1365
+ component: "ng13Directive",
1366
+ resolve: {
1367
+ data: () => {
1368
+ return "DATA!";
1369
+ },
1370
+ },
1371
+ });
1372
+
1373
+ const $state = svcs.$state,
1374
+ $httpBackend = svcs.$httpBackend,
1375
+ $q = svcs.$q;
1376
+
1377
+ $templateCache.put("/comp_tpl.html", "-{{ $ctrl.data }}-");
1378
+ $state.transitionTo("route2cmp");
1379
+ await wait(10);
1380
+
1381
+ const directiveEl = el[0].querySelector("div ui-view ng13-directive");
1382
+ expect(directiveEl).toBeDefined();
1383
+ expect($state.current.name).toBe("route2cmp");
1384
+ expect(el.text()).toBe("-DATA!-");
1385
+ });
1386
+
1387
+ it("should call $onInit() once", async () => {
1388
+ $stateProvider.state({
1389
+ name: "route2cmp",
1390
+ url: "/route2cmp",
1391
+ component: "ng13Directive",
1392
+ resolve: {
1393
+ data: () => {
1394
+ return "DATA!";
1395
+ },
1396
+ },
1397
+ });
1398
+
1399
+ const $state = svcs.$state;
1400
+
1401
+ $templateCache.put("/comp_tpl.html", "-{{ $ctrl.data }}-");
1402
+ $state.transitionTo("route2cmp");
1403
+ await wait(10);
1404
+
1405
+ expect(log).toBe("onInit;");
1406
+ });
1407
+
1408
+ it("should work with angular 1.5+ .component()s", async () => {
1409
+ $stateProvider.state({
1410
+ name: "route2cmp",
1411
+ url: "/route2cmp",
1412
+ component: "ngComponent",
1413
+ resolve: {
1414
+ data: () => {
1415
+ return "DATA!";
1416
+ },
1417
+ },
1418
+ });
1419
+
1420
+ const $state = svcs.$state,
1421
+ $httpBackend = svcs.$httpBackend,
1422
+ $q = svcs.$q;
1423
+
1424
+ $templateCache.put("/comp_tpl.html", "-{{ $ctrl.data }}-");
1425
+ $state.transitionTo("route2cmp");
1426
+ await wait(10);
1427
+
1428
+ const directiveEl = el[0].querySelector("div ui-view ng-component");
1429
+ expect(directiveEl).toBeDefined();
1430
+ expect($state.current.name).toBe("route2cmp");
1431
+ expect(el.text()).toBe("-DATA!-");
1432
+ });
1433
+
1434
+ it("should only call $onInit() once", async () => {
1435
+ $stateProvider.state({
1436
+ name: "route2cmp",
1437
+ component: "ngComponent",
1438
+ resolve: {
1439
+ data: () => {
1440
+ return "DATA!";
1441
+ },
1442
+ },
1443
+ });
1444
+
1445
+ const $state = svcs.$state,
1446
+ $httpBackend = svcs.$httpBackend,
1447
+ $q = svcs.$q;
1448
+
1449
+ $templateCache.put("/comp_tpl.html", "-{{ $ctrl.data }}-");
1450
+ $state.transitionTo("route2cmp");
1451
+ await wait(10);
1452
+
1453
+ expect(log).toBe("onInit;");
1454
+ });
1455
+
1456
+ it("should only call $onInit() once with componentProvider", async () => {
1457
+ $stateProvider.state({
1458
+ name: "route2cmp",
1459
+ componentProvider: () => "ngComponent",
1460
+ resolve: {
1461
+ data: () => {
1462
+ return "DATA!";
1463
+ },
1464
+ },
1465
+ });
1466
+
1467
+ const $state = svcs.$state,
1468
+ $httpBackend = svcs.$httpBackend,
1469
+ $q = svcs.$q;
1470
+
1471
+ $templateCache.put("/comp_tpl.html", "-{{ $ctrl.data }}-");
1472
+ $state.transitionTo("route2cmp");
1473
+ await wait(10);
1474
+
1475
+ expect(log).toBe("onInit;");
1476
+ });
1477
+
1478
+ it('should supply resolve data to "<", "=", "@" bindings', async () => {
1479
+ $stateProvider.state({
1480
+ name: "bindingtypes",
1481
+ component: "bindingTypes",
1482
+ resolve: {
1483
+ oneway: () => {
1484
+ return "ONEWAY";
1485
+ },
1486
+ twoway: () => {
1487
+ return "TWOWAY";
1488
+ },
1489
+ attribute: () => {
1490
+ return "ATTRIBUTE";
1491
+ },
1492
+ },
1493
+ bindings: { attr: "attribute" },
1494
+ });
1495
+
1496
+ const $state = svcs.$state,
1497
+ $httpBackend = svcs.$httpBackend,
1498
+ $q = svcs.$q;
1499
+
1500
+ $state.transitionTo("bindingtypes");
1501
+ await wait(10);
1502
+
1503
+ expect(el.text()).toBe("-ONEWAY,TWOWAY,ATTRIBUTE-");
1504
+ });
1505
+
1506
+ it('should supply resolve data to optional "<?", "=?", "@?" bindings', async () => {
1507
+ $stateProvider.state({
1508
+ name: "optionalbindingtypes",
1509
+ component: "optionalBindingTypes",
1510
+ resolve: {
1511
+ oneway: () => {
1512
+ return "ONEWAY";
1513
+ },
1514
+ twoway: () => {
1515
+ return "TWOWAY";
1516
+ },
1517
+ attribute: () => {
1518
+ return "ATTRIBUTE";
1519
+ },
1520
+ },
1521
+ bindings: { attr: "attribute" },
1522
+ });
1523
+
1524
+ const $state = svcs.$state;
1525
+
1526
+ $state.transitionTo("optionalbindingtypes");
1527
+ await wait(10);
1528
+
1529
+ expect(el.text()).toBe("-ONEWAY,TWOWAY,ATTRIBUTE-");
1530
+ });
1531
+
1532
+ // Test for #3099
1533
+ it('should not throw when routing to a component with output "&" binding', async () => {
1534
+ $stateProvider.state({
1535
+ name: "nothrow",
1536
+ component: "eventComponent",
1537
+ });
1538
+
1539
+ const $state = svcs.$state;
1540
+ $state.transitionTo("nothrow");
1541
+ await wait(10);
1542
+
1543
+ expect(el.text()).toBe("eventCmp");
1544
+ });
1545
+
1546
+ // Test for #3276
1547
+ it('should route to a component that is prefixed with "data"', async () => {
1548
+ $stateProvider.state({
1549
+ name: "data",
1550
+ component: "dataComponent",
1551
+ });
1552
+
1553
+ const $state = svcs.$state,
1554
+ $q = svcs.$q;
1555
+ $state.transitionTo("data");
1556
+ await wait(10);
1557
+
1558
+ expect(el.text()).toBe("DataComponent");
1559
+ });
1560
+
1561
+ // Test for #3276
1562
+ it('should bind a resolve that is prefixed with "data"', async () => {
1563
+ $stateProvider.state({
1564
+ name: "data",
1565
+ component: "mydataComponent",
1566
+ resolve: { dataUser: () => "user" },
1567
+ });
1568
+
1569
+ const $state = svcs.$state,
1570
+ $q = svcs.$q;
1571
+ $state.transitionTo("data");
1572
+ await wait(10);
1573
+
1574
+ expect(el.text()).toBe("-user-");
1575
+ });
1576
+
1577
+ // Test for #3239
1578
+ it("should pass any bindings (wired from a parent component template via the ui-view) through to the child", async () => {
1579
+ const $state = svcs.$state,
1580
+ $q = svcs.$q;
1581
+
1582
+ $stateProvider.state({
1583
+ name: "parent",
1584
+ template:
1585
+ '<ui-view oneway="data1w" twoway="data2w" attr="attrval"></ui-view>',
1586
+ controller: function ($scope) {
1587
+ $scope.data1w = "1w";
1588
+ $scope.data2w = "2w";
1589
+ },
1590
+ });
1591
+
1592
+ $stateProvider.state({
1593
+ name: "parent.child",
1594
+ component: "bindingTypes",
1595
+ });
1596
+
1597
+ $state.transitionTo("parent.child");
1598
+ await wait(10);
1599
+ expect(el.text()).toEqual("-1w,2w,attrval-");
1600
+ });
1601
+
1602
+ // Test for #3239
1603
+ it("should prefer ui-view bindings over resolve data", async () => {
1604
+ const $state = svcs.$state,
1605
+ $q = svcs.$q;
1606
+
1607
+ $stateProvider.state({
1608
+ name: "parent",
1609
+ template:
1610
+ '<ui-view oneway="data1w" twoway="data2w" attr="attrval"></ui-view>',
1611
+ resolve: {
1612
+ oneway: () => "asfasfd",
1613
+ twoway: () => "asfasfd",
1614
+ attr: () => "asfasfd",
1615
+ },
1616
+ controller: function ($scope) {
1617
+ $scope.data1w = "1w";
1618
+ $scope.data2w = "2w";
1619
+ },
1620
+ });
1621
+
1622
+ $stateProvider.state({
1623
+ name: "parent.child",
1624
+ component: "bindingTypes",
1625
+ });
1626
+
1627
+ $state.transitionTo("parent.child");
1628
+ await wait(10);
1629
+ expect(el.text()).toEqual("-1w,2w,attrval-");
1630
+ });
1631
+
1632
+ // Test for #3239
1633
+ it("should prefer ui-view bindings over resolve data unless a bindings exists", async () => {
1634
+ const $state = svcs.$state,
1635
+ $q = svcs.$q;
1636
+
1637
+ $stateProvider.state({
1638
+ name: "parent",
1639
+ template:
1640
+ '<ui-view oneway="data1w" twoway="data2w" attr="attrval"></ui-view>',
1641
+ resolve: {
1642
+ oneway: () => "asfasfd",
1643
+ twoway: () => "asfasfd",
1644
+ attr: () => "asfasfd",
1645
+ },
1646
+ controller: function ($scope) {
1647
+ $scope.data1w = "1w";
1648
+ $scope.data2w = "2w";
1649
+ },
1650
+ });
1651
+
1652
+ $stateProvider.state({
1653
+ name: "parent.child",
1654
+ component: "bindingTypes",
1655
+ bindings: { oneway: "oneway" },
1656
+ });
1657
+
1658
+ $state.transitionTo("parent.child");
1659
+ await wait(10);
1660
+ expect(el.text()).toEqual("-asfasfd,2w,attrval-");
1661
+ });
1662
+
1663
+ // Test for #3239
1664
+ it("should pass & bindings (wired from a parent component via the ui-view) through to the child", async () => {
1665
+ const $state = svcs.$state,
1666
+ $q = svcs.$q;
1667
+ $rootScope.log = [];
1668
+
1669
+ $stateProvider.state({
1670
+ name: "parent",
1671
+ component: "parentCallbackComponent",
1672
+ });
1673
+
1674
+ $stateProvider.state({
1675
+ name: "parent.child",
1676
+ component: "childEventComponent",
1677
+ });
1678
+
1679
+ $state.transitionTo("parent.child");
1680
+ await wait(10);
1681
+ expect($rootScope.log).toEqual([]);
1682
+ expect(
1683
+ el
1684
+ .text()
1685
+ .split(/\s+/)
1686
+ .filter((x) => x),
1687
+ ).toEqual(["parentCmp", "childCmp", "Button"]);
1688
+
1689
+ // - Click button
1690
+ // - ng-click handler calls $ctrl.onEvent({ foo: 123, bar: 456 })
1691
+ // - on-event is bound to $ctrl.handleEvent(foo, bar) on parentCallbackComponent
1692
+ // - handleEvent pushes param values to the log
1693
+ el.find("button")[0].click();
1694
+ expect($rootScope.log).toEqual([123, 456]);
1695
+ });
1696
+
1697
+ // Test for #3111
1698
+ it("should bind & bindings to a resolve that returns a function", async () => {
1699
+ const $state = svcs.$state,
1700
+ $q = svcs.$q,
1701
+ log = [];
1702
+
1703
+ $stateProvider.state({
1704
+ name: "resolve",
1705
+ component: "childEventComponent",
1706
+ resolve: {
1707
+ onEvent: () => (foo, bar) => {
1708
+ log.push(foo);
1709
+ log.push(bar);
1710
+ },
1711
+ },
1712
+ });
1713
+
1714
+ $state.transitionTo("resolve");
1715
+ await wait(10);
1716
+ expect(log).toEqual([]);
1717
+ el.find("button")[0].click();
1718
+ expect(log).toEqual([123, 456]);
1719
+ });
1720
+
1721
+ // Test for #3111
1722
+ it("should bind & bindings to a resolve that returns an array-style function", async () => {
1723
+ const $state = svcs.$state,
1724
+ $q = svcs.$q,
1725
+ log = [];
1726
+
1727
+ $stateProvider.state({
1728
+ name: "resolve",
1729
+ component: "childEventComponent",
1730
+ resolve: {
1731
+ onEvent: () => [
1732
+ "foo",
1733
+ "bar",
1734
+ (foo, bar) => {
1735
+ log.push(foo);
1736
+ log.push(bar);
1737
+ },
1738
+ ],
1739
+ },
1740
+ });
1741
+
1742
+ $state.transitionTo("resolve");
1743
+ await wait(10);
1744
+ expect(log).toEqual([]);
1745
+ el.find("button")[0].click();
1746
+ expect(log).toEqual([123, 456]);
1747
+ });
1748
+ });
1749
+
1750
+ describe("+ named views with component: declaration", () => {
1751
+ let stateDef;
1752
+ beforeEach(() => {
1753
+ stateDef = {
1754
+ name: "route2cmp",
1755
+ url: "/route2cmp",
1756
+ views: {
1757
+ header: { component: "header" },
1758
+ content: { component: "ngComponent" },
1759
+ },
1760
+ resolve: {
1761
+ status: () => {
1762
+ return "awesome";
1763
+ },
1764
+ data: () => {
1765
+ return "DATA!";
1766
+ },
1767
+ },
1768
+ };
1769
+
1770
+ el = jqLite(
1771
+ '<div><div ui-view="header"></div><div ui-view="content"</div>',
1772
+ );
1773
+ svcs.$compile(el)(scope);
1774
+ });
1775
+
1776
+ it("should disallow controller/template configuration in the view", () => {
1777
+ expect(() => {
1778
+ $stateProvider.state(stateDef);
1779
+ }).not.toThrow();
1780
+ expect(() => {
1781
+ const state = Object.assign({}, stateDef);
1782
+ state.views.header.template = "fails";
1783
+ $stateProvider.state(state);
1784
+ }).toThrow();
1785
+ });
1786
+
1787
+ it("should render components as views", async () => {
1788
+ $stateProvider.state(stateDef);
1789
+ const $state = svcs.$state;
1790
+
1791
+ $templateCache.put("/comp_tpl.html", "-{{ $ctrl.data }}-");
1792
+ $state.transitionTo("route2cmp");
1793
+ await wait(10);
1794
+
1795
+ const header = el[0].querySelector("[ui-view=header]");
1796
+ const content = el[0].querySelector("[ui-view=content]");
1797
+
1798
+ expect(header.textContent).toBe("#awesome#");
1799
+ expect(content.textContent).toBe("-DATA!-");
1800
+ });
1801
+
1802
+ it("should allow a component view declaration to use a string as a shorthand", async () => {
1803
+ stateDef = {
1804
+ name: "route2cmp",
1805
+ url: "/route2cmp",
1806
+ views: { header: "header", content: "ngComponent" },
1807
+ resolve: {
1808
+ status: () => {
1809
+ return "awesome";
1810
+ },
1811
+ data: () => {
1812
+ return "DATA!";
1813
+ },
1814
+ },
1815
+ };
1816
+ $stateProvider.state(stateDef);
1817
+ const $state = svcs.$state,
1818
+ $httpBackend = svcs.$httpBackend,
1819
+ $q = svcs.$q;
1820
+
1821
+ $templateCache.put("/comp_tpl.html", "-{{ $ctrl.data }}-");
1822
+ $state.transitionTo("route2cmp");
1823
+ await wait(10);
1824
+
1825
+ const header = el[0].querySelector("[ui-view=header]");
1826
+ const content = el[0].querySelector("[ui-view=content]");
1827
+
1828
+ expect(header.textContent).toBe("#awesome#");
1829
+ expect(content.textContent).toBe("-DATA!-");
1830
+ });
1831
+
1832
+ // Test for https://github.com/angular-ui/ui-router/issues/3353
1833
+ it("should allow different states to reuse view declaration", () => {
1834
+ const views = {
1835
+ header: { component: "header" },
1836
+ content: { component: "ngComponent" },
1837
+ };
1838
+
1839
+ const stateDef1 = { name: "def1", url: "/def1", views: views };
1840
+ const stateDef2 = { name: "def2", url: "/def2", views: views };
1841
+
1842
+ $stateProvider.state(stateDef1);
1843
+ $stateProvider.state(stateDef2);
1844
+ });
1845
+ });
1846
+
1847
+ describe("+ bindings: declaration", () => {
1848
+ it("should provide the named component binding with data from the named resolve", async () => {
1849
+ $stateProvider.state({
1850
+ name: "route2cmp",
1851
+ url: "/route2cmp",
1852
+ component: "ng12Directive",
1853
+ bindings: { data: "foo" },
1854
+ resolve: {
1855
+ foo: () => {
1856
+ return "DATA!";
1857
+ },
1858
+ },
1859
+ });
1860
+
1861
+ const $state = svcs.$state;
1862
+
1863
+ $templateCache.put("/comp_tpl.html", "-{{ $ctrl.data }}-");
1864
+ $state.transitionTo("route2cmp");
1865
+ await wait(10);
1866
+
1867
+ const directiveEl = el[0].querySelector("div ui-view ng12-directive");
1868
+ expect(directiveEl).toBeDefined();
1869
+ expect($state.current.name).toBe("route2cmp");
1870
+ expect(el.text()).toBe("-DATA!-");
1871
+ });
1872
+
1873
+ it("should provide default bindings for any component bindings omitted in the state.bindings map", async () => {
1874
+ $stateProvider.state({
1875
+ name: "route2cmp",
1876
+ url: "/route2cmp",
1877
+ component: "ngComponent",
1878
+ bindings: { data: "foo" },
1879
+ resolve: {
1880
+ foo: () => {
1881
+ return "DATA!";
1882
+ },
1883
+ data2: () => {
1884
+ return "DATA2!";
1885
+ },
1886
+ },
1887
+ });
1888
+
1889
+ const $state = svcs.$state,
1890
+ $httpBackend = svcs.$httpBackend,
1891
+ $q = svcs.$q;
1892
+
1893
+ $templateCache.put(
1894
+ "/comp_tpl.html",
1895
+ "-{{ $ctrl.data }}.{{ $ctrl.data2 }}-",
1896
+ );
1897
+ $state.transitionTo("route2cmp");
1898
+ await wait(10);
1899
+
1900
+ const directiveEl = el[0].querySelector("div ui-view ng-component");
1901
+ expect(directiveEl).toBeDefined();
1902
+ expect($state.current.name).toBe("route2cmp");
1903
+ expect(el.text()).toBe("-DATA!.DATA2!-");
1904
+ });
1905
+ });
1906
+
1907
+ describe("componentProvider", () => {
1908
+ it("should work with angular 1.2+ directives", async () => {
1909
+ $stateProvider.state({
1910
+ name: "ng12-dynamic-directive",
1911
+ url: "/ng12dynamicDirective/:type",
1912
+ componentProvider: [
1913
+ "$stateParams",
1914
+ function ($stateParams) {
1915
+ return $stateParams.type;
1916
+ },
1917
+ ],
1918
+ });
1919
+
1920
+ const $state = svcs.$state,
1921
+ $q = svcs.$q;
1922
+
1923
+ $state.transitionTo("ng12-dynamic-directive", {
1924
+ type: "ng12DynamicDirective",
1925
+ });
1926
+ await wait(10);
1927
+
1928
+ const directiveEl = el[0].querySelector(
1929
+ "div ui-view ng12-dynamic-directive",
1930
+ );
1931
+ expect(directiveEl).toBeDefined();
1932
+ expect($state.current.name).toBe("ng12-dynamic-directive");
1933
+ expect(el.text()).toBe("dynamic directive");
1934
+ });
1935
+
1936
+ // TODO Invalid transition
1937
+ xit("should load correct component when using componentProvider", async () => {
1938
+ $stateProvider.state({
1939
+ name: "dynamicComponent",
1940
+ url: "/dynamicComponent/:type",
1941
+ componentProvider: [
1942
+ "$stateParams",
1943
+ function ($stateParams) {
1944
+ return $stateParams.type;
1945
+ },
1946
+ ],
1947
+ });
1948
+
1949
+ const $state = svcs.$state;
1950
+
1951
+ await $state.transitionTo({
1952
+ name: "dynamicComponent",
1953
+ type: "dynamicComponent",
1954
+ });
1955
+ await wait(10);
1956
+
1957
+ const directiveEl = el[0].querySelector("div ui-view dynamic-component");
1958
+ expect(directiveEl).toBeDefined();
1959
+ expect($state.current.name).toBe("dynamicComponent");
1960
+ expect(el.text().trim()).toBe("dynamicComponent");
1961
+ });
1962
+ });
1963
+
1964
+ describe("uiOnParamsChanged()", () => {
1965
+ let param;
1966
+
1967
+ beforeEach(() => {
1968
+ param = null;
1969
+
1970
+ $stateProvider.state({
1971
+ name: "dynamic",
1972
+ url: "/dynamic/:param",
1973
+ component: "dynamicComponent",
1974
+ params: { param: { dynamic: true } },
1975
+ });
1976
+
1977
+ $stateProvider.state({
1978
+ name: "dynamic2",
1979
+ url: "/dynamic2/:param",
1980
+ componentProvider: () => "dynamicComponent",
1981
+ params: { param: { dynamic: true } },
1982
+ });
1983
+ });
1984
+
1985
+ it("should not be called on the initial transition", async () => {
1986
+ const $state = svcs.$state;
1987
+ $state.go("dynamic", { param: "abc" });
1988
+ await wait(10);
1989
+ expect(el.text().trim()).toBe("dynamicComponent");
1990
+ });
1991
+
1992
+ it("should be called when dynamic parameters change", async () => {
1993
+ const $state = svcs.$state;
1994
+ $state.go("dynamic", { param: "abc" });
1995
+ await wait(10);
1996
+ $state.go("dynamic", { param: "def" });
1997
+ await wait(10);
1998
+
1999
+ expect(el.text().trim()).toBe("dynamicComponent def");
2000
+ });
2001
+
2002
+ it("should work with componentProvider", async () => {
2003
+ const $state = svcs.$state,
2004
+ $q = svcs.$q;
2005
+ $state.go("dynamic2", { param: "abc" });
2006
+ await wait(10);
2007
+ $state.go("dynamic2", { param: "def" });
2008
+ await wait(10);
2009
+
2010
+ expect(el.text().trim()).toBe("dynamicComponent def");
2011
+ });
2012
+ });
2013
+ });