@angular-wave/angular.ts 0.0.11 → 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.
Files changed (88) hide show
  1. package/dist/angular-ts.esm.js +1 -1
  2. package/dist/angular-ts.umd.js +1 -1
  3. package/package.json +4 -1
  4. package/src/exts/messages.md +30 -30
  5. package/src/public.js +2 -0
  6. package/src/router/adapter/directives/stateDirectives.js +695 -0
  7. package/src/router/adapter/directives/viewDirective.js +514 -0
  8. package/src/router/adapter/injectables.js +314 -0
  9. package/src/router/adapter/interface.js +1 -0
  10. package/src/router/adapter/locationServices.js +86 -0
  11. package/src/router/adapter/services.js +132 -0
  12. package/src/router/adapter/stateFilters.js +43 -0
  13. package/src/router/adapter/stateProvider.js +137 -0
  14. package/src/router/adapter/statebuilders/onEnterExitRetain.js +30 -0
  15. package/src/router/adapter/statebuilders/views.js +146 -0
  16. package/src/router/adapter/templateFactory.js +218 -0
  17. package/src/router/adapter/urlRouterProvider.js +196 -0
  18. package/src/router/adapter/viewScroll.js +31 -0
  19. package/src/router/core/common/common.js +506 -0
  20. package/src/router/core/common/coreservices.js +15 -0
  21. package/src/router/core/common/glob.js +75 -0
  22. package/src/router/core/common/hof.js +194 -0
  23. package/src/router/core/common/predicates.js +44 -0
  24. package/src/router/core/common/queue.js +41 -0
  25. package/src/router/core/common/safeConsole.js +38 -0
  26. package/src/router/core/common/strings.js +141 -0
  27. package/src/router/core/common/trace.js +232 -0
  28. package/src/router/core/globals.js +29 -0
  29. package/src/router/core/hooks/coreResolvables.js +33 -0
  30. package/src/router/core/hooks/ignoredTransition.js +25 -0
  31. package/src/router/core/hooks/invalidTransition.js +14 -0
  32. package/src/router/core/hooks/lazyLoad.js +102 -0
  33. package/src/router/core/hooks/onEnterExitRetain.js +55 -0
  34. package/src/router/core/hooks/redirectTo.js +36 -0
  35. package/src/router/core/hooks/resolve.js +57 -0
  36. package/src/router/core/hooks/updateGlobals.js +30 -0
  37. package/src/router/core/hooks/url.js +25 -0
  38. package/src/router/core/hooks/views.js +39 -0
  39. package/src/router/core/interface.js +3 -0
  40. package/src/router/core/params/README.md +8 -0
  41. package/src/router/core/params/param.js +232 -0
  42. package/src/router/core/params/paramType.js +139 -0
  43. package/src/router/core/params/paramTypes.js +163 -0
  44. package/src/router/core/params/stateParams.js +35 -0
  45. package/src/router/core/path/pathNode.js +77 -0
  46. package/src/router/core/path/pathUtils.js +200 -0
  47. package/src/router/core/resolve/interface.js +10 -0
  48. package/src/router/core/resolve/resolvable.js +124 -0
  49. package/src/router/core/resolve/resolveContext.js +211 -0
  50. package/src/router/core/router.js +201 -0
  51. package/src/router/core/state/README.md +21 -0
  52. package/src/router/core/state/stateBuilder.js +333 -0
  53. package/src/router/core/state/stateMatcher.js +66 -0
  54. package/src/router/core/state/stateObject.js +116 -0
  55. package/src/router/core/state/stateQueueManager.js +89 -0
  56. package/src/router/core/state/stateRegistry.js +175 -0
  57. package/src/router/core/state/stateService.js +592 -0
  58. package/src/router/core/state/targetState.js +159 -0
  59. package/src/router/core/transition/hookBuilder.js +127 -0
  60. package/src/router/core/transition/hookRegistry.js +182 -0
  61. package/src/router/core/transition/interface.js +14 -0
  62. package/src/router/core/transition/rejectFactory.js +122 -0
  63. package/src/router/core/transition/transition.js +739 -0
  64. package/src/router/core/transition/transitionEventType.js +27 -0
  65. package/src/router/core/transition/transitionHook.js +199 -0
  66. package/src/router/core/transition/transitionService.js +311 -0
  67. package/src/router/core/url/interface.js +1 -0
  68. package/src/router/core/url/urlConfig.js +165 -0
  69. package/src/router/core/url/urlMatcher.js +548 -0
  70. package/src/router/core/url/urlMatcherFactory.js +123 -0
  71. package/src/router/core/url/urlRouter.js +115 -0
  72. package/src/router/core/url/urlRule.js +202 -0
  73. package/src/router/core/url/urlRules.js +348 -0
  74. package/src/router/core/url/urlService.js +268 -0
  75. package/src/router/core/vanilla/baseLocationService.js +31 -0
  76. package/src/router/core/vanilla/browserLocationConfig.js +42 -0
  77. package/src/router/core/vanilla/hashLocationService.js +19 -0
  78. package/src/router/core/vanilla/injector.js +98 -0
  79. package/src/router/core/vanilla/interface.js +1 -0
  80. package/src/router/core/vanilla/memoryLocationConfig.js +20 -0
  81. package/src/router/core/vanilla/memoryLocationService.js +13 -0
  82. package/src/router/core/vanilla/plugins.js +35 -0
  83. package/src/router/core/vanilla/pushStateLocationService.js +69 -0
  84. package/src/router/core/vanilla/q.js +54 -0
  85. package/src/router/core/vanilla/utils.js +63 -0
  86. package/src/router/core/view/interface.js +1 -0
  87. package/src/router/core/view/view.js +312 -0
  88. package/src/router/router.js +52 -0
