feat: RendererV2 integration (#14469)
This commit is contained in:

committed by
Igor Minar

parent
b4d444a0a7
commit
bb9c7ae6e7
@ -228,16 +228,22 @@ export class Parse5DomAdapter extends DomAdapter {
|
||||
el.parent = null;
|
||||
return el;
|
||||
}
|
||||
insertBefore(el: any, node: any) {
|
||||
this.remove(node);
|
||||
treeAdapter.insertBefore(el.parent, node, el);
|
||||
}
|
||||
insertAllBefore(el: any, nodes: any) { nodes.forEach((n: any) => this.insertBefore(el, n)); }
|
||||
insertAfter(el: any, node: any) {
|
||||
if (el.nextSibling) {
|
||||
this.insertBefore(el.nextSibling, node);
|
||||
insertBefore(parent: any, ref: any, newNode: any) {
|
||||
this.remove(newNode);
|
||||
if (ref) {
|
||||
treeAdapter.insertBefore(parent, newNode, ref);
|
||||
} else {
|
||||
this.appendChild(el.parent, node);
|
||||
this.appendChild(parent, newNode);
|
||||
}
|
||||
}
|
||||
insertAllBefore(parent: any, ref: any, nodes: any) {
|
||||
nodes.forEach((n: any) => this.insertBefore(parent, ref, n));
|
||||
}
|
||||
insertAfter(parent: any, ref: any, node: any) {
|
||||
if (ref.nextSibling) {
|
||||
this.insertBefore(parent, ref.nextSibling, node);
|
||||
} else {
|
||||
this.appendChild(parent, node);
|
||||
}
|
||||
}
|
||||
setInnerHTML(el: any, value: any) {
|
||||
|
@ -21,3 +21,5 @@ export type RenderDebugInfo = typeof r._RenderDebugInfo;
|
||||
export const RenderDebugInfo: typeof r.RenderDebugInfo = r.RenderDebugInfo;
|
||||
export type DebugDomRootRenderer = typeof r._DebugDomRootRenderer;
|
||||
export const DebugDomRootRenderer: typeof r.DebugDomRootRenderer = r.DebugDomRootRenderer;
|
||||
export type DebugDomRendererV2 = typeof r._DebugDomRendererV2;
|
||||
export const DebugDomRendererV2: typeof r.DebugDomRendererV2 = r.DebugDomRendererV2;
|
||||
|
@ -8,16 +8,15 @@
|
||||
|
||||
import {PlatformLocation} from '@angular/common';
|
||||
import {platformCoreDynamic} from '@angular/compiler';
|
||||
import {Injectable, InjectionToken, Injector, NgModule, PLATFORM_INITIALIZER, PlatformRef, Provider, RootRenderer, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
|
||||
import {InjectionToken, Injector, NgModule, PLATFORM_INITIALIZER, PlatformRef, Provider, RENDERER_V2_DIRECT, RendererV2, RootRenderer, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
|
||||
import {BrowserModule, DOCUMENT} from '@angular/platform-browser';
|
||||
|
||||
import {ServerPlatformLocation} from './location';
|
||||
import {Parse5DomAdapter, parseDocument} from './parse5_adapter';
|
||||
import {PlatformState} from './platform_state';
|
||||
import {DebugDomRootRenderer} from './private_import_core';
|
||||
import {DebugDomRendererV2, DebugDomRootRenderer} from './private_import_core';
|
||||
import {SharedStylesHost, getDOM} from './private_import_platform-browser';
|
||||
import {ServerRootRenderer} from './server_renderer';
|
||||
|
||||
import {ServerRendererV2, ServerRootRenderer} from './server_renderer';
|
||||
|
||||
function notSupported(feature: string): Error {
|
||||
throw new Error(`platform-server does not support '${feature}'.`);
|
||||
@ -35,14 +34,16 @@ function initParse5Adapter(injector: Injector) {
|
||||
}
|
||||
|
||||
export function _createConditionalRootRenderer(rootRenderer: any) {
|
||||
if (isDevMode()) {
|
||||
return new DebugDomRootRenderer(rootRenderer);
|
||||
}
|
||||
return rootRenderer;
|
||||
return isDevMode() ? new DebugDomRootRenderer(rootRenderer) : rootRenderer;
|
||||
}
|
||||
|
||||
export function _createDebugRendererV2(renderer: RendererV2): RendererV2 {
|
||||
return isDevMode() ? new DebugDomRendererV2(renderer) : renderer;
|
||||
}
|
||||
|
||||
export const SERVER_RENDER_PROVIDERS: Provider[] = [
|
||||
ServerRootRenderer,
|
||||
ServerRootRenderer, {provide: RENDERER_V2_DIRECT, useClass: ServerRendererV2},
|
||||
{provide: RendererV2, useFactory: _createDebugRendererV2, deps: [RENDERER_V2_DIRECT]},
|
||||
{provide: RootRenderer, useFactory: _createConditionalRootRenderer, deps: [ServerRootRenderer]},
|
||||
// use plain SharedStylesHost, not the DomSharedStylesHost
|
||||
SharedStylesHost
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {APP_ID, Inject, Injectable, NgZone, RenderComponentType, Renderer, RootRenderer, ViewEncapsulation} from '@angular/core';
|
||||
import {APP_ID, Inject, Injectable, NgZone, RenderComponentType, Renderer, RendererV2, RootRenderer, ViewEncapsulation} from '@angular/core';
|
||||
import {AnimationDriver, DOCUMENT} from '@angular/platform-browser';
|
||||
|
||||
import {isBlank, isPresent, stringify} from './facade/lang';
|
||||
@ -17,7 +17,7 @@ const TEMPLATE_COMMENT_TEXT = 'template bindings={}';
|
||||
const TEMPLATE_BINDINGS_EXP = /^template bindings=(.*)$/;
|
||||
|
||||
@Injectable()
|
||||
export class ServerRootRenderer {
|
||||
export class ServerRootRenderer implements RootRenderer {
|
||||
protected registeredComponents: Map<string, ServerRenderer> = new Map<string, ServerRenderer>();
|
||||
constructor(
|
||||
@Inject(DOCUMENT) public document: any, public sharedStylesHost: SharedStylesHost,
|
||||
@ -213,13 +213,13 @@ export class ServerRenderer implements Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
function moveNodesAfterSibling(sibling: any /** TODO #9100 */, nodes: any /** TODO #9100 */) {
|
||||
const parent = getDOM().parentElement(sibling);
|
||||
if (nodes.length > 0 && isPresent(parent)) {
|
||||
const nextSibling = getDOM().nextSibling(sibling);
|
||||
if (isPresent(nextSibling)) {
|
||||
function moveNodesAfterSibling(ref: any, nodes: any) {
|
||||
const parent = getDOM().parentElement(ref);
|
||||
if (nodes.length > 0 && parent) {
|
||||
const nextSibling = getDOM().nextSibling(ref);
|
||||
if (nextSibling) {
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
getDOM().insertBefore(nextSibling, nodes[i]);
|
||||
getDOM().insertBefore(parent, nextSibling, nodes[i]);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
@ -229,18 +229,119 @@ function moveNodesAfterSibling(sibling: any /** TODO #9100 */, nodes: any /** TO
|
||||
}
|
||||
}
|
||||
|
||||
function appendNodes(parent: any /** TODO #9100 */, nodes: any /** TODO #9100 */) {
|
||||
function appendNodes(parent: any, nodes: any) {
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
getDOM().appendChild(parent, nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function decoratePreventDefault(eventHandler: Function): Function {
|
||||
return (event: any /** TODO #9100 */) => {
|
||||
const allowDefaultBehavior = eventHandler(event);
|
||||
if (allowDefaultBehavior === false) {
|
||||
// TODO(tbosch): move preventDefault into event plugins...
|
||||
getDOM().preventDefault(event);
|
||||
@Injectable()
|
||||
export class ServerRendererV2 implements RendererV2 {
|
||||
constructor(private ngZone: NgZone, @Inject(DOCUMENT) private document: any) {}
|
||||
|
||||
createElement(name: string, namespace?: string, debugInfo?: any): any {
|
||||
if (namespace) {
|
||||
return getDOM().createElementNS(NAMESPACE_URIS[namespace], name);
|
||||
}
|
||||
};
|
||||
|
||||
return getDOM().createElement(name);
|
||||
}
|
||||
|
||||
createComment(value: string, debugInfo?: any): any { return getDOM().createComment(value); }
|
||||
|
||||
createText(value: string, debugInfo?: any): any { return getDOM().createTextNode(value); }
|
||||
|
||||
appendChild(parent: any, newChild: any): void { getDOM().appendChild(parent, newChild); }
|
||||
|
||||
insertBefore(parent: any, newChild: any, refChild: any): void {
|
||||
if (parent) {
|
||||
getDOM().insertBefore(parent, refChild, newChild);
|
||||
}
|
||||
}
|
||||
|
||||
removeChild(parent: any, oldChild: any): void { getDOM().removeChild(parent, oldChild); }
|
||||
|
||||
selectRootElement(selectorOrNode: string|any, debugInfo?: any): any {
|
||||
let el: any;
|
||||
if (typeof selectorOrNode === 'string') {
|
||||
el = getDOM().querySelector(this.document, selectorOrNode);
|
||||
if (!el) {
|
||||
throw new Error(`The selector "${selectorOrNode}" did not match any elements`);
|
||||
}
|
||||
} else {
|
||||
el = selectorOrNode;
|
||||
}
|
||||
getDOM().clearNodes(el);
|
||||
return el;
|
||||
}
|
||||
|
||||
parentNode(node: any): any { return getDOM().parentElement(node); }
|
||||
|
||||
nextSibling(node: any): any { return getDOM().nextSibling(node); }
|
||||
|
||||
setAttribute(el: any, name: string, value: string, namespace?: string): void {
|
||||
if (namespace) {
|
||||
getDOM().setAttributeNS(el, NAMESPACE_URIS[namespace], namespace + ':' + name, value);
|
||||
} else {
|
||||
getDOM().setAttribute(el, name, value);
|
||||
}
|
||||
}
|
||||
|
||||
removeAttribute(el: any, name: string, namespace?: string): void {
|
||||
if (namespace) {
|
||||
getDOM().removeAttributeNS(el, NAMESPACE_URIS[namespace], name);
|
||||
} else {
|
||||
getDOM().removeAttribute(el, name);
|
||||
}
|
||||
}
|
||||
|
||||
setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void {
|
||||
if (getDOM().isCommentNode(el)) {
|
||||
const m = getDOM().getText(el).replace(/\n/g, '').match(TEMPLATE_BINDINGS_EXP);
|
||||
const obj = m === null ? {} : JSON.parse(m[1]);
|
||||
obj[propertyName] = propertyValue;
|
||||
getDOM().setText(el, TEMPLATE_COMMENT_TEXT.replace('{}', JSON.stringify(obj, null, 2)));
|
||||
} else {
|
||||
this.setAttribute(el, propertyName, propertyValue);
|
||||
}
|
||||
}
|
||||
|
||||
removeBindingDebugInfo(el: any, propertyName: string): void {
|
||||
if (getDOM().isCommentNode(el)) {
|
||||
const m = getDOM().getText(el).replace(/\n/g, '').match(TEMPLATE_BINDINGS_EXP);
|
||||
const obj = m === null ? {} : JSON.parse(m[1]);
|
||||
delete obj[propertyName];
|
||||
getDOM().setText(el, TEMPLATE_COMMENT_TEXT.replace('{}', JSON.stringify(obj, null, 2)));
|
||||
} else {
|
||||
this.removeAttribute(el, propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
addClass(el: any, name: string): void { getDOM().addClass(el, name); }
|
||||
|
||||
removeClass(el: any, name: string): void { getDOM().removeClass(el, name); }
|
||||
|
||||
setStyle(el: any, style: string, value: any, hasVendorPrefix: boolean, hasImportant: boolean):
|
||||
void {
|
||||
getDOM().setStyle(el, style, value);
|
||||
}
|
||||
|
||||
removeStyle(el: any, style: string, hasVendorPrefix: boolean): void {
|
||||
getDOM().removeStyle(el, style);
|
||||
}
|
||||
|
||||
setProperty(el: any, name: string, value: any): void { getDOM().setProperty(el, name, value); }
|
||||
|
||||
setText(node: any, value: string): void { getDOM().setText(node, value); }
|
||||
|
||||
listen(
|
||||
target: 'document'|'window'|'body'|any, eventName: string,
|
||||
callback: (event: any) => boolean): () => void {
|
||||
// Note: We are not using the EventsPlugin here as this is not needed
|
||||
// to run our tests.
|
||||
const el =
|
||||
typeof target === 'string' ? getDOM().getGlobalEventTarget(this.document, target) : target;
|
||||
const outsideHandler = (event: any) => this.ngZone.runGuarded(() => callback(event));
|
||||
return this.ngZone.runOutsideAngular(() => getDOM().onAndCancel(el, eventName, outsideHandler));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user