fix(ivy): use default selector for Components if selector is empty (#29239)

Prior to this change default selector for Components was not applied in case selector is missing or defined as an empty string. This update aligns this behavior between Ivy and VE: now default selector is used for Components when it's needed. Directives with empty selector are not allowed and trigger a compile-time error in both Ivy and VE.

PR Close #29239
This commit is contained in:
Andrew Kushnir 2019-03-11 17:58:37 -07:00 committed by Kara Erickson
parent 9d4b7d7d41
commit fe76494759
3 changed files with 85 additions and 1 deletions

View File

@ -172,7 +172,8 @@ export function extractDirectiveMetadata(
throw new FatalDiagnosticError(
ErrorCode.VALUE_HAS_WRONG_TYPE, expr, `selector must be a string`);
}
selector = resolved;
// use default selector in case selector is an empty string
selector = resolved === '' ? defaultSelector : resolved;
}
if (!selector) {
throw new Error(`Directive ${clazz.name !.text} has no selector, please add it!`);

View File

@ -738,6 +738,75 @@ describe('ngtsc behavioral tests', () => {
'i0.ɵNgModuleDefWithMeta<TestModule, [typeof TestPipe, typeof TestCmp], never, never>');
});
describe('empty and missing selectors', () => {
it('should use default selector for Components when no selector present', () => {
env.tsconfig({});
env.write('test.ts', `
import {Component} from '@angular/core';
@Component({
template: '...',
})
export class TestCmp {}
`);
env.driveMain();
const jsContents = env.getContents('test.js');
expect(jsContents).toContain('selectors: [["ng-component"]]');
});
it('should use default selector for Components with empty string selector', () => {
env.tsconfig({});
env.write('test.ts', `
import {Component} from '@angular/core';
@Component({
selector: '',
template: '...',
})
export class TestCmp {}
`);
env.driveMain();
const jsContents = env.getContents('test.js');
expect(jsContents).toContain('selectors: [["ng-component"]]');
});
it('should throw if selector is missing in Directive decorator params', () => {
env.tsconfig({});
env.write('test.ts', `
import {Directive} from '@angular/core';
@Directive({
inputs: ['a', 'b']
})
export class TestDir {}
`);
const errors = env.driveDiagnostics();
expect(trim(errors[0].messageText as string))
.toContain('Directive TestDir has no selector, please add it!');
});
it('should throw if Directive selector is an empty string', () => {
env.tsconfig({});
env.write('test.ts', `
import {Directive} from '@angular/core';
@Directive({
selector: ''
})
export class TestDir {}
`);
const errors = env.driveDiagnostics();
expect(trim(errors[0].messageText as string))
.toContain('Directive TestDir has no selector, please add it!');
});
});
describe('multiple decorators on classes', () => {
it('should compile @Injectable on Components, Directives, Pipes, and Modules', () => {
env.tsconfig();

View File

@ -1426,6 +1426,20 @@ function declareTests(config?: {useJit: boolean}) {
.toThrowError(`Directive ${stringify(SomeDirective)} has no selector, please add it!`);
});
it('should throw when using directives with empty string selector', () => {
@Directive({selector: ''})
class SomeDirective {
}
@Component({selector: 'comp', template: ''})
class SomeComponent {
}
TestBed.configureTestingModule({declarations: [MyComp, SomeDirective, SomeComponent]});
expect(() => TestBed.createComponent(MyComp))
.toThrowError(`Directive ${stringify(SomeDirective)} has no selector, please add it!`);
});
it('should use a default element name for components without selectors', () => {
let noSelectorComponentFactory: ComponentFactory<SomeComponent> = undefined !;