@@ -0,0 +1,137 @@
1
+ /** @publicapi @module ng1 */ /** */
2
+
3
+ import { val } from "../core/common/hof";
4
+ import { createProxyFunctions } from "../core/common/common";
5
+ import { isObject } from "../../core/utils";
6
+ /**
7
+ * The Angular 1 `StateProvider`
8
+ *
9
+ * The `$stateProvider` works similar to Angular's v1 router, but it focuses purely
10
+ * on state.
11
+ *
12
+ * A state corresponds to a "place" in the application in terms of the overall UI and
13
+ * navigation. A state describes (via the controller / template / view properties) what
14
+ * the UI looks like and does at that place.
15
+ *
16
+ * States often have things in common, and the primary way of factoring out these
17
+ * commonalities in this model is via the state hierarchy, i.e. parent/child states aka
18
+ * nested states.
19
+ *
20
+ * The `$stateProvider` provides interfaces to declare these states for your app.
21
+ */
22
+ export class StateProvider {
23
+ constructor(stateRegistry, stateService) {
24
+ this.stateRegistry = stateRegistry;
25
+ this.stateService = stateService;
26
+ createProxyFunctions(val(StateProvider.prototype), this, val(this));
27
+ }
28
+ /**
29
+ * Decorates states when they are registered
30
+ *
31
+ * Allows you to extend (carefully) or override (at your own peril) the
32
+ * `stateBuilder` object used internally by [[StateRegistry]].
33
+ * This can be used to add custom functionality to ui-router,
34
+ * for example inferring templateUrl based on the state name.
35
+ *
36
+ * When passing only a name, it returns the current (original or decorated) builder
37
+ * function that matches `name`.
38
+ *
39
+ * The builder functions that can be decorated are listed below. Though not all
40
+ * necessarily have a good use case for decoration, that is up to you to decide.
41
+ *
42
+ * In addition, users can attach custom decorators, which will generate new
43
+ * properties within the state's internal definition. There is currently no clear
44
+ * use-case for this beyond accessing internal states (i.e. $state.$current),
45
+ * however, expect this to become increasingly relevant as we introduce additional
46
+ * meta-programming features.
47
+ *
48
+ * **Warning**: Decorators should not be interdependent because the order of
49
+ * execution of the builder functions in non-deterministic. Builder functions
50
+ * should only be dependent on the state definition object and super function.
51
+ *
52
+ *
53
+ * Existing builder functions and current return values:
54
+ *
55
+ * - **parent** `{object}` - returns the parent state object.
56
+ * - **data** `{object}` - returns state data, including any inherited data that is not
57
+ * overridden by own values (if any).
58
+ * - **url** `{object}` - returns a {@link ui.router.util.type:UrlMatcher UrlMatcher}
59
+ * or `null`.
60
+ * - **navigable** `{object}` - returns closest ancestor state that has a URL (aka is
61
+ * navigable).
62
+ * - **params** `{object}` - returns an array of state params that are ensured to
63
+ * be a super-set of parent's params.
64
+ * - **views** `{object}` - returns a views object where each key is an absolute view
65
+ * name (i.e. "viewName@stateName") and each value is the config object
66
+ * (template, controller) for the view. Even when you don't use the views object
67
+ * explicitly on a state config, one is still created for you internally.
68
+ * So by decorating this builder function you have access to decorating template
69
+ * and controller properties.
70
+ * - **ownParams** `{object}` - returns an array of params that belong to the state,
71
+ * not including any params defined by ancestor states.
72
+ * - **path** `{string}` - returns the full path from the root down to this state.
73
+ * Needed for state activation.
74
+ * - **includes** `{object}` - returns an object that includes every state that
75
+ * would pass a `$state.includes()` test.
76
+ *
77
+ * #### Example:
78
+ * Override the internal 'views' builder with a function that takes the state
79
+ * definition, and a reference to the internal function being overridden:
80
+ * ```js
81
+ * $stateProvider.decorator('views', function (state, parent) {
82
+ * let result = {},
83
+ * views = parent(state);
84
+ *
85
+ * angular.forEach(views, function (config, name) {
86
+ * let autoName = (state.name + '.' + name).replace('.', '/');
87
+ * config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html';
88
+ * result[name] = config;
89
+ * });
90
+ * return result;
91
+ * });
92
+ *
93
+ * $stateProvider.state('home', {
94
+ * views: {
95
+ * 'contact.list': { controller: 'ListController' },
96
+ * 'contact.item': { controller: 'ItemController' }
97
+ * }
98
+ * });
99
+ * ```
100
+ *
101
+ *
102
+ * ```js
103
+ * // Auto-populates list and item views with /partials/home/contact/list.html,
104
+ * // and /partials/home/contact/item.html, respectively.
105
+ * $state.go('home');
106
+ * ```
107
+ *
108
+ * @param {string} name The name of the builder function to decorate.
109
+ * @param {object} func A function that is responsible for decorating the original
110
+ * builder function. The function receives two parameters:
111
+ *
112
+ * - `{object}` - state - The state config object.
113
+ * - `{object}` - super - The original builder function.
114
+ *
115
+ * @return {object} $stateProvider - $stateProvider instance
116
+ */
117
+ decorator(name, func) {
118
+ return this.stateRegistry.decorator(name, func) || this;
119
+ }
120
+ state(name, definition) {
121
+ if (isObject(name)) {
122
+ definition = name;
123
+ } else {
124
+ definition.name = name;
125
+ }
126
+ this.stateRegistry.register(definition);
127
+ return this;
128
+ }
129
+ /**
130
+ * Registers an invalid state handler
131
+ *
132
+ * This is a passthrough to [[StateService.onInvalid]] for ng1.
133
+ */
134
+ onInvalid(callback) {
135
+ return this.stateService.onInvalid(callback);
136
+ }
137
+ }
@@ -0,0 +1,30 @@
1
+ /** @publicapi @module ng1 */ /** */
2
+ import { getLocals } from "../services";
3
+ import { services } from "../../core/common/coreservices";
4
+ import { extend } from "../../../core/utils";
5
+ import { ResolveContext } from "../../core/resolve/resolveContext";
6
+
7
+ /**
8
+ * This is a [[StateBuilder.builder]] function for angular1 `onEnter`, `onExit`,
9
+ * `onRetain` callback hooks on a [[Ng1StateDeclaration]].
10
+ *
11
+ * When the [[StateBuilder]] builds a [[StateObject]] object from a raw [[StateDeclaration]], this builder
12
+ * ensures that those hooks are injectable for @uirouter/angularjs (ng1).
13
+ *
14
+ * @internalapi
15
+ */
16
+ export const getStateHookBuilder = (hookName) =>
17
+ function stateHookBuilder(stateObject) {
18
+ const hook = stateObject[hookName];
19
+ const pathname = hookName === "onExit" ? "from" : "to";
20
+ function decoratedNg1Hook(trans, state) {
21
+ const resolveContext = new ResolveContext(trans.treeChanges(pathname));
22
+ const subContext = resolveContext.subContext(state.$$state());
23
+ const locals = extend(getLocals(subContext), {
24
+ $state$: state,
25
+ $transition$: trans,
26
+ });
27
+ return services.$injector.invoke(hook, this, locals);
28
+ }
29
+ return hook ? decoratedNg1Hook : undefined;
30
+ };
@@ -0,0 +1,146 @@
1
+ /** @publicapi @module ng1 */ /** */
2
+ import { pick, forEach, tail, extend } from "../../core/common/common";
3
+ import { isArray, isDefined, isString } from "../../../core/utils";
4
+ import { isInjectable } from "../../core/common/predicates";
5
+ import { services } from "../../core/common/coreservices";
6
+ import { trace } from "../../core/common/trace";
7
+ import { ViewService } from "../../core/view/view";
8
+ import { ResolveContext } from "../../core/resolve/resolveContext";
9
+ import { Resolvable } from "../../core/resolve/resolvable";
10
+
11
+ /** @internalapi */
12
+ export function getNg1ViewConfigFactory() {
13
+ let templateFactory = null;
14
+ return (path, view) => {
15
+ templateFactory =
16
+ templateFactory || services.$injector.get("$templateFactory");
17
+ return [new Ng1ViewConfig(path, view, templateFactory)];
18
+ };
19
+ }
20
+ /** @internalapi */
21
+ const hasAnyKey = (keys, obj) =>
22
+ keys.reduce((acc, key) => acc || isDefined(obj[key]), false);
23
+ /**
24
+ * This is a [[StateBuilder.builder]] function for angular1 `views`.
25
+ *
26
+ * When the [[StateBuilder]] builds a [[StateObject]] object from a raw [[StateDeclaration]], this builder
27
+ * handles the `views` property with logic specific to @uirouter/angularjs (ng1).
28
+ *
29
+ * If no `views: {}` property exists on the [[StateDeclaration]], then it creates the `views` object
30
+ * and applies the state-level configuration to a view named `$default`.
31
+ *
32
+ * @internalapi
33
+ */
34
+ export function ng1ViewsBuilder(state) {
35
+ // Do not process root state
36
+ if (!state.parent) return {};
37
+ const tplKeys = [
38
+ "templateProvider",
39
+ "templateUrl",
40
+ "template",
41
+ "notify",
42
+ "async",
43
+ ],
44
+ ctrlKeys = [
45
+ "controller",
46
+ "controllerProvider",
47
+ "controllerAs",
48
+ "resolveAs",
49
+ ],
50
+ compKeys = ["component", "bindings", "componentProvider"],
51
+ nonCompKeys = tplKeys.concat(ctrlKeys),
52
+ allViewKeys = compKeys.concat(nonCompKeys);
53
+ // Do not allow a state to have both state-level props and also a `views: {}` property.
54
+ // A state without a `views: {}` property can declare properties for the `$default` view as properties of the state.
55
+ // However, the `$default` approach should not be mixed with a separate `views: ` block.
56
+ if (isDefined(state.views) && hasAnyKey(allViewKeys, state)) {
57
+ throw new Error(
58
+ `State '${state.name}' has a 'views' object. ` +
59
+ `It cannot also have "view properties" at the state level. ` +
60
+ `Move the following properties into a view (in the 'views' object): ` +
61
+ ` ${allViewKeys.filter((key) => isDefined(state[key])).join(", ")}`,
62
+ );
63
+ }
64
+ const views = {},
65
+ viewsObject = state.views || { $default: pick(state, allViewKeys) };
66
+ forEach(viewsObject, function (config, name) {
67
+ // Account for views: { "": { template... } }
68
+ name = name || "$default";
69
+ // Account for views: { header: "headerComponent" }
70
+ if (isString(config)) config = { component: config };
71
+ // Make a shallow copy of the config object
72
+ config = extend({}, config);
73
+ // Do not allow a view to mix props for component-style view with props for template/controller-style view
74
+ if (hasAnyKey(compKeys, config) && hasAnyKey(nonCompKeys, config)) {
75
+ throw new Error(
76
+ `Cannot combine: ${compKeys.join("|")} with: ${nonCompKeys.join("|")} in stateview: '${name}@${state.name}'`,
77
+ );
78
+ }
79
+ config.resolveAs = config.resolveAs || "$resolve";
80
+ config.$type = "ng1";
81
+ config.$context = state;
82
+ config.$name = name;
83
+ const normalized = ViewService.normalizeUIViewTarget(
84
+ config.$context,
85
+ config.$name,
86
+ );
87
+ config.$uiViewName = normalized.uiViewName;
88
+ config.$uiViewContextAnchor = normalized.uiViewContextAnchor;
89
+ views[name] = config;
90
+ });
91
+ return views;
92
+ }
93
+ /** @hidden */
94
+ let id = 0;
95
+ /** @internalapi */
96
+ export class Ng1ViewConfig {
97
+ constructor(path, viewDecl, factory) {
98
+ this.path = path;
99
+ this.viewDecl = viewDecl;
100
+ this.factory = factory;
101
+ this.$id = id++;
102
+ this.loaded = false;
103
+ this.getTemplate = (uiView, context) =>
104
+ this.component
105
+ ? this.factory.makeComponentTemplate(
106
+ uiView,
107
+ context,
108
+ this.component,
109
+ this.viewDecl.bindings,
110
+ )
111
+ : this.template;
112
+ }
113
+ load() {
114
+ const $q = services.$q;
115
+ const context = new ResolveContext(this.path);
116
+ const params = this.path.reduce(
117
+ (acc, node) => extend(acc, node.paramValues),
118
+ {},
119
+ );
120
+ const promises = {
121
+ template: $q.when(
122
+ this.factory.fromConfig(this.viewDecl, params, context),
123
+ ),
124
+ controller: $q.when(this.getController(context)),
125
+ };
126
+ return $q.all(promises).then((results) => {
127
+ trace.traceViewServiceEvent("Loaded", this);
128
+ this.controller = results.controller;
129
+ extend(this, results.template); // Either { template: "tpl" } or { component: "cmpName" }
130
+ return this;
131
+ });
132
+ }
133
+ /**
134
+ * Gets the controller for a view configuration.
135
+ *
136
+ * @returns {Function|Promise.<Function>} Returns a controller, or a promise that resolves to a controller.
137
+ */
138
+ getController(context) {
139
+ const provider = this.viewDecl.controllerProvider;
140
+ if (!isInjectable(provider)) return this.viewDecl.controller;
141
+ const deps = services.$injector.annotate(provider);
142
+ const providerFn = isArray(provider) ? tail(provider) : provider;
143
+ const resolvable = new Resolvable("", providerFn, deps);
144
+ return resolvable.get(context);
145
+ }
146
+ }
@@ -0,0 +1,218 @@
1
+ /** @publicapi @module view */ /** */
2
+ import {
3
+ isArray,
4
+ isDefined,
5
+ isFunction,
6
+ isObject,
7
+ } from "../core/common/predicates";
8
+ import { services } from "../core/common/coreservices";
9
+ import { tail, unnestR } from "../core/common/common";
10
+ import { Resolvable } from "../core/resolve/resolvable";
11
+ import { kebobString } from "../core/common/strings";
12
+
13
+ /**
14
+ * Service which manages loading of templates from a ViewConfig.
15
+ */
16
+ export class TemplateFactory {
17
+ constructor() {
18
+ /** @hidden */ this._useHttp = true; // TODO investigate
19
+ /** @hidden */ this.$get = [
20
+ "$http",
21
+ "$templateCache",
22
+ "$injector",
23
+ ($http, $templateCache, $injector) => {
24
+ this.$templateRequest =
25
+ $injector.has &&
26
+ $injector.has("$templateRequest") &&
27
+ $injector.get("$templateRequest");
28
+ this.$http = $http;
29
+ this.$templateCache = $templateCache;
30
+ return this;
31
+ },
32
+ ];
33
+ }
34
+ /** @hidden */
35
+ useHttpService(value) {
36
+ this._useHttp = value;
37
+ }
38
+ /**
39
+ * Creates a template from a configuration object.
40
+ *
41
+ * @param config Configuration object for which to load a template.
42
+ * The following properties are search in the specified order, and the first one
43
+ * that is defined is used to create the template:
44
+ *
45
+ * @param params Parameters to pass to the template function.
46
+ * @param context The resolve context associated with the template's view
47
+ *
48
+ * @return {string|object} The template html as a string, or a promise for
49
+ * that string,or `null` if no template is configured.
50
+ */
51
+ fromConfig(config, params, context) {
52
+ const defaultTemplate = "<ui-view></ui-view>";
53
+ const asTemplate = (result) =>
54
+ services.$q.when(result).then((str) => ({ template: str }));
55
+ const asComponent = (result) =>
56
+ services.$q.when(result).then((str) => ({ component: str }));
57
+ return isDefined(config.template)
58
+ ? asTemplate(this.fromString(config.template, params))
59
+ : isDefined(config.templateUrl)
60
+ ? asTemplate(this.fromUrl(config.templateUrl, params))
61
+ : isDefined(config.templateProvider)
62
+ ? asTemplate(
63
+ this.fromProvider(config.templateProvider, params, context),
64
+ )
65
+ : isDefined(config.component)
66
+ ? asComponent(config.component)
67
+ : isDefined(config.componentProvider)
68
+ ? asComponent(
69
+ this.fromComponentProvider(
70
+ config.componentProvider,
71
+ params,
72
+ context,
73
+ ),
74
+ )
75
+ : asTemplate(defaultTemplate);
76
+ }
77
+ /**
78
+ * Creates a template from a string or a function returning a string.
79
+ *
80
+ * @param template html template as a string or function that returns an html template as a string.
81
+ * @param params Parameters to pass to the template function.
82
+ *
83
+ * @return {string|object} The template html as a string, or a promise for that
84
+ * string.
85
+ */
86
+ fromString(template, params) {
87
+ return isFunction(template) ? template(params) : template;
88
+ }
89
+ /**
90
+ * Loads a template from the a URL via `$http` and `$templateCache`.
91
+ *
92
+ * @param {string|Function} url url of the template to load, or a function
93
+ * that returns a url.
94
+ * @param {Object} params Parameters to pass to the url function.
95
+ * @return {string|Promise.<string>} The template html as a string, or a promise
96
+ * for that string.
97
+ */
98
+ fromUrl(url, params) {
99
+ if (isFunction(url)) url = url(params);
100
+ if (url == null) return null;
101
+ if (this._useHttp) {
102
+ return this.$http
103
+ .get(url, {
104
+ cache: this.$templateCache,
105
+ headers: { Accept: "text/html" },
106
+ })
107
+ .then(function (response) {
108
+ return response.data;
109
+ });
110
+ }
111
+ return this.$templateRequest(url);
112
+ }
113
+ /**
114
+ * Creates a template by invoking an injectable provider function.
115
+ *
116
+ * @param provider Function to invoke via `locals`
117
+ * @param {Function} injectFn a function used to invoke the template provider
118
+ * @return {string|Promise.<string>} The template html as a string, or a promise
119
+ * for that string.
120
+ */
121
+ fromProvider(provider, params, context) {
122
+ const deps = services.$injector.annotate(provider);
123
+ const providerFn = isArray(provider) ? tail(provider) : provider;
124
+ const resolvable = new Resolvable("", providerFn, deps);
125
+ return resolvable.get(context);
126
+ }
127
+ /**
128
+ * Creates a component's template by invoking an injectable provider function.
129
+ *
130
+ * @param provider Function to invoke via `locals`
131
+ * @param {Function} injectFn a function used to invoke the template provider
132
+ * @return {string} The template html as a string: "<component-name input1='::$resolve.foo'></component-name>".
133
+ */
134
+ fromComponentProvider(provider, params, context) {
135
+ const deps = services.$injector.annotate(provider);
136
+ const providerFn = isArray(provider) ? tail(provider) : provider;
137
+ const resolvable = new Resolvable("", providerFn, deps);
138
+ return resolvable.get(context);
139
+ }
140
+ /**
141
+ * Creates a template from a component's name
142
+ *
143
+ * This implements route-to-component.
144
+ * It works by retrieving the component (directive) metadata from the injector.
145
+ * It analyses the component's bindings, then constructs a template that instantiates the component.
146
+ * The template wires input and output bindings to resolves or from the parent component.
147
+ *
148
+ * @param uiView {object} The parent ui-view (for binding outputs to callbacks)
149
+ * @param context The ResolveContext (for binding outputs to callbacks returned from resolves)
150
+ * @param component {string} Component's name in camel case.
151
+ * @param bindings An object defining the component's bindings: {foo: '<'}
152
+ * @return {string} The template as a string: "<component-name input1='::$resolve.foo'></component-name>".
153
+ */
154
+ makeComponentTemplate(uiView, context, component, bindings) {
155
+ bindings = bindings || {};
156
+ // Bind once prefix
157
+ const prefix = angular.version.minor >= 3 ? "::" : "";
158
+ // Convert to kebob name. Add x- prefix if the string starts with `x-` or `data-`
159
+ const kebob = (camelCase) => {
160
+ const kebobed = kebobString(camelCase);
161
+ return /^(x|data)-/.exec(kebobed) ? `x-${kebobed}` : kebobed;
162
+ };
163
+ const attributeTpl = (input) => {
164
+ const { name, type } = input;
165
+ const attrName = kebob(name);
166
+ // If the ui-view has an attribute which matches a binding on the routed component
167
+ // then pass that attribute through to the routed component template.
168
+ // Prefer ui-view wired mappings to resolve data, unless the resolve was explicitly bound using `bindings:`
169
+ if (uiView.attr(attrName) && !bindings[name])
170
+ return `${attrName}='${uiView.attr(attrName)}'`;
171
+ const resolveName = bindings[name] || name;
172
+ // Pre-evaluate the expression for "@" bindings by enclosing in {{ }}
173
+ // some-attr="{{ ::$resolve.someResolveName }}"
174
+ if (type === "@")
175
+ return `${attrName}='{{${prefix}$resolve.${resolveName}}}'`;
176
+ // Wire "&" callbacks to resolves that return a callback function
177
+ // Get the result of the resolve (should be a function) and annotate it to get its arguments.
178
+ // some-attr="$resolve.someResolveResultName(foo, bar)"
179
+ if (type === "&") {
180
+ const res = context.getResolvable(resolveName);
181
+ const fn = res && res.data;
182
+ const args = (fn && services.$injector.annotate(fn)) || [];
183
+ // account for array style injection, i.e., ['foo', function(foo) {}]
184
+ const arrayIdxStr = isArray(fn) ? `[${fn.length - 1}]` : "";
185
+ return `${attrName}='$resolve.${resolveName}${arrayIdxStr}(${args.join(",")})'`;
186
+ }
187
+ // some-attr="::$resolve.someResolveName"
188
+ return `${attrName}='${prefix}$resolve.${resolveName}'`;
189
+ };
190
+ const attrs = getComponentBindings(component).map(attributeTpl).join(" ");
191
+ const kebobName = kebob(component);
192
+ return `<${kebobName} ${attrs}></${kebobName}>`;
193
+ }
194
+ }
195
+ // Gets all the directive(s)' inputs ('@', '=', and '<') and outputs ('&')
196
+ function getComponentBindings(name) {
197
+ const cmpDefs = services.$injector.get(name + "Directive"); // could be multiple
198
+ if (!cmpDefs || !cmpDefs.length)
199
+ throw new Error(`Unable to find component named '${name}'`);
200
+ return cmpDefs.map(getBindings).reduce(unnestR, []);
201
+ }
202
+ // Given a directive definition, find its object input attributes
203
+ // Use different properties, depending on the type of directive (component, bindToController, normal)
204
+ const getBindings = (def) => {
205
+ if (isObject(def.bindToController))
206
+ return scopeBindings(def.bindToController);
207
+ return scopeBindings(def.scope);
208
+ };
209
+ // for ng 1.2 style, process the scope: { input: "=foo" }
210
+ // for ng 1.3 through ng 1.5, process the component's bindToController: { input: "=foo" } object
211
+ const scopeBindings = (bindingsObj) =>
212
+ Object.keys(bindingsObj || {})
213
+ // [ 'input', [ '=foo', '=', 'foo' ] ]
214
+ .map((key) => [key, /^([=<@&])[?]?(.*)/.exec(bindingsObj[key])])
215
+ // skip malformed values
216
+ .filter((tuple) => isDefined(tuple) && isArray(tuple[1]))
217
+ // { name: ('foo' || 'input'), type: '=' }
218
+ .map((tuple) => ({ name: tuple[1][2] || tuple[0], type: tuple[1][1] }));