
Now, using `ng.probe(element)` in the browser console returns a DebugElement when in dev mode. `ComponentFixture#debugElement` also returns a new DebugElement. Breaking Change: This is a breaking change for unit tests. The API for the DebugElement has changed. Now, there is a DebugElement or DebugNode for every node in the DOM, not only nodes with an ElementRef. `componentViewChildren` is removed, and `childNodes` is a list of ElementNodes corresponding to every child in the DOM. `query` no longer takes a scope parameter, since the entire rendered DOM is included in the `childNodes`. Before: ``` componentFixture.debugElement.componentViewChildren[0]; ``` After ``` // Depending on the DOM structure of your component, the // index may have changed or the first component child // may be a sub-child. componentFixture.debugElement.children[0]; ``` Before: ``` debugElement.query(By.css('div'), Scope.all()); ``` After: ``` debugElement.query(By.css('div')); ``` Before: ``` componentFixture.debugElement.elementRef; ``` After: ``` componentFixture.elementRef; ```
286 lines
9.9 KiB
TypeScript
286 lines
9.9 KiB
TypeScript
import {
|
|
Renderer,
|
|
RootRenderer,
|
|
RenderComponentType,
|
|
RenderDebugInfo
|
|
} from 'angular2/src/core/render/api';
|
|
import {
|
|
ClientMessageBroker,
|
|
ClientMessageBrokerFactory,
|
|
FnArg,
|
|
UiArguments
|
|
} from "angular2/src/web_workers/shared/client_message_broker";
|
|
import {isPresent, isBlank, print} from "angular2/src/facade/lang";
|
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
|
import {Injectable} from "angular2/src/core/di";
|
|
import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
|
|
import {RENDERER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
|
import {Serializer, RenderStoreObject} from 'angular2/src/web_workers/shared/serializer';
|
|
import {EVENT_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
|
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
|
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
|
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
|
import {deserializeGenericEvent} from './event_deserializer';
|
|
|
|
@Injectable()
|
|
export class WebWorkerRootRenderer implements RootRenderer {
|
|
private _messageBroker;
|
|
public globalEvents: NamedEventEmitter = new NamedEventEmitter();
|
|
private _componentRenderers: Map<string, WebWorkerRenderer> =
|
|
new Map<string, WebWorkerRenderer>();
|
|
|
|
constructor(messageBrokerFactory: ClientMessageBrokerFactory, bus: MessageBus,
|
|
private _serializer: Serializer, private _renderStore: RenderStore) {
|
|
this._messageBroker = messageBrokerFactory.createMessageBroker(RENDERER_CHANNEL);
|
|
bus.initChannel(EVENT_CHANNEL);
|
|
var source = bus.from(EVENT_CHANNEL);
|
|
ObservableWrapper.subscribe(source, (message) => this._dispatchEvent(message));
|
|
}
|
|
|
|
private _dispatchEvent(message: {[key: string]: any}): void {
|
|
var eventName = message['eventName'];
|
|
var target = message['eventTarget'];
|
|
var event = deserializeGenericEvent(message['event']);
|
|
if (isPresent(target)) {
|
|
this.globalEvents.dispatchEvent(eventNameWithTarget(target, eventName), event);
|
|
} else {
|
|
var element =
|
|
<WebWorkerRenderNode>this._serializer.deserialize(message['element'], RenderStoreObject);
|
|
element.events.dispatchEvent(eventName, event);
|
|
}
|
|
}
|
|
|
|
renderComponent(componentType: RenderComponentType): Renderer {
|
|
var result = this._componentRenderers.get(componentType.id);
|
|
if (isBlank(result)) {
|
|
result = new WebWorkerRenderer(this, componentType);
|
|
this._componentRenderers.set(componentType.id, result);
|
|
var id = this._renderStore.allocateId();
|
|
this._renderStore.store(result, id);
|
|
this.runOnService('renderComponent', [
|
|
new FnArg(componentType, RenderComponentType),
|
|
new FnArg(result, RenderStoreObject),
|
|
]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
runOnService(fnName: string, fnArgs: FnArg[]) {
|
|
var args = new UiArguments(fnName, fnArgs);
|
|
this._messageBroker.runOnService(args, null);
|
|
}
|
|
|
|
allocateNode(): WebWorkerRenderNode {
|
|
var result = new WebWorkerRenderNode();
|
|
var id = this._renderStore.allocateId();
|
|
this._renderStore.store(result, id);
|
|
return result;
|
|
}
|
|
|
|
allocateId(): number { return this._renderStore.allocateId(); }
|
|
|
|
destroyNodes(nodes: any[]) {
|
|
for (var i = 0; i < nodes.length; i++) {
|
|
this._renderStore.remove(nodes[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
export class WebWorkerRenderer implements Renderer, RenderStoreObject {
|
|
constructor(private _rootRenderer: WebWorkerRootRenderer,
|
|
private _componentType: RenderComponentType) {}
|
|
|
|
renderComponent(componentType: RenderComponentType): Renderer {
|
|
return this._rootRenderer.renderComponent(componentType);
|
|
}
|
|
|
|
private _runOnService(fnName: string, fnArgs: FnArg[]) {
|
|
var fnArgsWithRenderer = [new FnArg(this, RenderStoreObject)].concat(fnArgs);
|
|
this._rootRenderer.runOnService(fnName, fnArgsWithRenderer);
|
|
}
|
|
|
|
selectRootElement(selector: string): any {
|
|
var node = this._rootRenderer.allocateNode();
|
|
this._runOnService('selectRootElement',
|
|
[new FnArg(selector, null), new FnArg(node, RenderStoreObject)]);
|
|
return node;
|
|
}
|
|
|
|
createElement(parentElement: any, name: string): any {
|
|
var node = this._rootRenderer.allocateNode();
|
|
this._runOnService('createElement', [
|
|
new FnArg(parentElement, RenderStoreObject),
|
|
new FnArg(name, null),
|
|
new FnArg(node, RenderStoreObject)
|
|
]);
|
|
return node;
|
|
}
|
|
|
|
createViewRoot(hostElement: any): any {
|
|
var viewRoot = this._componentType.encapsulation === ViewEncapsulation.Native ?
|
|
this._rootRenderer.allocateNode() :
|
|
hostElement;
|
|
this._runOnService(
|
|
'createViewRoot',
|
|
[new FnArg(hostElement, RenderStoreObject), new FnArg(viewRoot, RenderStoreObject)]);
|
|
return viewRoot;
|
|
}
|
|
|
|
createTemplateAnchor(parentElement: any): any {
|
|
var node = this._rootRenderer.allocateNode();
|
|
this._runOnService(
|
|
'createTemplateAnchor',
|
|
[new FnArg(parentElement, RenderStoreObject), new FnArg(node, RenderStoreObject)]);
|
|
return node;
|
|
}
|
|
|
|
createText(parentElement: any, value: string): any {
|
|
var node = this._rootRenderer.allocateNode();
|
|
this._runOnService('createText', [
|
|
new FnArg(parentElement, RenderStoreObject),
|
|
new FnArg(value, null),
|
|
new FnArg(node, RenderStoreObject)
|
|
]);
|
|
return node;
|
|
}
|
|
|
|
projectNodes(parentElement: any, nodes: any[]) {
|
|
this._runOnService(
|
|
'projectNodes',
|
|
[new FnArg(parentElement, RenderStoreObject), new FnArg(nodes, RenderStoreObject)]);
|
|
}
|
|
|
|
attachViewAfter(node: any, viewRootNodes: any[]) {
|
|
this._runOnService(
|
|
'attachViewAfter',
|
|
[new FnArg(node, RenderStoreObject), new FnArg(viewRootNodes, RenderStoreObject)]);
|
|
}
|
|
|
|
detachView(viewRootNodes: any[]) {
|
|
this._runOnService('detachView', [new FnArg(viewRootNodes, RenderStoreObject)]);
|
|
}
|
|
|
|
destroyView(hostElement: any, viewAllNodes: any[]) {
|
|
this._runOnService(
|
|
'destroyView',
|
|
[new FnArg(hostElement, RenderStoreObject), new FnArg(viewAllNodes, RenderStoreObject)]);
|
|
this._rootRenderer.destroyNodes(viewAllNodes);
|
|
}
|
|
|
|
setElementProperty(renderElement: any, propertyName: string, propertyValue: any) {
|
|
this._runOnService('setElementProperty', [
|
|
new FnArg(renderElement, RenderStoreObject),
|
|
new FnArg(propertyName, null),
|
|
new FnArg(propertyValue, null)
|
|
]);
|
|
}
|
|
|
|
setElementAttribute(renderElement: any, attributeName: string, attributeValue: string) {
|
|
this._runOnService('setElementAttribute', [
|
|
new FnArg(renderElement, RenderStoreObject),
|
|
new FnArg(attributeName, null),
|
|
new FnArg(attributeValue, null)
|
|
]);
|
|
}
|
|
|
|
setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string) {
|
|
this._runOnService('setBindingDebugInfo', [
|
|
new FnArg(renderElement, RenderStoreObject),
|
|
new FnArg(propertyName, null),
|
|
new FnArg(propertyValue, null)
|
|
]);
|
|
}
|
|
|
|
setElementDebugInfo(renderElement: any, info: RenderDebugInfo) {}
|
|
|
|
setElementClass(renderElement: any, className: string, isAdd: boolean) {
|
|
this._runOnService('setElementClass', [
|
|
new FnArg(renderElement, RenderStoreObject),
|
|
new FnArg(className, null),
|
|
new FnArg(isAdd, null)
|
|
]);
|
|
}
|
|
|
|
setElementStyle(renderElement: any, styleName: string, styleValue: string) {
|
|
this._runOnService('setElementStyle', [
|
|
new FnArg(renderElement, RenderStoreObject),
|
|
new FnArg(styleName, null),
|
|
new FnArg(styleValue, null)
|
|
]);
|
|
}
|
|
|
|
invokeElementMethod(renderElement: any, methodName: string, args: any[]) {
|
|
this._runOnService('invokeElementMethod', [
|
|
new FnArg(renderElement, RenderStoreObject),
|
|
new FnArg(methodName, null),
|
|
new FnArg(args, null)
|
|
]);
|
|
}
|
|
|
|
setText(renderNode: any, text: string) {
|
|
this._runOnService('setText',
|
|
[new FnArg(renderNode, RenderStoreObject), new FnArg(text, null)]);
|
|
}
|
|
|
|
listen(renderElement: WebWorkerRenderNode, name: string, callback: Function): Function {
|
|
renderElement.events.listen(name, callback);
|
|
var unlistenCallbackId = this._rootRenderer.allocateId();
|
|
this._runOnService('listen', [
|
|
new FnArg(renderElement, RenderStoreObject),
|
|
new FnArg(name, null),
|
|
new FnArg(unlistenCallbackId, null)
|
|
]);
|
|
return () => {
|
|
renderElement.events.unlisten(name, callback);
|
|
this._runOnService('listenDone', [new FnArg(unlistenCallbackId, null)]);
|
|
};
|
|
}
|
|
|
|
listenGlobal(target: string, name: string, callback: Function): Function {
|
|
this._rootRenderer.globalEvents.listen(eventNameWithTarget(target, name), callback);
|
|
var unlistenCallbackId = this._rootRenderer.allocateId();
|
|
this._runOnService(
|
|
'listenGlobal',
|
|
[new FnArg(target, null), new FnArg(name, null), new FnArg(unlistenCallbackId, null)]);
|
|
return () => {
|
|
this._rootRenderer.globalEvents.unlisten(eventNameWithTarget(target, name), callback);
|
|
this._runOnService('listenDone', [new FnArg(unlistenCallbackId, null)]);
|
|
};
|
|
}
|
|
}
|
|
|
|
export class NamedEventEmitter {
|
|
private _listeners: Map<string, Function[]>;
|
|
|
|
private _getListeners(eventName: string): Function[] {
|
|
if (isBlank(this._listeners)) {
|
|
this._listeners = new Map<string, Function[]>();
|
|
}
|
|
var listeners = this._listeners.get(eventName);
|
|
if (isBlank(listeners)) {
|
|
listeners = [];
|
|
this._listeners.set(eventName, listeners);
|
|
}
|
|
return listeners;
|
|
}
|
|
|
|
listen(eventName: string, callback: Function) { this._getListeners(eventName).push(callback); }
|
|
|
|
unlisten(eventName: string, callback: Function) {
|
|
ListWrapper.remove(this._getListeners(eventName), callback);
|
|
}
|
|
|
|
dispatchEvent(eventName: string, event: any) {
|
|
var listeners = this._getListeners(eventName);
|
|
for (var i = 0; i < listeners.length; i++) {
|
|
listeners[i](event);
|
|
}
|
|
}
|
|
}
|
|
|
|
function eventNameWithTarget(target: string, eventName: string): string {
|
|
return `${target}:${eventName}`;
|
|
}
|
|
|
|
export class WebWorkerRenderNode { events: NamedEventEmitter = new NamedEventEmitter(); }
|