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;
|
||||
}
|
||||
ListWrapper.push(results, current);
|
||||
|
||||
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)) {
|
||||
|
@ -223,4 +245,4 @@ function resultIdLog(result) {
|
|||
logEntry(idLog, null, current);
|
||||
});
|
||||
return idLog;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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…
Reference in New Issue