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;
|
_parent:CompileElement;
|
||||||
_results;
|
_results;
|
||||||
_additionalChildren;
|
_additionalChildren;
|
||||||
|
_ignoreCurrentElement: boolean;
|
||||||
|
|
||||||
constructor(steps) {
|
constructor(steps) {
|
||||||
this._steps = steps;
|
this._steps = steps;
|
||||||
this._currentStepIndex = 0;
|
this._currentStepIndex = 0;
|
||||||
|
@ -27,14 +29,21 @@ export class CompileControl {
|
||||||
var previousStepIndex = this._currentStepIndex;
|
var previousStepIndex = this._currentStepIndex;
|
||||||
var previousParent = this._parent;
|
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];
|
var step = this._steps[i];
|
||||||
this._parent = parent;
|
this._parent = parent;
|
||||||
this._currentStepIndex = i;
|
this._currentStepIndex = i;
|
||||||
step.process(parent, current, this);
|
step.process(parent, current, this);
|
||||||
parent = this._parent;
|
parent = this._parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this._ignoreCurrentElement) {
|
||||||
ListWrapper.push(results, current);
|
ListWrapper.push(results, current);
|
||||||
|
}
|
||||||
|
|
||||||
this._currentStepIndex = previousStepIndex;
|
this._currentStepIndex = previousStepIndex;
|
||||||
this._parent = previousParent;
|
this._parent = previousParent;
|
||||||
|
@ -55,4 +64,14 @@ export class CompileControl {
|
||||||
}
|
}
|
||||||
ListWrapper.push(this._additionalChildren, element);
|
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;
|
distanceToInheritedBinder:number;
|
||||||
inheritedElementBinder:ElementBinderBuilder;
|
inheritedElementBinder:ElementBinderBuilder;
|
||||||
compileChildren: boolean;
|
compileChildren: boolean;
|
||||||
ignoreBindings: boolean;
|
|
||||||
elementDescription: string; // e.g. '<div [class]="foo">' : used to provide context in case of error
|
elementDescription: string; // e.g. '<div [class]="foo">' : used to provide context in case of error
|
||||||
|
|
||||||
constructor(element, compilationUnit = '') {
|
constructor(element, compilationUnit = '') {
|
||||||
|
@ -34,8 +33,6 @@ export class CompileElement {
|
||||||
this.inheritedElementBinder = null;
|
this.inheritedElementBinder = null;
|
||||||
this.distanceToInheritedBinder = 0;
|
this.distanceToInheritedBinder = 0;
|
||||||
this.compileChildren = true;
|
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
|
// description is calculated here as compilation steps may change the element
|
||||||
var tplDesc = assertionsEnabled()? getElementDescription(element) : null;
|
var tplDesc = assertionsEnabled()? getElementDescription(element) : null;
|
||||||
if (compilationUnit !== '') {
|
if (compilationUnit !== '') {
|
||||||
|
|
|
@ -30,10 +30,6 @@ export class PropertyBindingParser extends CompileStep {
|
||||||
}
|
}
|
||||||
|
|
||||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||||
if (current.ignoreBindings) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var attrs = current.attrs();
|
var attrs = current.attrs();
|
||||||
var newAttrs = MapWrapper.create();
|
var newAttrs = MapWrapper.create();
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ export class TextInterpolationParser extends CompileStep {
|
||||||
}
|
}
|
||||||
|
|
||||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||||
if (!current.compileChildren || current.ignoreBindings) {
|
if (!current.compileChildren) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var element = current.element;
|
var element = current.element;
|
||||||
|
|
|
@ -23,12 +23,9 @@ export class ShadowDomCompileStep extends CompileStep {
|
||||||
}
|
}
|
||||||
|
|
||||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||||
if (current.ignoreBindings) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var tagName = DOM.tagName(current.element).toUpperCase();
|
var tagName = DOM.tagName(current.element).toUpperCase();
|
||||||
if (tagName == 'STYLE') {
|
if (tagName == 'STYLE') {
|
||||||
this._processStyleElement(current);
|
this._processStyleElement(current, control);
|
||||||
} else if (tagName == 'CONTENT') {
|
} else if (tagName == 'CONTENT') {
|
||||||
this._processContentElement(current);
|
this._processContentElement(current);
|
||||||
} else {
|
} else {
|
||||||
|
@ -39,17 +36,20 @@ export class ShadowDomCompileStep extends CompileStep {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_processStyleElement(current) {
|
_processStyleElement(current:CompileElement, control:CompileControl) {
|
||||||
current.ignoreBindings = true;
|
|
||||||
var stylePromise = this._shadowDomStrategy.processStyleElement(
|
var stylePromise = this._shadowDomStrategy.processStyleElement(
|
||||||
this._template.componentId, this._template.absUrl, current.element
|
this._template.componentId, this._template.absUrl, current.element
|
||||||
);
|
);
|
||||||
if (isPresent(stylePromise) && PromiseWrapper.isPromise(stylePromise)) {
|
if (isPresent(stylePromise) && PromiseWrapper.isPromise(stylePromise)) {
|
||||||
ListWrapper.push(this._subTaskPromises, 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()) {
|
if (this._shadowDomStrategy.hasNativeContentElement()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,19 @@ export function main() {
|
||||||
expect(results[3].inheritedElementBinder.parent).toBe(results[0].inheritedElementBinder);
|
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', () => {
|
describe('control.addParent', () => {
|
||||||
it('should report the new parent to the following processor and the result', () => {
|
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>');
|
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) {
|
function logEntry(log, parent, current) {
|
||||||
var parentId = '';
|
var parentId = '';
|
||||||
if (isPresent(parent)) {
|
if (isPresent(parent)) {
|
||||||
|
|
|
@ -11,10 +11,9 @@ var EMPTY_MAP = MapWrapper.create();
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('PropertyBindingParser', () => {
|
describe('PropertyBindingParser', () => {
|
||||||
function createPipeline(ignoreBindings = false, hasNestedProtoView = false) {
|
function createPipeline(hasNestedProtoView = false) {
|
||||||
return new CompilePipeline([
|
return new CompilePipeline([
|
||||||
new MockStep((parent, current, control) => {
|
new MockStep((parent, current, control) => {
|
||||||
current.ignoreBindings = ignoreBindings;
|
|
||||||
if (hasNestedProtoView) {
|
if (hasNestedProtoView) {
|
||||||
current.bindElement().bindNestedProtoView(el('<template></template>'));
|
current.bindElement().bindNestedProtoView(el('<template></template>'));
|
||||||
}
|
}
|
||||||
|
@ -22,18 +21,13 @@ export function main() {
|
||||||
new PropertyBindingParser(new Parser(new Lexer()))]);
|
new PropertyBindingParser(new Parser(new Lexer()))]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function process(element, ignoreBindings = false, hasNestedProtoView = false) {
|
function process(element, hasNestedProtoView = false) {
|
||||||
return ListWrapper.map(
|
return ListWrapper.map(
|
||||||
createPipeline(ignoreBindings, hasNestedProtoView).process(element),
|
createPipeline(hasNestedProtoView).process(element),
|
||||||
(compileElement) => compileElement.inheritedElementBinder
|
(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', () => {
|
it('should detect [] syntax', () => {
|
||||||
var results = process(el('<div [a]="b"></div>'));
|
var results = process(el('<div [a]="b"></div>'));
|
||||||
expect(MapWrapper.get(results[0].propertyBindings, 'a').source).toEqual('b');
|
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', () => {
|
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(results[0].variableBindings).toEqual(EMPTY_MAP);
|
||||||
expect(MapWrapper.get(results[0].nestedProtoView.variableBindings, 'washington')).toEqual('george');
|
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', () => {
|
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(results[0].variableBindings).toEqual(EMPTY_MAP);
|
||||||
expect(MapWrapper.get(results[0].nestedProtoView.variableBindings, 'washington')).toEqual('george');
|
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 {CompilePipeline} from 'angular2/src/render/dom/compiler/compile_pipeline';
|
||||||
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {Lexer, Parser} from 'angular2/change_detection';
|
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';
|
import {IgnoreChildrenStep} from './pipeline_spec';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('TextInterpolationParser', () => {
|
describe('TextInterpolationParser', () => {
|
||||||
function createPipeline(ignoreBindings = false) {
|
function createPipeline() {
|
||||||
return new CompilePipeline([
|
return new CompilePipeline([
|
||||||
new MockStep((parent, current, control) => { current.ignoreBindings = ignoreBindings; }),
|
|
||||||
new IgnoreChildrenStep(),
|
new IgnoreChildrenStep(),
|
||||||
new TextInterpolationParser(new Parser(new Lexer()))
|
new TextInterpolationParser(new Parser(new Lexer()))
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function process(element, ignoreBindings = false) {
|
function process(element) {
|
||||||
return ListWrapper.map(
|
return ListWrapper.map(
|
||||||
createPipeline(ignoreBindings).process(element),
|
createPipeline().process(element),
|
||||||
(compileElement) => compileElement.inheritedElementBinder
|
(compileElement) => compileElement.inheritedElementBinder
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -30,11 +26,6 @@ export function main() {
|
||||||
expect(elementBinder.textBindingIndices[bindingIndex]).toEqual(nodeIndex);
|
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', () => {
|
it('should find text interpolation in normal elements', () => {
|
||||||
var result = process(el('<div>{{expr1}}<span></span>{{expr2}}</div>'))[0];
|
var result = process(el('<div>{{expr1}}<span></span>{{expr2}}</div>'))[0];
|
||||||
assertTextBinding(result, 0, 0, "{{expr1}}");
|
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