Skip to content

Commit 54e4ac9

Browse files
Merge pull request #2675 from angular-ui/ng-upgrade
Add core code necessary to support ng-upgrade via ui-router-ng1-to-ng2
2 parents 3fc2012 + 3f711a1 commit 54e4ac9

File tree

7 files changed

+68
-30
lines changed

7 files changed

+68
-30
lines changed

src/core.ts

+1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ export * from "./state/module";
88
export * from "./transition/module";
99
export * from "./url/module";
1010
export * from "./view/module";
11+
export * from "./globals";
1112

1213
export { UIRouter } from "./router";

src/ng1/viewDirective.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/** @module ng1_directives */ /** for typedoc */
22
"use strict";
33
import {extend, map, unnestR, filter} from "../common/common";
4-
import {isDefined, isFunction} from "../common/predicates";
4+
import {isDefined, isFunction, isString} from "../common/predicates";
55
import {trace} from "../common/trace";
66
import {ActiveUIView} from "../view/interface";
77
import {Ng1ViewConfig} from "./viewsBuilder";
@@ -357,7 +357,7 @@ function $ViewDirectiveFill ( $compile, $controller, $transitions, $view,
357357
}
358358

359359
// Wait for the component to appear in the DOM
360-
if (cfg.viewDecl.component) {
360+
if (isString(cfg.viewDecl.component)) {
361361
let cmp = cfg.viewDecl.component;
362362
let kebobName = kebobString(cmp);
363363
let getComponentController = () => {

src/ng1/viewsBuilder.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ export function ng1ViewsBuilder(state: State) {
4949
config.templateProvider = ['$injector', function($injector) {
5050
const resolveFor = key => config.bindings && config.bindings[key] || key;
5151
const prefix = angular.version.minor >= 3 ? "::" : "";
52-
let attrs = getComponentInputs($injector, config.component).map(key => `${kebobString(key)}='${prefix}$resolve.${resolveFor(key)}'`).join(" ");
52+
let attrs = getComponentInputs($injector, config.component)
53+
.map(key => `${kebobString(key)}='${prefix}$resolve.${resolveFor(key)}'`).join(" ");
5354
let kebobName = kebobString(config.component);
5455
return `<${kebobName} ${attrs}></${kebobName}>`;
5556
}];

src/ng2/componentUtil.ts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {InputMetadata, ComponentMetadata} from "angular2/core";
2+
3+
export const ng2ComponentInputs = (ng2CompClass) => {
4+
/** Get "@Input('foo') _foo" inputs */
5+
let props = Reflect['getMetadata']('propMetadata', ng2CompClass);
6+
let _props = Object.keys(props || {})
7+
// -> { string, anno[] } tuples
8+
.map(key => ({ key, annoArr: props[key] }))
9+
// -> to { string, anno } tuples
10+
.reduce((acc, tuple) => acc.concat(tuple.annoArr.map(anno => ({ key: tuple.key, anno }))), [])
11+
// Only Inputs
12+
.filter(tuple => tuple.anno instanceof InputMetadata)
13+
// If they have a bindingPropertyName, i.e. "@Input('foo') _foo", then foo, else _foo
14+
.map(tuple => ({ resolve: tuple.anno.bindingPropertyName || tuple.key, prop: tuple.key }));
15+
16+
/** Get "inputs: ['foo']" inputs */
17+
let inputs = Reflect['getMetadata']('annotations', ng2CompClass)
18+
// Find the ComponentMetadata class annotation
19+
.filter(x => x instanceof ComponentMetadata && !!x.inputs)
20+
// Get the .inputs string array
21+
.map(x => x.inputs)
22+
// Flatten
23+
.reduce((acc, arr) => acc.concat(arr), [])
24+
.map(input => ({ resolve: input, prop: input }));
25+
26+
return _props.concat(inputs);
27+
};

src/ng2/providers.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,7 @@ export const UIROUTER_PROVIDERS: Provider[] = [
110110

111111
provide(UIRouterGlobals, { useFactory: (r: UIRouter) => { return r.globals; }, deps: [UIRouter]}),
112112

113-
provide(UiView.INJECT.context, { useFactory: (r: StateRegistry) => { return r.root(); }, deps: [StateRegistry]} ),
114-
115-
provide(UiView.INJECT.fqn, { useValue: null })
113+
provide(UiView.PARENT_INJECT, { useFactory: (r: StateRegistry) => { return { fqn: null, context: r.root() } }, deps: [StateRegistry]} )
116114

117115
];
118116

src/ng2/uiSref.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import {Directive, Inject, Input} from "angular2/core";
44
import {Optional} from "angular2/core";
55
import {ElementRef} from "angular2/core";
66
import {Renderer} from "angular2/core";
7-
import {UiView} from "./uiView";
8-
import {ViewContext} from "../view/interface";
7+
import {UiView, ParentUiViewInject} from "./uiView";
98
import {extend} from "../common/common";
109

1110
/** @hidden */
@@ -69,7 +68,7 @@ export class UiSref {
6968

7069
constructor(
7170
private _router: UIRouter,
72-
@Inject(UiView.INJECT.context) public context: ViewContext,
71+
@Inject(UiView.PARENT_INJECT) public parent: ParentUiViewInject,
7372
@Optional() private _anchorUiSref: AnchorUiSref
7473
) { }
7574

@@ -88,7 +87,7 @@ export class UiSref {
8887
}
8988

9089
getOptions() {
91-
let defOpts = { relative: this.context.name, inherit: true };
90+
let defOpts = { relative: this.parent && this.parent.context && this.parent.context.name, inherit: true };
9291
return extend(defOpts, this.options || {});
9392
}
9493

src/ng2/uiView.ts

+32-20
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {trace} from "../common/trace";
1111
import {Inject} from "angular2/core";
1212
import {ViewContext, ViewConfig} from "../view/interface";
1313
import {Ng2ViewDeclaration} from "./interface";
14+
import {ng2ComponentInputs} from "./componentUtil";
1415

1516
/** @hidden */
1617
let id = 0;
@@ -23,6 +24,12 @@ const getProviders = (injector) => {
2324
return providers;
2425
};
2526

27+
// These are provide()d as the string UiView.PARENT_INJECT
28+
export interface ParentUiViewInject {
29+
context: ViewContext;
30+
fqn: string;
31+
}
32+
2633
/**
2734
* A UI-Router viewport directive, which is filled in by a view (component) on a state.
2835
*
@@ -84,44 +91,39 @@ const getProviders = (injector) => {
8491
// <div style="padding: 1em; border: 1px solid lightgrey;">
8592
//
8693
// <div #content style="color: lightgrey; font-size: smaller;">
87-
// <div>ui-view #{{uiViewData.id}} created by '{{ parentContext.name || "(root)" }}' state</div>
88-
// <div>name: (absolute) '{{uiViewData.fqn}}' (contextual) '{{uiViewData.name}}@{{parentContext.name}}' </div>
89-
// <div>currently filled by: '{{(uiViewData.config && uiViewData.config.viewDecl.$context) || 'empty...'}}'</div>
94+
// <div>ui-view #{{uiViewData?.id}} created by '{{ parentContext?.name || "(root)" }}' state</div>
95+
// <div>name: (absolute) '{{uiViewData?.fqn}}' (contextual) '{{uiViewData?.name}}@{{parentContext?.name}}' </div>
96+
// <div>currently filled by: '{{(uiViewData?.config && uiViewData?.config?.viewDecl?.$context) || 'empty...'}}'</div>
9097
// </div>
9198
//
9299
// </div>`
93100
})
94101
export class UiView {
95102
@Input() name: string;
96-
@Input() set 'ui-view'(val) { this.name = val; }
97-
103+
@Input('ui-view') set _name(val) { this.name = val; }
98104
componentRef: ComponentRef;
99105
deregister: Function;
100106
uiViewData: any = {};
101107

102-
static INJECT = {
103-
fqn: "UiView.parentFQN",
104-
context: "UiView.parentContext"
105-
};
108+
static PARENT_INJECT = "UiView.PARENT_INJECT";
106109

107110
constructor(
108111
public router: UIRouter,
109-
@Inject(UiView.INJECT.context) public parentContext: ViewContext,
110-
@Inject(UiView.INJECT.fqn) public parentFqn: string,
112+
@Inject(UiView.PARENT_INJECT) public parent: ParentUiViewInject,
111113
public dcl: DynamicComponentLoader,
112114
public elementRef: ElementRef,
113115
public injector: Injector
114116
) { }
115117

116118
ngOnInit() {
117-
let parentFqn = this.parentFqn;
119+
let parentFqn = this.parent.fqn;
118120
let name = this.name || '$default';
119121

120122
this.uiViewData = {
121123
id: id++,
122124
name: name,
123125
fqn: parentFqn ? parentFqn + "." + name : name,
124-
creationContext: this.parentContext,
126+
creationContext: this.parent.context,
125127
configUpdated: this.viewConfigUpdated.bind(this),
126128
config: undefined
127129
};
@@ -131,10 +133,11 @@ export class UiView {
131133

132134
disposeLast() {
133135
if (this.componentRef) this.componentRef.dispose();
136+
this.componentRef = null;
134137
}
135138

136139
ngOnDestroy() {
137-
this.deregister();
140+
if (this.deregister) this.deregister();
138141
this.disposeLast();
139142
}
140143

@@ -159,17 +162,26 @@ export class UiView {
159162
let rc = config.node.resolveContext;
160163
let resolvables = rc.getResolvables();
161164
let rawProviders = Object.keys(resolvables).map(key => provide(key, { useValue: resolvables[key].data }));
162-
rawProviders.push(provide(UiView.INJECT.context, { useValue: config.viewDecl.$context }));
163-
rawProviders.push(provide(UiView.INJECT.fqn, { useValue: uiViewData.fqn }));
165+
rawProviders.push(provide(UiView.PARENT_INJECT, { useValue: { context: config.viewDecl.$context, fqn: uiViewData.fqn } }));
164166
let providers = Injector.resolve(rawProviders);
165167

166-
let exclusions = [UiView.INJECT.context, UiView.INJECT.fqn];
168+
let exclusions = [UiView.PARENT_INJECT];
167169
providers = getProviders(injector).filter(x => exclusions.indexOf(x.key.displayName) === -1).concat(providers);
168170

169-
// The 'controller' should be a Component class
170-
// TODO: pull from 'component' declaration, do not require template.
171171
let component = <Type> viewDecl.component;
172-
dcl.loadIntoLocation(component, elementRef, "content", providers).then(ref => this.componentRef = ref);
172+
dcl.loadIntoLocation(component, elementRef, "content", providers).then(ref => {
173+
this.componentRef = ref;
174+
175+
// TODO: wire uiCanExit and uiOnParamsChanged callbacks
176+
177+
// Set resolve data to matching @Input("prop")
178+
let inputs = ng2ComponentInputs(component);
179+
let bindings = viewDecl['bindings'] || {};
180+
181+
inputs.map(tuple => ({ prop: tuple.prop, resolve: bindings[tuple.prop] || tuple.resolve }))
182+
.filter(tuple => resolvables[tuple.resolve] !== undefined)
183+
.forEach(tuple => { ref.instance[tuple.prop] = resolvables[tuple.resolve].data });
184+
});
173185
}
174186
}
175187

0 commit comments

Comments
 (0)