Julie Ralph e1bf3d33f8 feat(debug): replace DebugElement with new Debug DOM
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;
```
2016-01-29 11:28:10 -08:00

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(); }