Skip to content

Commit 9429032

Browse files
tboschmhevery
authored andcommitted
feat(upgrade): use ComponentFactory.inputs/outputs/ngContentSelectors (#15214)
DEPRECATION: - the arguments `inputs` / `outputs` / `ngContentSelectors` of `downgradeComponent` are no longer used as Angular calculates these automatically now. - Compiler.getNgContentSelectors is deprecated. Use ComponentFactory.ngContentSelectors instead.
1 parent 791534f commit 9429032

22 files changed

+92
-416
lines changed

packages/compiler/src/metadata_resolver.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,6 @@ export class CompileMetadataResolver {
156156
const templateName = inputs[propName];
157157
factory.inputs.push({propName, templateName});
158158
}
159-
const outputsArr: {propName: string, templateName: string}[] = [];
160159
for (let propName in outputs) {
161160
const templateName = outputs[propName];
162161
factory.outputs.push({propName, templateName});

packages/compiler/test/aot/compiler_spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,6 @@ describe('compiler (unbundled Angular)', () => {
324324
const host = new MockCompilerHost(['/app/app.ts'], FILES, angularFiles);
325325
const aotHost = new MockAotCompilerHost(host);
326326
let generatedFiles: GeneratedFile[];
327-
const warnSpy = spyOn(console, 'warn');
328327
compile(host, aotHost, expectNoDiagnostics).then((generatedFiles) => {
329328
const genFile = generatedFiles.find(genFile => genFile.srcFileUrl === '/app/app.ts');
330329
const createComponentFactoryCall =

packages/examples/upgrade/static/ts/module.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,7 @@ ng1AppModule.factory('heroesService', downgradeInjectable(HeroesService));
145145

146146
// #docregion ng2-heroes-wrapper
147147
// This is directive will act as the interface to the "downgraded" Angular component
148-
ng1AppModule.directive(
149-
'ng2Heroes',
150-
downgradeComponent(
151-
// The inputs and outputs here must match the relevant names of the properties on the
152-
// "downgraded" component
153-
{component: Ng2HeroesComponent, inputs: ['heroes'], outputs: ['addHero', 'removeHero']}));
148+
ng1AppModule.directive('ng2Heroes', downgradeComponent({component: Ng2HeroesComponent}));
154149
// #enddocregion
155150

156151
// #docregion example-app

packages/upgrade/src/common/component_info.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,23 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Type} from '@angular/core';
10-
11-
export interface ComponentInfo {
12-
component: Type<any>;
13-
inputs?: string[];
14-
outputs?: string[];
15-
selectors?: string[];
16-
}
17-
189
/**
1910
* A `PropertyBinding` represents a mapping between a property name
2011
* and an attribute name. It is parsed from a string of the form
2112
* `"prop: attr"`; or simply `"propAndAttr" where the property
2213
* and attribute have the same identifier.
2314
*/
2415
export class PropertyBinding {
25-
prop: string;
26-
attr: string;
2716
bracketAttr: string;
2817
bracketParenAttr: string;
2918
parenAttr: string;
3019
onAttr: string;
3120
bindAttr: string;
3221
bindonAttr: string;
3322

34-
constructor(public binding: string) { this.parseBinding(); }
23+
constructor(public prop: string, public attr: string) { this.parseBinding(); }
3524

3625
private parseBinding() {
37-
const parts = this.binding.split(':');
38-
this.prop = parts[0].trim();
39-
this.attr = (parts[1] || this.prop).trim();
4026
this.bracketAttr = `[${this.attr}]`;
4127
this.parenAttr = `(${this.attr})`;
4228
this.bracketParenAttr = `[(${this.attr})]`;

packages/upgrade/src/common/content_projection_helper.ts

Lines changed: 0 additions & 20 deletions
This file was deleted.

packages/upgrade/src/common/downgrade_component.ts

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {ComponentFactory, ComponentFactoryResolver, Injector, Type} from '@angul
1111
import * as angular from './angular1';
1212
import {$COMPILE, $INJECTOR, $PARSE, INJECTOR_KEY, REQUIRE_INJECTOR, REQUIRE_NG_MODEL} from './constants';
1313
import {DowngradeComponentAdapter} from './downgrade_component_adapter';
14-
import {NgContentSelectorHelper} from './ng_content_selector_helper';
1514
import {controllerKey, getComponentName} from './util';
1615

1716
let downgradeCount = 0;
@@ -38,15 +37,6 @@ let downgradeCount = 0;
3837
*
3938
* {@example upgrade/static/ts/module.ts region="ng2-heroes-wrapper"}
4039
*
41-
* In this example you can see that we must provide information about the component being
42-
* "downgraded". This is because once the AoT compiler has run, all metadata about the
43-
* component has been removed from the code, and so cannot be inferred.
44-
*
45-
* We must do the following:
46-
* * specify the Angular component class that is to be downgraded
47-
* * specify all inputs and outputs that the AngularJS component expects
48-
* * specify the selectors used in any `ng-content` elements in the component's template
49-
*
5040
* @description
5141
*
5242
* A helper function that returns a factory function to be used for registering an
@@ -55,28 +45,17 @@ let downgradeCount = 0;
5545
* The parameter contains information about the Component that is being downgraded:
5646
*
5747
* * `component: Type<any>`: The type of the Component that will be downgraded
58-
* * `inputs: string[]`: A collection of strings that specify what inputs the component accepts
59-
* * `outputs: string[]`: A collection of strings that specify what outputs the component emits
60-
* * `selectors: string[]`: A collection of strings that specify what selectors are expected on
61-
* `ng-content` elements in the template to enable content projection (a.k.a. transclusion in
62-
* AngularJS)
63-
*
64-
* The `inputs` and `outputs` are strings that map the names of properties to camelCased
65-
* attribute names. They are of the form `"prop: attr"`; or simply `"propAndAttr" where the
66-
* property and attribute have the same identifier.
67-
*
68-
* The `selectors` are the values of the `select` attribute of each of the `ng-content` elements
69-
* that appear in the downgraded component's template.
70-
* These selectors must be provided in the order that they appear in the template as they are
71-
* mapped by index to the projected nodes.
7248
*
7349
* @experimental
7450
*/
75-
export function downgradeComponent(info: /* ComponentInfo */ {
51+
export function downgradeComponent(info: {
7652
component: Type<any>;
53+
/** @deprecated since v4. This parameter is no longer used */
7754
inputs?: string[];
55+
/** @deprecated since v4. This parameter is no longer used */
7856
outputs?: string[];
79-
selectors?: string[]
57+
/** @deprecated since v4. This parameter is no longer used */
58+
selectors?: string[];
8059
}): any /* angular.IInjectable */ {
8160
const idPrefix = `NG2_UPGRADE_${downgradeCount++}_`;
8261
let idCount = 0;
@@ -114,7 +93,7 @@ export function downgradeComponent(info: /* ComponentInfo */ {
11493
const id = idPrefix + (idCount++);
11594
const injectorPromise = new ParentInjectorPromise(element);
11695
const facade = new DowngradeComponentAdapter(
117-
id, info, element, attrs, scope, ngModel, injector, $injector, $compile, $parse,
96+
id, element, attrs, scope, ngModel, injector, $injector, $compile, $parse,
11897
componentFactory);
11998

12099
const projectableNodes = facade.compileContents();

packages/upgrade/src/common/downgrade_component_adapter.ts

Lines changed: 38 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@
99
import {ChangeDetectorRef, ComponentFactory, ComponentRef, EventEmitter, Injector, OnChanges, ReflectiveInjector, SimpleChange, SimpleChanges, Type} from '@angular/core';
1010

1111
import * as angular from './angular1';
12-
import {ComponentInfo, PropertyBinding} from './component_info';
12+
import {PropertyBinding} from './component_info';
1313
import {$SCOPE} from './constants';
14-
import {NgContentSelectorHelper} from './ng_content_selector_helper';
1514
import {getAttributesAsArray, getComponentName, hookupNgModel} from './util';
1615

1716
const INITIAL_VALUE = {
@@ -27,7 +26,7 @@ export class DowngradeComponentAdapter {
2726
private changeDetector: ChangeDetectorRef = null;
2827

2928
constructor(
30-
private id: string, private info: ComponentInfo, private element: angular.IAugmentedJQuery,
29+
private id: string, private element: angular.IAugmentedJQuery,
3130
private attrs: angular.IAttributes, private scope: angular.IScope,
3231
private ngModel: angular.INgModelController, private parentInjector: Injector,
3332
private $injector: angular.IInjectorService, private $compile: angular.ICompileService,
@@ -67,9 +66,9 @@ export class DowngradeComponentAdapter {
6766

6867
setupInputs(): void {
6968
const attrs = this.attrs;
70-
const inputs = this.info.inputs || [];
69+
const inputs = this.componentFactory.inputs || [];
7170
for (let i = 0; i < inputs.length; i++) {
72-
const input = new PropertyBinding(inputs[i]);
71+
const input = new PropertyBinding(inputs[i].propName, inputs[i].templateName);
7372
let expr: any /** TODO #9100 */ = null;
7473

7574
if (attrs.hasOwnProperty(input.attr)) {
@@ -103,7 +102,7 @@ export class DowngradeComponentAdapter {
103102
}
104103
}
105104

106-
const prototype = this.info.component.prototype;
105+
const prototype = this.componentFactory.componentType.prototype;
107106
if (prototype && (<OnChanges>prototype).ngOnChanges) {
108107
// Detect: OnChanges interface
109108
this.inputChanges = {};
@@ -118,9 +117,9 @@ export class DowngradeComponentAdapter {
118117

119118
setupOutputs() {
120119
const attrs = this.attrs;
121-
const outputs = this.info.outputs || [];
120+
const outputs = this.componentFactory.outputs || [];
122121
for (let j = 0; j < outputs.length; j++) {
123-
const output = new PropertyBinding(outputs[j]);
122+
const output = new PropertyBinding(outputs[j].propName, outputs[j].templateName);
124123
let expr: any /** TODO #9100 */ = null;
125124
let assignExpr = false;
126125

@@ -158,7 +157,7 @@ export class DowngradeComponentAdapter {
158157
});
159158
} else {
160159
throw new Error(
161-
`Missing emitter '${output.prop}' on component '${getComponentName(this.info.component)}'!`);
160+
`Missing emitter '${output.prop}' on component '${getComponentName(this.componentFactory.componentType)}'!`);
162161
}
163162
}
164163
}
@@ -183,49 +182,31 @@ export class DowngradeComponentAdapter {
183182
}
184183

185184
groupProjectableNodes() {
186-
const ngContentSelectorHelper =
187-
this.parentInjector.get(NgContentSelectorHelper) as NgContentSelectorHelper;
188-
const ngContentSelectors = ngContentSelectorHelper.getNgContentSelectors(this.info);
189-
190-
if (!ngContentSelectors) {
191-
throw new Error('Expecting ngContentSelectors for: ' + getComponentName(this.info.component));
192-
}
193-
194-
return this._groupNodesBySelector(ngContentSelectors, this.element.contents());
185+
let ngContentSelectors = this.componentFactory.ngContentSelectors;
186+
return groupNodesBySelector(ngContentSelectors, this.element.contents());
195187
}
188+
}
196189

197-
/**
198-
* Group a set of DOM nodes into `ngContent` groups, based on the given content selectors.
199-
*/
200-
private _groupNodesBySelector(ngContentSelectors: string[], nodes: Node[]): Node[][] {
201-
const projectableNodes: Node[][] = [];
202-
let wildcardNgContentIndex: number;
190+
/**
191+
* Group a set of DOM nodes into `ngContent` groups, based on the given content selectors.
192+
*/
193+
export function groupNodesBySelector(ngContentSelectors: string[], nodes: Node[]): Node[][] {
194+
const projectableNodes: Node[][] = [];
195+
let wildcardNgContentIndex: number;
203196

204-
for (let i = 0, ii = ngContentSelectors.length; i < ii; ++i) {
205-
projectableNodes[i] = [];
206-
}
197+
for (let i = 0, ii = ngContentSelectors.length; i < ii; ++i) {
198+
projectableNodes[i] = [];
199+
}
207200

208-
for (let j = 0, jj = nodes.length; j < jj; ++j) {
209-
const node = nodes[j];
210-
const ngContentIndex = findMatchingNgContentIndex(node, ngContentSelectors);
211-
if (ngContentIndex != null) {
212-
projectableNodes[ngContentIndex].push(node);
213-
}
201+
for (let j = 0, jj = nodes.length; j < jj; ++j) {
202+
const node = nodes[j];
203+
const ngContentIndex = findMatchingNgContentIndex(node, ngContentSelectors);
204+
if (ngContentIndex != null) {
205+
projectableNodes[ngContentIndex].push(node);
214206
}
215-
216-
return projectableNodes;
217207
}
218-
}
219-
220-
let _matches: (this: any, selector: string) => boolean;
221208

222-
function matchesSelector(el: any, selector: string): boolean {
223-
if (!_matches) {
224-
const elProto = <any>Element.prototype;
225-
_matches = elProto.matchesSelector || elProto.mozMatchesSelector || elProto.msMatchesSelector ||
226-
elProto.oMatchesSelector || elProto.webkitMatchesSelector;
227-
}
228-
return _matches.call(el, selector);
209+
return projectableNodes;
229210
}
230211

231212
function findMatchingNgContentIndex(element: any, ngContentSelectors: string[]): number {
@@ -247,4 +228,15 @@ function findMatchingNgContentIndex(element: any, ngContentSelectors: string[]):
247228
ngContentIndices.push(wildcardNgContentIndex);
248229
}
249230
return ngContentIndices.length ? ngContentIndices[0] : null;
250-
}
231+
}
232+
233+
let _matches: (this: any, selector: string) => boolean;
234+
235+
function matchesSelector(el: any, selector: string): boolean {
236+
if (!_matches) {
237+
const elProto = <any>Element.prototype;
238+
_matches = elProto.matches || elProto.matchesSelector || elProto.mozMatchesSelector ||
239+
elProto.msMatchesSelector || elProto.oMatchesSelector || elProto.webkitMatchesSelector;
240+
}
241+
return el.nodeType === Node.ELEMENT_NODE ? _matches.call(el, selector) : false;
242+
}

packages/upgrade/src/common/ng_content_selector_helper.ts

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)