refactor(compiler): add control.ignoreCurrentElement() to skip the current element
relates to #808
This commit is contained in:
		
							parent
							
								
									f0477e164a
								
							
						
					
					
						commit
						678d541da7
					
				| @ -13,6 +13,8 @@ export class CompileControl { | ||||
|   _parent:CompileElement; | ||||
|   _results; | ||||
|   _additionalChildren; | ||||
|   _ignoreCurrentElement: boolean; | ||||
| 
 | ||||
|   constructor(steps) { | ||||
|     this._steps = steps; | ||||
|     this._currentStepIndex = 0; | ||||
| @ -27,14 +29,21 @@ export class CompileControl { | ||||
|     var previousStepIndex = this._currentStepIndex; | ||||
|     var previousParent = this._parent; | ||||
| 
 | ||||
|     for (var i=startStepIndex; i<this._steps.length; i++) { | ||||
|     this._ignoreCurrentElement = false; | ||||
| 
 | ||||
|     for (var i = startStepIndex; | ||||
|          i < this._steps.length && !this._ignoreCurrentElement; | ||||
|          i++) { | ||||
|       var step = this._steps[i]; | ||||
|       this._parent = parent; | ||||
|       this._currentStepIndex = i; | ||||
|       step.process(parent, current, this); | ||||
|       parent = this._parent; | ||||
|     } | ||||
| 
 | ||||
|     if (!this._ignoreCurrentElement) { | ||||
|       ListWrapper.push(results, current); | ||||
|     } | ||||
| 
 | ||||
|     this._currentStepIndex = previousStepIndex; | ||||
|     this._parent = previousParent; | ||||
| @ -55,4 +64,14 @@ export class CompileControl { | ||||
|     } | ||||
|     ListWrapper.push(this._additionalChildren, element); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Ignores the current element. | ||||
|    * | ||||
|    * When a step call [ignoreCurrentElement], no further steps are executed on the current | ||||
|    * element and no [CompileElement] is added to the result list. | ||||
|    */ | ||||
|   ignoreCurrentElement() { | ||||
|     this._ignoreCurrentElement = true; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -18,7 +18,6 @@ export class CompileElement { | ||||
|   distanceToInheritedBinder:number; | ||||
|   inheritedElementBinder:ElementBinderBuilder; | ||||
|   compileChildren: boolean; | ||||
|   ignoreBindings: boolean; | ||||
|   elementDescription: string; // e.g. '<div [class]="foo">' : used to provide context in case of error
 | ||||
| 
 | ||||
|   constructor(element, compilationUnit = '') { | ||||
| @ -34,8 +33,6 @@ export class CompileElement { | ||||
|     this.inheritedElementBinder = null; | ||||
|     this.distanceToInheritedBinder = 0; | ||||
|     this.compileChildren = true; | ||||
|     // set to true to ignore all the bindings on the element
 | ||||
|     this.ignoreBindings = false; | ||||
|     // description is calculated here as compilation steps may change the element
 | ||||
|     var tplDesc = assertionsEnabled()? getElementDescription(element) : null; | ||||
|     if (compilationUnit !== '') { | ||||
|  | ||||
| @ -30,10 +30,6 @@ export class PropertyBindingParser extends CompileStep { | ||||
|   } | ||||
| 
 | ||||
|   process(parent:CompileElement, current:CompileElement, control:CompileControl) { | ||||
|     if (current.ignoreBindings) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     var attrs = current.attrs(); | ||||
|     var newAttrs = MapWrapper.create(); | ||||
| 
 | ||||
|  | ||||
| @ -19,7 +19,7 @@ export class TextInterpolationParser extends CompileStep { | ||||
|   } | ||||
| 
 | ||||
|   process(parent:CompileElement, current:CompileElement, control:CompileControl) { | ||||
|     if (!current.compileChildren || current.ignoreBindings) { | ||||
|     if (!current.compileChildren) { | ||||
|       return; | ||||
|     } | ||||
|     var element = current.element; | ||||
|  | ||||
| @ -23,12 +23,9 @@ export class ShadowDomCompileStep extends CompileStep { | ||||
|   } | ||||
| 
 | ||||
|   process(parent:CompileElement, current:CompileElement, control:CompileControl) { | ||||
|     if (current.ignoreBindings) { | ||||
|       return; | ||||
|     } | ||||
|     var tagName = DOM.tagName(current.element).toUpperCase(); | ||||
|     if (tagName == 'STYLE') { | ||||
|       this._processStyleElement(current); | ||||
|       this._processStyleElement(current, control); | ||||
|     } else if (tagName == 'CONTENT') { | ||||
|       this._processContentElement(current); | ||||
|     } else { | ||||
| @ -39,17 +36,20 @@ export class ShadowDomCompileStep extends CompileStep { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   _processStyleElement(current) { | ||||
|     current.ignoreBindings = true; | ||||
|   _processStyleElement(current:CompileElement, control:CompileControl) { | ||||
|     var stylePromise = this._shadowDomStrategy.processStyleElement( | ||||
|       this._template.componentId, this._template.absUrl, current.element | ||||
|     ); | ||||
|     if (isPresent(stylePromise) && PromiseWrapper.isPromise(stylePromise)) { | ||||
|       ListWrapper.push(this._subTaskPromises, stylePromise); | ||||
|     } | ||||
| 
 | ||||
|     // Style elements should not be further processed by the compiler, as they can not contain
 | ||||
|     // bindings. Skipping further compiler steps allow speeding up the compilation process.
 | ||||
|     control.ignoreCurrentElement(); | ||||
|   } | ||||
| 
 | ||||
|   _processContentElement(current) { | ||||
|   _processContentElement(current:CompileElement) { | ||||
|     if (this._shadowDomStrategy.hasNativeContentElement()) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
| @ -80,6 +80,19 @@ export function main() { | ||||
|       expect(results[3].inheritedElementBinder.parent).toBe(results[0].inheritedElementBinder); | ||||
|     }); | ||||
| 
 | ||||
|     it('should not execute further steps when ignoreCurrentElement has been called', () => { | ||||
|       var element = el('<div id="1"><span id="2" ignore-current></span><span id="3"></span></div>'); | ||||
|       var logs = []; | ||||
|       var pipeline = new CompilePipeline([ | ||||
|         new IgnoreCurrentElementStep(), | ||||
|         createLoggerStep(logs), | ||||
|       ]); | ||||
|       var results = pipeline.process(element); | ||||
| 
 | ||||
|       expect(results.length).toBe(2); | ||||
|       expect(logs).toEqual(['1', '1<3']) | ||||
|     }); | ||||
| 
 | ||||
|     describe('control.addParent', () => { | ||||
|       it('should report the new parent to the following processor and the result', () => { | ||||
|         var element = el('<div id="1"><span wrap0="1" id="2"><b id="3"></b></span></div>'); | ||||
| @ -188,6 +201,15 @@ export class IgnoreChildrenStep extends CompileStep { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class IgnoreCurrentElementStep extends CompileStep { | ||||
|   process(parent:CompileElement, current:CompileElement, control:CompileControl) { | ||||
|     var attributeMap = DOM.attributeMap(current.element); | ||||
|     if (MapWrapper.contains(attributeMap, 'ignore-current')) { | ||||
|       control.ignoreCurrentElement(); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function logEntry(log, parent, current) { | ||||
|   var parentId = ''; | ||||
|   if (isPresent(parent)) { | ||||
|  | ||||
| @ -11,10 +11,9 @@ var EMPTY_MAP = MapWrapper.create(); | ||||
| 
 | ||||
| export function main() { | ||||
|   describe('PropertyBindingParser', () => { | ||||
|     function createPipeline(ignoreBindings = false, hasNestedProtoView = false) { | ||||
|     function createPipeline(hasNestedProtoView = false) { | ||||
|       return new CompilePipeline([ | ||||
|         new MockStep((parent, current, control) => { | ||||
|           current.ignoreBindings = ignoreBindings; | ||||
|           if (hasNestedProtoView) { | ||||
|             current.bindElement().bindNestedProtoView(el('<template></template>')); | ||||
|           } | ||||
| @ -22,18 +21,13 @@ export function main() { | ||||
|         new PropertyBindingParser(new Parser(new Lexer()))]); | ||||
|     } | ||||
| 
 | ||||
|     function process(element, ignoreBindings = false, hasNestedProtoView = false) { | ||||
|     function process(element, hasNestedProtoView = false) { | ||||
|       return ListWrapper.map( | ||||
|         createPipeline(ignoreBindings, hasNestedProtoView).process(element), | ||||
|         createPipeline(hasNestedProtoView).process(element), | ||||
|         (compileElement) => compileElement.inheritedElementBinder | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     it('should not parse bindings when ignoreBindings is true', () => { | ||||
|       var results = process(el('<div [a]="b"></div>'), true); | ||||
|       expect(results[0]).toEqual(null); | ||||
|     }); | ||||
| 
 | ||||
|     it('should detect [] syntax', () => { | ||||
|       var results = process(el('<div [a]="b"></div>')); | ||||
|       expect(MapWrapper.get(results[0].propertyBindings, 'a').source).toEqual('b'); | ||||
| @ -64,13 +58,13 @@ export function main() { | ||||
|     }); | ||||
| 
 | ||||
|     it('should store variable binding for a template element on the nestedProtoView', () => { | ||||
|       var results = process(el('<template var-george="washington"></p>'), false, true); | ||||
|       var results = process(el('<template var-george="washington"></p>'), true); | ||||
|       expect(results[0].variableBindings).toEqual(EMPTY_MAP); | ||||
|       expect(MapWrapper.get(results[0].nestedProtoView.variableBindings, 'washington')).toEqual('george'); | ||||
|     }); | ||||
| 
 | ||||
|     it('should store variable binding for a non-template element using shorthand syntax on the nestedProtoView', () => { | ||||
|       var results = process(el('<template #george="washington"></template>'), false, true); | ||||
|       var results = process(el('<template #george="washington"></template>'), true); | ||||
|       expect(results[0].variableBindings).toEqual(EMPTY_MAP); | ||||
|       expect(MapWrapper.get(results[0].nestedProtoView.variableBindings, 'washington')).toEqual('george'); | ||||
|     }); | ||||
|  | ||||
| @ -3,24 +3,20 @@ import {TextInterpolationParser} from 'angular2/src/render/dom/compiler/text_int | ||||
| import {CompilePipeline} from 'angular2/src/render/dom/compiler/compile_pipeline'; | ||||
| import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection'; | ||||
| import {Lexer, Parser} from 'angular2/change_detection'; | ||||
| import {CompileElement} from 'angular2/src/render/dom/compiler/compile_element'; | ||||
| import {CompileStep} from 'angular2/src/render/dom/compiler/compile_step' | ||||
| import {CompileControl} from 'angular2/src/render/dom/compiler/compile_control'; | ||||
| import {IgnoreChildrenStep} from './pipeline_spec'; | ||||
| 
 | ||||
| export function main() { | ||||
|   describe('TextInterpolationParser', () => { | ||||
|     function createPipeline(ignoreBindings = false) { | ||||
|     function createPipeline() { | ||||
|       return new CompilePipeline([ | ||||
|         new MockStep((parent, current, control) => { current.ignoreBindings = ignoreBindings; }), | ||||
|         new IgnoreChildrenStep(), | ||||
|         new TextInterpolationParser(new Parser(new Lexer())) | ||||
|       ]); | ||||
|     } | ||||
| 
 | ||||
|     function process(element, ignoreBindings = false) { | ||||
|     function process(element) { | ||||
|       return ListWrapper.map( | ||||
|         createPipeline(ignoreBindings).process(element), | ||||
|         createPipeline().process(element), | ||||
|         (compileElement) => compileElement.inheritedElementBinder | ||||
|       ); | ||||
|     } | ||||
| @ -30,11 +26,6 @@ export function main() { | ||||
|       expect(elementBinder.textBindingIndices[bindingIndex]).toEqual(nodeIndex); | ||||
|     } | ||||
| 
 | ||||
|     it('should not look for text interpolation when ignoreBindings is true', () => { | ||||
|       var results = process(el('<div>{{expr1}}<span></span>{{expr2}}</div>'), true); | ||||
|       expect(results[0]).toEqual(null); | ||||
|     }); | ||||
| 
 | ||||
|     it('should find text interpolation in normal elements', () => { | ||||
|       var result = process(el('<div>{{expr1}}<span></span>{{expr2}}</div>'))[0]; | ||||
|       assertTextBinding(result, 0, 0, "{{expr1}}"); | ||||
| @ -69,14 +60,3 @@ export function main() { | ||||
|     }); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| class MockStep extends CompileStep { | ||||
|   processClosure:Function; | ||||
|   constructor(process) { | ||||
|     super(); | ||||
|     this.processClosure = process; | ||||
|   } | ||||
|   process(parent:CompileElement, current:CompileElement, control:CompileControl) { | ||||
|     this.processClosure(parent, current, control); | ||||
|   } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user