fix(compiler): Allow components to use any style of selector. Fixes #1602
This commit is contained in:
		
							parent
							
								
									4422819754
								
							
						
					
					
						commit
						c20a5d65d8
					
				| @ -20,6 +20,7 @@ import {CompileStepFactory, DefaultStepFactory} from './compile_step_factory'; | ||||
| import {ElementSchemaRegistry} from '../schema/element_schema_registry'; | ||||
| import {Parser} from 'angular2/src/change_detection/change_detection'; | ||||
| import * as pvm from '../view/proto_view_merger'; | ||||
| import {CssSelector} from './selector'; | ||||
| import {DOCUMENT_TOKEN, APP_ID_TOKEN} from '../dom_tokens'; | ||||
| import {Inject} from 'angular2/di'; | ||||
| import {SharedStylesHost} from '../view/shared_styles_host'; | ||||
| @ -50,7 +51,7 @@ export class DomCompiler extends RenderCompiler { | ||||
|   } | ||||
| 
 | ||||
|   compileHost(directiveMetadata: DirectiveMetadata): Promise<ProtoViewDto> { | ||||
|     var hostViewDef = new ViewDefinition({ | ||||
|     let hostViewDef = new ViewDefinition({ | ||||
|       componentId: directiveMetadata.id, | ||||
|       templateAbsUrl: null, template: null, | ||||
|       styles: null, | ||||
| @ -58,10 +59,12 @@ export class DomCompiler extends RenderCompiler { | ||||
|       directives: [directiveMetadata], | ||||
|       encapsulation: ViewEncapsulation.NONE | ||||
|     }); | ||||
|     return this._compileView( | ||||
|         hostViewDef, new TemplateAndStyles( | ||||
|                          `<${directiveMetadata.selector}></${directiveMetadata.selector}>`, []), | ||||
|         ViewType.HOST); | ||||
| 
 | ||||
|     let selector = CssSelector.parse(directiveMetadata.selector)[0]; | ||||
|     let hostTemplate = selector.getMatchingElementTemplate(); | ||||
|     let templateAndStyles = new TemplateAndStyles(hostTemplate, []); | ||||
| 
 | ||||
|     return this._compileView(hostViewDef, templateAndStyles, ViewType.HOST); | ||||
|   } | ||||
| 
 | ||||
|   mergeProtoViewsRecursively( | ||||
|  | ||||
| @ -24,21 +24,12 @@ export class DirectiveParser implements CompileStep { | ||||
|     for (var i = 0; i < _directives.length; i++) { | ||||
|       var directive = _directives[i]; | ||||
|       var selector = CssSelector.parse(directive.selector); | ||||
|       this._ensureComponentOnlyHasElementSelector(selector, directive); | ||||
|       this._selectorMatcher.addSelectables(selector, i); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   processStyle(style: string): string { return style; } | ||||
| 
 | ||||
|   _ensureComponentOnlyHasElementSelector(selector, directive) { | ||||
|     var isElementSelector = selector.length === 1 && selector[0].isElementSelector(); | ||||
|     if (!isElementSelector && directive.type === DirectiveMetadata.COMPONENT_TYPE) { | ||||
|       throw new BaseException( | ||||
|           `Component '${directive.id}' can only have an element selector, but had '${directive.selector}'`); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   processElement(parent: CompileElement, current: CompileElement, control: CompileControl) { | ||||
|     var attrs = current.attrs(); | ||||
|     var classList = current.classList(); | ||||
|  | ||||
| @ -91,6 +91,21 @@ export class CssSelector { | ||||
|     this.element = element; | ||||
|   } | ||||
| 
 | ||||
|   /** Gets a template string for an element that matches the selector. */ | ||||
|   getMatchingElementTemplate(): string { | ||||
|     let tagName = isPresent(this.element) ? this.element : 'div'; | ||||
|     let classAttr = this.classNames.length > 0 ? ` class="${this.classNames.join(' ')}"` : ''; | ||||
| 
 | ||||
|     let attrs = ''; | ||||
|     for (let i = 0; i < this.attrs.length; i += 2) { | ||||
|       let attrName = this.attrs[i]; | ||||
|       let attrValue = this.attrs[i + 1] !== '' ? `="${this.attrs[i + 1]}"` : ''; | ||||
|       attrs += ` ${attrName}${attrValue}`; | ||||
|     } | ||||
| 
 | ||||
|     return `<${tagName}${classAttr}${attrs}></${tagName}>`; | ||||
|   } | ||||
| 
 | ||||
|   addAttribute(name: string, value: string = _EMPTY_ATTR_VALUE) { | ||||
|     this.attrs.push(name.toLowerCase()); | ||||
|     if (isPresent(value)) { | ||||
|  | ||||
| @ -89,6 +89,27 @@ export function runCompilerCommonTests() { | ||||
|                }); | ||||
|          })); | ||||
| 
 | ||||
|       it('should create element from component selector', inject([AsyncTestCompleter], (async) => { | ||||
|            var compiler = createCompiler((parent, current, control) => { | ||||
|              current.inheritedProtoView.bindVariable('b', 'a'); | ||||
|            }); | ||||
| 
 | ||||
|            var dirMetadata = DirectiveMetadata.create({ | ||||
|              id: 'id', | ||||
|              selector: 'marquee.jazzy[size=huge]', | ||||
|              type: DirectiveMetadata.COMPONENT_TYPE | ||||
|            }); | ||||
| 
 | ||||
|            compiler.compileHost(dirMetadata) | ||||
|                .then((protoView) => { | ||||
|                  let element = DOM.firstChild(DOM.content(templateRoot(protoView))); | ||||
|                  expect(DOM.tagName(element).toLowerCase()).toEqual('marquee'); | ||||
|                  expect(DOM.hasClass(element, 'jazzy')).toBe(true); | ||||
|                  expect(DOM.getAttribute(element, 'size')).toEqual('huge'); | ||||
|                  async.done(); | ||||
|                }); | ||||
|          })); | ||||
| 
 | ||||
|       it('should use the inline template and compile in sync', | ||||
|          inject([AsyncTestCompleter], (async) => { | ||||
|            var compiler = createCompiler(EMPTY_STEP); | ||||
|  | ||||
| @ -174,12 +174,6 @@ export function main() { | ||||
|         expect(results[0].componentId).toEqual('someComponent'); | ||||
|       }); | ||||
| 
 | ||||
|       it('should throw when the provided selector is not an element selector', () => { | ||||
|         expect(() => { createPipeline(null, [componentWithNonElementSelector]); }) | ||||
|             .toThrowError( | ||||
|                 `Component 'componentWithNonElementSelector' can only have an element selector, but had '[attr]'`); | ||||
|       }); | ||||
| 
 | ||||
|       it('should not allow multiple component directives on the same element', () => { | ||||
|         expect(() => { | ||||
|           process(el('<some-comp></some-comp>'), null, [someComponent, someComponentDup]); | ||||
|  | ||||
| @ -335,4 +335,27 @@ export function main() { | ||||
|       expect(cssSelectors[2].notSelectors[0].classNames).toEqual(['special']); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('CssSelector.getMatchingElementTemplate', () => { | ||||
|     it('should create an element with a tagName, classes, and attributes', () => { | ||||
|       let selector = CssSelector.parse('blink.neon.hotpink[sweet][dismissable=false]')[0]; | ||||
|       let template = selector.getMatchingElementTemplate(); | ||||
| 
 | ||||
|       expect(template).toEqual('<blink class="neon hotpink" sweet dismissable="false"></blink>'); | ||||
|     }); | ||||
| 
 | ||||
|     it('should create an element without a tag name', () => { | ||||
|       let selector = CssSelector.parse('[fancy]')[0]; | ||||
|       let template = selector.getMatchingElementTemplate(); | ||||
| 
 | ||||
|       expect(template).toEqual('<div fancy></div>'); | ||||
|     }); | ||||
| 
 | ||||
|     it('should ignore :not selectors', () => { | ||||
|       let selector = CssSelector.parse('grape:not(.red)')[0]; | ||||
|       let template = selector.getMatchingElementTemplate(); | ||||
| 
 | ||||
|       expect(template).toEqual('<grape></grape>'); | ||||
|     }); | ||||
|   }); | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user