feat(compiler): allow multiple exportAs names
This change allows users to specify multiple exportAs names for a directive by giving a comma-delimited list inside the string. The primary motivation for this change is to allow these names to be changed in a backwards compatible way.
This commit is contained in:
parent
0d45828460
commit
3a500981ef
|
@ -580,7 +580,7 @@ class TemplateParseVisitor implements html.Visitor {
|
|||
directive.inputs, props, directiveProperties, targetBoundDirectivePropNames);
|
||||
elementOrDirectiveRefs.forEach((elOrDirRef) => {
|
||||
if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
|
||||
(directive.exportAs == elOrDirRef.value)) {
|
||||
(elOrDirRef.isReferenceToDirective(directive))) {
|
||||
targetReferences.push(new ReferenceAst(
|
||||
elOrDirRef.name, createTokenForReference(directive.type.reference),
|
||||
elOrDirRef.sourceSpan));
|
||||
|
@ -805,8 +805,25 @@ class NonBindableVisitor implements html.Visitor {
|
|||
visitExpansionCase(expansionCase: html.ExpansionCase, context: any): any { return expansionCase; }
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference to an element or directive in a template. E.g., the reference in this template:
|
||||
*
|
||||
* <div #myMenu="coolMenu">
|
||||
*
|
||||
* would be {name: 'myMenu', value: 'coolMenu', sourceSpan: ...}
|
||||
*/
|
||||
class ElementOrDirectiveRef {
|
||||
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
|
||||
|
||||
/** Gets whether this is a reference to the given directive. */
|
||||
isReferenceToDirective(directive: CompileDirectiveSummary) {
|
||||
return splitExportAs(directive.exportAs).indexOf(this.value) !== -1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Splits a raw, potentially comma-delimted `exportAs` value into an array of names. */
|
||||
function splitExportAs(exportAs: string | null): string[] {
|
||||
return exportAs ? exportAs.split(',').map(e => e.trim()) : [];
|
||||
}
|
||||
|
||||
export function splitClasses(classAttrValue: string): string[] {
|
||||
|
|
|
@ -1209,6 +1209,24 @@ Binding to attribute 'onEvent' is disallowed for security reasons ("<my-componen
|
|||
]);
|
||||
});
|
||||
|
||||
it('should assign references to directives via exportAs with multiple names', () => {
|
||||
const pizzaTestDirective =
|
||||
compileDirectiveMetadataCreate({
|
||||
selector: 'pizza-test',
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Pizza'}}),
|
||||
exportAs: 'pizza, cheeseSauceBread'
|
||||
}).toSummary();
|
||||
|
||||
const template = '<pizza-test #food="pizza" #yum="cheeseSauceBread"></pizza-test>';
|
||||
|
||||
expect(humanizeTplAst(parse(template, [pizzaTestDirective]))).toEqual([
|
||||
[ElementAst, 'pizza-test'],
|
||||
[ReferenceAst, 'food', createTokenForReference(pizzaTestDirective.type.reference)],
|
||||
[ReferenceAst, 'yum', createTokenForReference(pizzaTestDirective.type.reference)],
|
||||
[DirectiveAst, pizzaTestDirective],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should report references with values that dont match a directive as errors', () => {
|
||||
expect(() => parse('<div #a="dirA"></div>', [])).toThrowError(`Template parse errors:
|
||||
There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"></div>"): TestComp@0:5`);
|
||||
|
@ -1231,6 +1249,31 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
|
|||
|
||||
});
|
||||
|
||||
it('should report duplicate reference names when using mutliple exportAs names', () => {
|
||||
const pizzaDirective =
|
||||
compileDirectiveMetadataCreate({
|
||||
selector: '[dessert-pizza]',
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Pizza'}}),
|
||||
exportAs: 'dessertPizza, chocolate'
|
||||
}).toSummary();
|
||||
|
||||
const chocolateDirective =
|
||||
compileDirectiveMetadataCreate({
|
||||
selector: '[chocolate]',
|
||||
type: createTypeMeta({reference: {filePath: someModuleUrl, name: 'Chocolate'}}),
|
||||
exportAs: 'chocolate'
|
||||
}).toSummary();
|
||||
|
||||
const template = '<div dessert-pizza chocolate #snack="chocolate"></div>';
|
||||
const compileTemplate = () => parse(template, [pizzaDirective, chocolateDirective]);
|
||||
const duplicateReferenceError = 'Template parse errors:\n' +
|
||||
'Reference "#snack" is defined several times ' +
|
||||
'("<div dessert-pizza chocolate [ERROR ->]#snack="chocolate"></div>")' +
|
||||
': TestComp@0:29';
|
||||
|
||||
expect(compileTemplate).toThrowError(duplicateReferenceError);
|
||||
});
|
||||
|
||||
it('should not throw error when there is same reference name in different templates',
|
||||
() => {
|
||||
expect(() => parse('<div #a><template #a><span>OK</span></template></div>', []))
|
||||
|
|
|
@ -54,7 +54,8 @@ export interface DirectiveDecorator {
|
|||
*
|
||||
* **Metadata Properties:**
|
||||
*
|
||||
* * **exportAs** - name under which the component instance is exported in a template
|
||||
* * **exportAs** - name under which the component instance is exported in a template. Can be
|
||||
* given a single name or a comma-delimited list of names.
|
||||
* * **host** - map of class property to host element bindings for events, properties and
|
||||
* attributes
|
||||
* * **inputs** - list of class property names to data-bind as component inputs
|
||||
|
|
|
@ -469,6 +469,20 @@ function declareTests({useJit}: {useJit: boolean}) {
|
|||
.toBeAnInstanceOf(ExportDir);
|
||||
});
|
||||
|
||||
it('should assign a directive to a ref when it has multiple exportAs names', () => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [MyComp, DirectiveWithMultipleExportAsNames]});
|
||||
|
||||
const template = '<div multiple-export-as #x="dirX" #y="dirY"></div>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
expect(fixture.debugElement.children[0].references !['x'])
|
||||
.toBeAnInstanceOf(DirectiveWithMultipleExportAsNames);
|
||||
expect(fixture.debugElement.children[0].references !['y'])
|
||||
.toBeAnInstanceOf(DirectiveWithMultipleExportAsNames);
|
||||
});
|
||||
|
||||
it('should make the assigned component accessible in property bindings, even if they were declared before the component',
|
||||
() => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, ChildComp]});
|
||||
|
@ -2442,6 +2456,10 @@ class SomeImperativeViewport {
|
|||
class ExportDir {
|
||||
}
|
||||
|
||||
@Directive({selector: '[multiple-export-as]', exportAs: 'dirX, dirY'})
|
||||
export class DirectiveWithMultipleExportAsNames {
|
||||
}
|
||||
|
||||
@Component({selector: 'comp'})
|
||||
class ComponentWithoutView {
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue