refactor(render): don’t store DOM nodes but store strings for big ProtoViews.
Also inserts comment nodes before/after projected nodes so that text nodes don’t get merged when we serialize/deserialize them. Closes #3356 First part of #3364
This commit is contained in:
@@ -1390,6 +1390,28 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
describe('different proto view storages', () => {
|
||||
function runWithMode(mode: string) {
|
||||
return inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyComp,
|
||||
new viewAnn.View({template: `<!--${mode}--><div>{{ctxProp}}</div>`}))
|
||||
.createAsync(MyComp)
|
||||
.then((rootTC) => {
|
||||
rootTC.componentInstance.ctxProp = 'Hello World!';
|
||||
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.nativeElement).toHaveText('Hello World!');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
it('should work with storing DOM nodes', runWithMode('cache'));
|
||||
|
||||
it('should work with serializing the DOM nodes', runWithMode('nocache'));
|
||||
});
|
||||
|
||||
// Disabled until a solution is found, refs:
|
||||
// - https://github.com/angular/angular/issues/776
|
||||
// - https://github.com/angular/angular/commit/81f3f32
|
||||
|
||||
@@ -34,6 +34,8 @@ import {SharedStylesHost} from 'angular2/src/render/dom/view/shared_styles_host'
|
||||
|
||||
import {MockStep} from './pipeline_spec';
|
||||
|
||||
import {ReferenceCloneableTemplate} from 'angular2/src/render/dom/util';
|
||||
|
||||
export function runCompilerCommonTests() {
|
||||
describe('DomCompiler', function() {
|
||||
var mockStepFactory: MockStepFactory;
|
||||
@@ -78,8 +80,7 @@ export function runCompilerCommonTests() {
|
||||
{id: 'id', selector: 'custom', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||
compiler.compileHost(dirMetadata)
|
||||
.then((protoView) => {
|
||||
expect(DOM.tagName(DOM.firstChild(DOM.content(
|
||||
resolveInternalDomProtoView(protoView.render).rootElement)))
|
||||
expect(DOM.tagName(DOM.firstChild(DOM.content(templateRoot(protoView))))
|
||||
.toLowerCase())
|
||||
.toEqual('custom');
|
||||
expect(mockStepFactory.viewDef.directives).toEqual([dirMetadata]);
|
||||
@@ -95,8 +96,7 @@ export function runCompilerCommonTests() {
|
||||
compiler.compile(
|
||||
new ViewDefinition({componentId: 'someId', template: 'inline component'}))
|
||||
.then((protoView) => {
|
||||
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).rootElement))
|
||||
.toEqual('inline component');
|
||||
expect(DOM.getInnerHTML(templateRoot(protoView))).toEqual('inline component');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -106,8 +106,7 @@ export function runCompilerCommonTests() {
|
||||
var compiler = createCompiler(EMPTY_STEP, null, urlData);
|
||||
compiler.compile(new ViewDefinition({componentId: 'someId', templateAbsUrl: 'someUrl'}))
|
||||
.then((protoView) => {
|
||||
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).rootElement))
|
||||
.toEqual('url component');
|
||||
expect(DOM.getInnerHTML(templateRoot(protoView))).toEqual('url component');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -173,8 +172,7 @@ export function runCompilerCommonTests() {
|
||||
encapsulation: ViewEncapsulation.NONE
|
||||
}))
|
||||
.then((protoViewDto) => {
|
||||
var domProtoView = resolveInternalDomProtoView(protoViewDto.render);
|
||||
expect(DOM.getInnerHTML(domProtoView.rootElement)).toEqual('');
|
||||
expect(DOM.getInnerHTML(templateRoot(protoViewDto))).toEqual('');
|
||||
expect(sharedStylesHost.getAllStyles()).toEqual(['a {};']);
|
||||
async.done();
|
||||
});
|
||||
@@ -190,8 +188,7 @@ export function runCompilerCommonTests() {
|
||||
encapsulation: ViewEncapsulation.EMULATED
|
||||
}))
|
||||
.then((protoViewDto) => {
|
||||
var domProtoView = resolveInternalDomProtoView(protoViewDto.render);
|
||||
expect(DOM.getInnerHTML(domProtoView.rootElement)).toEqual('');
|
||||
expect(DOM.getInnerHTML(templateRoot(protoViewDto))).toEqual('');
|
||||
expect(sharedStylesHost.getAllStyles()).toEqual(['a {};']);
|
||||
async.done();
|
||||
});
|
||||
@@ -208,8 +205,7 @@ export function runCompilerCommonTests() {
|
||||
encapsulation: ViewEncapsulation.NATIVE
|
||||
}))
|
||||
.then((protoViewDto) => {
|
||||
var domProtoView = resolveInternalDomProtoView(protoViewDto.render);
|
||||
expect(DOM.getInnerHTML(domProtoView.rootElement))
|
||||
expect(DOM.getInnerHTML(templateRoot(protoViewDto)))
|
||||
.toEqual('<style>a {};</style>');
|
||||
expect(sharedStylesHost.getAllStyles()).toEqual([]);
|
||||
async.done();
|
||||
@@ -259,6 +255,11 @@ export function runCompilerCommonTests() {
|
||||
});
|
||||
}
|
||||
|
||||
function templateRoot(protoViewDto: ProtoViewDto) {
|
||||
var pv = resolveInternalDomProtoView(protoViewDto.render);
|
||||
return (<ReferenceCloneableTemplate>pv.cloneableTemplate).templateRoot;
|
||||
}
|
||||
|
||||
class MockStepFactory extends CompileStepFactory {
|
||||
steps: List<CompileStep>;
|
||||
subTaskPromises: List<Promise<any>>;
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
beforeEachBindings,
|
||||
SpyObject,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {
|
||||
prepareTemplateForClone,
|
||||
ReferenceCloneableTemplate,
|
||||
SerializedCloneableTemplate
|
||||
} from 'angular2/src/render/dom/util';
|
||||
|
||||
export function main() {
|
||||
describe('Dom util', () => {
|
||||
|
||||
describe('prepareTemplateForClone', () => {
|
||||
it('should use a reference for small templates', () => {
|
||||
var t = DOM.createTemplate('');
|
||||
var ct = prepareTemplateForClone(t);
|
||||
expect((<ReferenceCloneableTemplate>ct).templateRoot).toBe(t);
|
||||
});
|
||||
|
||||
it('should use a reference for big templates with a force comment', () => {
|
||||
var templateString = '<!--cache-->';
|
||||
for (var i = 0; i < 100; i++) {
|
||||
templateString += '<div></div>';
|
||||
}
|
||||
var t = DOM.createTemplate(templateString);
|
||||
var ct = prepareTemplateForClone(t);
|
||||
expect((<ReferenceCloneableTemplate>ct).templateRoot).toBe(t);
|
||||
});
|
||||
|
||||
it('should serialize for big templates', () => {
|
||||
var templateString = '';
|
||||
for (var i = 0; i < 100; i++) {
|
||||
templateString += '<div></div>';
|
||||
}
|
||||
var t = DOM.createTemplate(templateString);
|
||||
var ct = prepareTemplateForClone(t);
|
||||
expect((<SerializedCloneableTemplate>ct).templateString).toEqual(templateString);
|
||||
});
|
||||
|
||||
it('should serialize for templates with the force comment', () => {
|
||||
var templateString = '<!--nocache-->';
|
||||
var t = DOM.createTemplate(templateString);
|
||||
var ct = prepareTemplateForClone(t);
|
||||
expect((<SerializedCloneableTemplate>ct).templateString).toEqual(templateString);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ReferenceCloneableTemplate', () => {
|
||||
it('should return template.content nodes (no import)', () => {
|
||||
var t = DOM.createTemplate('a');
|
||||
var ct = new ReferenceCloneableTemplate(t);
|
||||
var clone = ct.clone(false);
|
||||
expect(clone).not.toBe(DOM.content(t));
|
||||
expect(DOM.getText(DOM.firstChild(clone))).toEqual('a');
|
||||
});
|
||||
|
||||
it('should return template.content nodes (import into doc)', () => {
|
||||
var t = DOM.createTemplate('a');
|
||||
var ct = new ReferenceCloneableTemplate(t);
|
||||
var clone = ct.clone(true);
|
||||
expect(clone).not.toBe(DOM.content(t));
|
||||
expect(DOM.getText(DOM.firstChild(clone))).toEqual('a');
|
||||
});
|
||||
});
|
||||
|
||||
describe('SerializedCloneableTemplate', () => {
|
||||
it('should return template.content nodes (no import)', () => {
|
||||
var t = DOM.createTemplate('a');
|
||||
var ct = new SerializedCloneableTemplate(t);
|
||||
var clone = ct.clone(false);
|
||||
expect(clone).not.toBe(DOM.content(t));
|
||||
expect(DOM.getText(DOM.firstChild(clone))).toEqual('a');
|
||||
});
|
||||
|
||||
it('should return template.content nodes (import into doc)', () => {
|
||||
var t = DOM.createTemplate('a');
|
||||
var ct = new SerializedCloneableTemplate(t);
|
||||
var clone = ct.clone(true);
|
||||
expect(clone).not.toBe(DOM.content(t));
|
||||
expect(DOM.getText(DOM.firstChild(clone))).toEqual('a');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
xdescribe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
@@ -27,8 +28,8 @@ import {
|
||||
} from 'angular2/src/render/api';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {cloneAndQueryProtoView} from 'angular2/src/render/dom/util';
|
||||
import {resolveInternalDomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
import {cloneAndQueryProtoView, ReferenceCloneableTemplate} from 'angular2/src/render/dom/util';
|
||||
import {resolveInternalDomProtoView, DomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
import {ProtoViewBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
|
||||
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
|
||||
|
||||
@@ -90,23 +91,23 @@ export function main() {
|
||||
['<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"></a></root>']));
|
||||
|
||||
it('should project static text',
|
||||
runAndAssert(
|
||||
'root', ['<a>b</a>', 'A(<ng-content></ng-content>)'],
|
||||
['<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(b)</a></root>']));
|
||||
runAndAssert('root', ['<a>b</a>', 'A(<ng-content></ng-content>)'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<!--[-->b<!--]-->)</a></root>'
|
||||
]));
|
||||
|
||||
it('should project text interpolation',
|
||||
runAndAssert(
|
||||
'root', ['<a>{{b}}</a>', 'A(<ng-content></ng-content>)'],
|
||||
['<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A({0})</a></root>']));
|
||||
runAndAssert('root', ['<a>{{b}}</a>', 'A(<ng-content></ng-content>)'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<!--[-->{0}<!--]-->)</a></root>'
|
||||
]));
|
||||
|
||||
it('should project text interpolation to elements without bindings',
|
||||
runAndAssert('root', ['<a>{{b}}</a>', '<div><ng-content></ng-content></div>'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><div class="ng-binding">{0}</div></a></root>'
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><div class="ng-binding"><!--[-->{0}<!--]--></div></a></root>'
|
||||
]));
|
||||
|
||||
it('should project elements',
|
||||
runAndAssert('root', ['<a><div></div></a>', 'A(<ng-content></ng-content>)'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<div></div>)</a></root>'
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<!--[--><div></div><!--]-->)</a></root>'
|
||||
]));
|
||||
|
||||
it('should project elements using the selector',
|
||||
@@ -117,14 +118,26 @@ export function main() {
|
||||
'A(<ng-content select=".x"></ng-content>)'
|
||||
],
|
||||
[
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<div class="x">a</div><div class="x">b</div>)</a></root>'
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<!--[--><div class="x">a</div><div class="x">b</div><!--]-->)</a></root>'
|
||||
]));
|
||||
|
||||
it('should reproject',
|
||||
runAndAssert(
|
||||
'root',
|
||||
['<a>x</a>', 'A(<b><ng-content></ng-content></b>)', 'B(<ng-content></ng-content>)'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<b class="ng-binding" idx="2">B(x)</b>)</a></root>'
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<b class="ng-binding" idx="2">B(<!--[--><!--[-->x<!--]--><!--]-->)</b>)</a></root>'
|
||||
]));
|
||||
|
||||
it('should reproject text interpolation to sibling text nodes',
|
||||
runAndAssert(
|
||||
'root',
|
||||
[
|
||||
'<a>{{x}}</a>',
|
||||
'<b>A(<ng-content></ng-content>)</b>)',
|
||||
'B(<ng-content></ng-content>)'
|
||||
],
|
||||
[
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><b class="ng-binding" idx="2">B(<!--[-->A(<!--[-->{0}<!--]-->)<!--]-->)</b>)</a></root>'
|
||||
]));
|
||||
|
||||
it('should reproject by combining selectors',
|
||||
@@ -136,7 +149,7 @@ export function main() {
|
||||
'B(<ng-content select=".y"></ng-content>)'
|
||||
],
|
||||
[
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<b class="ng-binding" idx="2">B(<div class="x y"></div>)</b>)</a></root>'
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<b class="ng-binding" idx="2">B(<!--[--><div class="x y"></div><!--]-->)</b>)</a></root>'
|
||||
]));
|
||||
|
||||
it('should keep non projected embedded views as fragments (so that they can be moved manually)',
|
||||
@@ -147,14 +160,14 @@ export function main() {
|
||||
it('should project embedded views and match the template element',
|
||||
runAndAssert(
|
||||
'root', ['<a><template class="x">b</template></a>', 'A(<ng-content></ng-content>)'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<template class="x ng-binding" idx="2"></template>)</a></root>',
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<!--[--><template class="x ng-binding" idx="2"></template><!--]-->)</a></root>',
|
||||
'b'
|
||||
]));
|
||||
|
||||
it('should project nodes using the ng-content in embedded views',
|
||||
runAndAssert('root', ['<a>b</a>', 'A(<ng-content *ng-if></ng-content>)'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<template class="ng-binding" idx="2" ng-if=""></template>)</a></root>',
|
||||
'b'
|
||||
'<!--[-->b<!--]-->'
|
||||
]));
|
||||
|
||||
it('should allow to use wildcard selector after embedded view with non wildcard selector',
|
||||
@@ -165,8 +178,8 @@ export function main() {
|
||||
'A(<ng-content select=".x" *ng-if></ng-content>, <ng-content></ng-content>)'
|
||||
],
|
||||
[
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<template class="ng-binding" idx="2" ng-if=""></template>, b)</a></root>',
|
||||
'<div class="x">a</div>'
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<template class="ng-binding" idx="2" ng-if=""></template>, <!--[-->b<!--]-->)</a></root>',
|
||||
'<!--[--><div class="x">a</div><!--]-->'
|
||||
]));
|
||||
|
||||
});
|
||||
@@ -208,7 +221,7 @@ export function main() {
|
||||
'<ng-content select="c"></ng-content><ng-content select="b"></ng-content>'
|
||||
],
|
||||
[
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><c class="ng-binding" idx="3"></c><b class="ng-binding" idx="2"></b></a></root>'
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><!--[--><c class="ng-binding" idx="3"></c><!--]--><!--[--><b class="ng-binding" idx="2"></b><!--]--></a></root>'
|
||||
]));
|
||||
|
||||
});
|
||||
@@ -227,7 +240,7 @@ export function main() {
|
||||
'<ng-content select="[y]"></ng-content><ng-content select="[x]"></ng-content>'
|
||||
],
|
||||
[
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><div class="ng-binding" idx="3" y="">{1}</div><div class="ng-binding" idx="2" x="">{0}</div></a></root>'
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><!--[--><div class="ng-binding" idx="3" y="">{1}</div><!--]--><!--[--><div class="ng-binding" idx="2" x="">{0}</div><!--]--></a></root>'
|
||||
]));
|
||||
|
||||
});
|
||||
@@ -252,7 +265,7 @@ export function main() {
|
||||
tb.merge([rootProtoViewDto, componentProtoViewDto])
|
||||
.then(mergeMappings => {
|
||||
var domPv = resolveInternalDomProtoView(mergeMappings.mergedProtoViewRef);
|
||||
expect(DOM.getInnerHTML(domPv.rootElement))
|
||||
expect(DOM.getInnerHTML(templateRoot(domPv)))
|
||||
.toEqual('<root class="ng-binding" a="b"></root>');
|
||||
async.done();
|
||||
});
|
||||
@@ -264,6 +277,10 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
function templateRoot(pv: DomProtoView) {
|
||||
return (<ReferenceCloneableTemplate>pv.cloneableTemplate).templateRoot;
|
||||
}
|
||||
|
||||
function runAndAssert(hostElementName: string, componentTemplates: string[],
|
||||
expectedFragments: string[]) {
|
||||
var useNativeEncapsulation = hostElementName.startsWith('native-');
|
||||
|
||||
@@ -34,11 +34,12 @@ import {
|
||||
RenderViewWithFragmentsStore,
|
||||
WorkerRenderViewRef
|
||||
} from 'angular2/src/web-workers/shared/render_view_with_fragments_store';
|
||||
import {resolveInternalDomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
import {resolveInternalDomProtoView, DomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
import {someComponent} from '../../render/dom/dom_renderer_integration_spec';
|
||||
import {WebWorkerMain} from 'angular2/src/web-workers/ui/impl';
|
||||
import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
|
||||
import {MockMessageBus, MockMessageBusSink, MockMessageBusSource} from './worker_test_util';
|
||||
import {ReferenceCloneableTemplate} from 'angular2/src/render/dom/util';
|
||||
|
||||
export function main() {
|
||||
function createBroker(workerSerializer: Serializer, uiSerializer: Serializer, tb: DomTestbed,
|
||||
@@ -103,7 +104,7 @@ export function main() {
|
||||
compiler.compileHost(dirMetadata)
|
||||
.then((protoView) => {
|
||||
expect(DOM.tagName(DOM.firstChild(DOM.content(
|
||||
resolveWebWorkerRef(protoView.render).rootElement)))
|
||||
templateRoot(resolveWebWorkerRef(protoView.render)))))
|
||||
.toLowerCase())
|
||||
.toEqual('custom');
|
||||
expect(protoView).not.toBeNull();
|
||||
@@ -297,6 +298,10 @@ class WorkerTestRootView extends TestRootView {
|
||||
}
|
||||
}
|
||||
|
||||
function templateRoot(pv: DomProtoView) {
|
||||
return (<ReferenceCloneableTemplate>pv.cloneableTemplate).templateRoot;
|
||||
}
|
||||
|
||||
function createSerializer(protoViewRefStore: RenderProtoViewRefStore,
|
||||
renderViewStore: RenderViewWithFragmentsStore): Serializer {
|
||||
var injector = createTestInjector([
|
||||
|
||||
Reference in New Issue
Block a user