Skip to content

Commit c20a5d6

Browse files
committed
fix(compiler): Allow components to use any style of selector. Fixes angular#1602
1 parent 4422819 commit c20a5d6

File tree

6 files changed

+67
-20
lines changed

6 files changed

+67
-20
lines changed

modules/angular2/src/render/dom/compiler/compiler.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {CompileStepFactory, DefaultStepFactory} from './compile_step_factory';
2020
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
2121
import {Parser} from 'angular2/src/change_detection/change_detection';
2222
import * as pvm from '../view/proto_view_merger';
23+
import {CssSelector} from './selector';
2324
import {DOCUMENT_TOKEN, APP_ID_TOKEN} from '../dom_tokens';
2425
import {Inject} from 'angular2/di';
2526
import {SharedStylesHost} from '../view/shared_styles_host';
@@ -50,18 +51,20 @@ export class DomCompiler extends RenderCompiler {
5051
}
5152

5253
compileHost(directiveMetadata: DirectiveMetadata): Promise<ProtoViewDto> {
53-
var hostViewDef = new ViewDefinition({
54+
let hostViewDef = new ViewDefinition({
5455
componentId: directiveMetadata.id,
5556
templateAbsUrl: null, template: null,
5657
styles: null,
5758
styleAbsUrls: null,
5859
directives: [directiveMetadata],
5960
encapsulation: ViewEncapsulation.NONE
6061
});
61-
return this._compileView(
62-
hostViewDef, new TemplateAndStyles(
63-
`<${directiveMetadata.selector}></${directiveMetadata.selector}>`, []),
64-
ViewType.HOST);
62+
63+
let selector = CssSelector.parse(directiveMetadata.selector)[0];
64+
let hostTemplate = selector.getMatchingElementTemplate();
65+
let templateAndStyles = new TemplateAndStyles(hostTemplate, []);
66+
67+
return this._compileView(hostViewDef, templateAndStyles, ViewType.HOST);
6568
}
6669

6770
mergeProtoViewsRecursively(

modules/angular2/src/render/dom/compiler/directive_parser.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,12 @@ export class DirectiveParser implements CompileStep {
2424
for (var i = 0; i < _directives.length; i++) {
2525
var directive = _directives[i];
2626
var selector = CssSelector.parse(directive.selector);
27-
this._ensureComponentOnlyHasElementSelector(selector, directive);
2827
this._selectorMatcher.addSelectables(selector, i);
2928
}
3029
}
3130

3231
processStyle(style: string): string { return style; }
3332

34-
_ensureComponentOnlyHasElementSelector(selector, directive) {
35-
var isElementSelector = selector.length === 1 && selector[0].isElementSelector();
36-
if (!isElementSelector && directive.type === DirectiveMetadata.COMPONENT_TYPE) {
37-
throw new BaseException(
38-
`Component '${directive.id}' can only have an element selector, but had '${directive.selector}'`);
39-
}
40-
}
41-
4233
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
4334
var attrs = current.attrs();
4435
var classList = current.classList();

modules/angular2/src/render/dom/compiler/selector.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,21 @@ export class CssSelector {
9191
this.element = element;
9292
}
9393

94+
/** Gets a template string for an element that matches the selector. */
95+
getMatchingElementTemplate(): string {
96+
let tagName = isPresent(this.element) ? this.element : 'div';
97+
let classAttr = this.classNames.length > 0 ? ` class="${this.classNames.join(' ')}"` : '';
98+
99+
let attrs = '';
100+
for (let i = 0; i < this.attrs.length; i += 2) {
101+
let attrName = this.attrs[i];
102+
let attrValue = this.attrs[i + 1] !== '' ? `="${this.attrs[i + 1]}"` : '';
103+
attrs += ` ${attrName}${attrValue}`;
104+
}
105+
106+
return `<${tagName}${classAttr}${attrs}></${tagName}>`;
107+
}
108+
94109
addAttribute(name: string, value: string = _EMPTY_ATTR_VALUE) {
95110
this.attrs.push(name.toLowerCase());
96111
if (isPresent(value)) {

modules/angular2/test/render/dom/compiler/compiler_common_tests.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,27 @@ export function runCompilerCommonTests() {
8989
});
9090
}));
9191

92+
it('should create element from component selector', inject([AsyncTestCompleter], (async) => {
93+
var compiler = createCompiler((parent, current, control) => {
94+
current.inheritedProtoView.bindVariable('b', 'a');
95+
});
96+
97+
var dirMetadata = DirectiveMetadata.create({
98+
id: 'id',
99+
selector: 'marquee.jazzy[size=huge]',
100+
type: DirectiveMetadata.COMPONENT_TYPE
101+
});
102+
103+
compiler.compileHost(dirMetadata)
104+
.then((protoView) => {
105+
let element = DOM.firstChild(DOM.content(templateRoot(protoView)));
106+
expect(DOM.tagName(element).toLowerCase()).toEqual('marquee');
107+
expect(DOM.hasClass(element, 'jazzy')).toBe(true);
108+
expect(DOM.getAttribute(element, 'size')).toEqual('huge');
109+
async.done();
110+
});
111+
}));
112+
92113
it('should use the inline template and compile in sync',
93114
inject([AsyncTestCompleter], (async) => {
94115
var compiler = createCompiler(EMPTY_STEP);

modules/angular2/test/render/dom/compiler/directive_parser_spec.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,6 @@ export function main() {
174174
expect(results[0].componentId).toEqual('someComponent');
175175
});
176176

177-
it('should throw when the provided selector is not an element selector', () => {
178-
expect(() => { createPipeline(null, [componentWithNonElementSelector]); })
179-
.toThrowError(
180-
`Component 'componentWithNonElementSelector' can only have an element selector, but had '[attr]'`);
181-
});
182-
183177
it('should not allow multiple component directives on the same element', () => {
184178
expect(() => {
185179
process(el('<some-comp></some-comp>'), null, [someComponent, someComponentDup]);

modules/angular2/test/render/dom/compiler/selector_spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,4 +335,27 @@ export function main() {
335335
expect(cssSelectors[2].notSelectors[0].classNames).toEqual(['special']);
336336
});
337337
});
338+
339+
describe('CssSelector.getMatchingElementTemplate', () => {
340+
it('should create an element with a tagName, classes, and attributes', () => {
341+
let selector = CssSelector.parse('blink.neon.hotpink[sweet][dismissable=false]')[0];
342+
let template = selector.getMatchingElementTemplate();
343+
344+
expect(template).toEqual('<blink class="neon hotpink" sweet dismissable="false"></blink>');
345+
});
346+
347+
it('should create an element without a tag name', () => {
348+
let selector = CssSelector.parse('[fancy]')[0];
349+
let template = selector.getMatchingElementTemplate();
350+
351+
expect(template).toEqual('<div fancy></div>');
352+
});
353+
354+
it('should ignore :not selectors', () => {
355+
let selector = CssSelector.parse('grape:not(.red)')[0];
356+
let template = selector.getMatchingElementTemplate();
357+
358+
expect(template).toEqual('<grape></grape>');
359+
});
360+
});
338361
}

0 commit comments

Comments
 (0)