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; ```
This commit is contained in:
@ -15,7 +15,6 @@ import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_
|
||||
import {AppViewManager} from './linker/view_manager';
|
||||
import {AppViewManager_} from "./linker/view_manager";
|
||||
import {ViewResolver} from './linker/view_resolver';
|
||||
import {AppViewListener} from './linker/view_listener';
|
||||
import {DirectiveResolver} from './linker/directive_resolver';
|
||||
import {PipeResolver} from './linker/pipe_resolver';
|
||||
import {Compiler} from './linker/compiler';
|
||||
@ -32,7 +31,6 @@ export const APPLICATION_COMMON_PROVIDERS: Array<Type | Provider | any[]> = CONS
|
||||
APP_ID_RANDOM_PROVIDER,
|
||||
ResolvedMetadataCache,
|
||||
new Provider(AppViewManager, {useClass: AppViewManager_}),
|
||||
AppViewListener,
|
||||
ViewResolver,
|
||||
new Provider(IterableDiffers, {useValue: defaultIterableDiffers}),
|
||||
new Provider(KeyValueDiffers, {useValue: defaultKeyValueDiffers}),
|
||||
|
@ -1,248 +0,0 @@
|
||||
import {Type, isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, MapWrapper, Predicate} from 'angular2/src/facade/collection';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
|
||||
import {AppElement} from 'angular2/src/core/linker/element';
|
||||
import {AppView} from 'angular2/src/core/linker/view';
|
||||
import {ElementRef, ElementRef_} from 'angular2/src/core/linker/element_ref';
|
||||
|
||||
/**
|
||||
* A DebugElement contains information from the Angular compiler about an
|
||||
* element and provides access to the corresponding ElementInjector and
|
||||
* underlying DOM Element, as well as a way to query for children.
|
||||
*
|
||||
* A DebugElement can be obtained from a {@link ComponentFixture} or from an
|
||||
* {@link ElementRef} via {@link inspectElement}.
|
||||
*/
|
||||
export abstract class DebugElement {
|
||||
/**
|
||||
* Return the instance of the component associated with this element, if any.
|
||||
*/
|
||||
get componentInstance(): any { return unimplemented(); };
|
||||
|
||||
/**
|
||||
* Return the native HTML element for this DebugElement.
|
||||
*/
|
||||
get nativeElement(): any { return unimplemented(); };
|
||||
|
||||
/**
|
||||
* Return an Angular {@link ElementRef} for this element.
|
||||
*/
|
||||
get elementRef(): ElementRef { return unimplemented(); };
|
||||
|
||||
/**
|
||||
* Get the directive active for this element with the given index, if any.
|
||||
*/
|
||||
abstract getDirectiveInstance(directiveIndex: number): any;
|
||||
|
||||
/**
|
||||
* Get child DebugElements from within the Light DOM.
|
||||
*
|
||||
* @return {DebugElement[]}
|
||||
*/
|
||||
get children(): DebugElement[] { return unimplemented(); };
|
||||
|
||||
/**
|
||||
* Get the root DebugElement children of a component. Returns an empty
|
||||
* list if the current DebugElement is not a component root.
|
||||
*
|
||||
* @return {DebugElement[]}
|
||||
*/
|
||||
get componentViewChildren(): DebugElement[] { return unimplemented(); };
|
||||
|
||||
/**
|
||||
* Simulate an event from this element as if the user had caused
|
||||
* this event to fire from the page.
|
||||
*/
|
||||
abstract triggerEventHandler(eventName: string, eventObj: Event): void;
|
||||
|
||||
/**
|
||||
* Check whether the element has a directive with the given type.
|
||||
*/
|
||||
abstract hasDirective(type: Type): boolean;
|
||||
|
||||
/**
|
||||
* Inject the given type from the element injector.
|
||||
*/
|
||||
abstract inject(type: Type): any;
|
||||
|
||||
|
||||
/**
|
||||
* Read a local variable from the element (e.g. one defined with `#variable`).
|
||||
*/
|
||||
abstract getLocal(name: string): any;
|
||||
|
||||
/**
|
||||
* Return the first descendant TestElement matching the given predicate
|
||||
* and scope.
|
||||
*
|
||||
* @param {Function: boolean} predicate
|
||||
* @param {Scope} scope
|
||||
*
|
||||
* @return {DebugElement}
|
||||
*/
|
||||
query(predicate: Predicate<DebugElement>, scope: Function = Scope.all): DebugElement {
|
||||
var results = this.queryAll(predicate, scope);
|
||||
return results.length > 0 ? results[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return descendant TestElememts matching the given predicate
|
||||
* and scope.
|
||||
*
|
||||
* @param {Function: boolean} predicate
|
||||
* @param {Scope} scope
|
||||
*
|
||||
* @return {DebugElement[]}
|
||||
*/
|
||||
queryAll(predicate: Predicate<DebugElement>, scope: Function = Scope.all): DebugElement[] {
|
||||
var elementsInScope: any[] = scope(this);
|
||||
|
||||
return elementsInScope.filter(predicate);
|
||||
}
|
||||
}
|
||||
|
||||
export class DebugElement_ extends DebugElement {
|
||||
constructor(private _appElement: AppElement) { super(); }
|
||||
|
||||
get componentInstance(): any {
|
||||
if (!isPresent(this._appElement)) {
|
||||
return null;
|
||||
}
|
||||
return this._appElement.getComponent();
|
||||
}
|
||||
|
||||
get nativeElement(): any { return this.elementRef.nativeElement; }
|
||||
|
||||
get elementRef(): ElementRef { return this._appElement.ref; }
|
||||
|
||||
getDirectiveInstance(directiveIndex: number): any {
|
||||
return this._appElement.getDirectiveAtIndex(directiveIndex);
|
||||
}
|
||||
|
||||
get children(): DebugElement[] {
|
||||
return this._getChildElements(this._appElement.parentView, this._appElement);
|
||||
}
|
||||
|
||||
get componentViewChildren(): DebugElement[] {
|
||||
if (!isPresent(this._appElement.componentView)) {
|
||||
// The current element is not a component.
|
||||
return [];
|
||||
}
|
||||
|
||||
return this._getChildElements(this._appElement.componentView, null);
|
||||
}
|
||||
|
||||
triggerEventHandler(eventName: string, eventObj: Event): void {
|
||||
this._appElement.parentView.triggerEventHandlers(eventName, eventObj,
|
||||
this._appElement.proto.index);
|
||||
}
|
||||
|
||||
hasDirective(type: Type): boolean {
|
||||
if (!isPresent(this._appElement)) {
|
||||
return false;
|
||||
}
|
||||
return this._appElement.hasDirective(type);
|
||||
}
|
||||
|
||||
inject(type: Type): any {
|
||||
if (!isPresent(this._appElement)) {
|
||||
return null;
|
||||
}
|
||||
return this._appElement.get(type);
|
||||
}
|
||||
|
||||
getLocal(name: string): any { return this._appElement.parentView.locals.get(name); }
|
||||
|
||||
/** @internal */
|
||||
_getChildElements(view: AppView, parentAppElement: AppElement): DebugElement[] {
|
||||
var els = [];
|
||||
for (var i = 0; i < view.appElements.length; ++i) {
|
||||
var appEl = view.appElements[i];
|
||||
if (appEl.parent == parentAppElement) {
|
||||
els.push(new DebugElement_(appEl));
|
||||
|
||||
var views = appEl.nestedViews;
|
||||
if (isPresent(views)) {
|
||||
views.forEach(
|
||||
(nextView) => { els = els.concat(this._getChildElements(nextView, null)); });
|
||||
}
|
||||
}
|
||||
}
|
||||
return els;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link DebugElement} for an {@link ElementRef}.
|
||||
*
|
||||
* @param {ElementRef}: elementRef
|
||||
* @return {DebugElement}
|
||||
*/
|
||||
export function inspectElement(elementRef: ElementRef): DebugElement {
|
||||
return new DebugElement_((<ElementRef_>elementRef).internalElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps an array of {@link DebugElement}s to an array of native DOM elements.
|
||||
*/
|
||||
export function asNativeElements(arr: DebugElement[]): any[] {
|
||||
return arr.map((debugEl) => debugEl.nativeElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of scope functions used with {@link DebugElement}'s query functionality.
|
||||
*/
|
||||
export class Scope {
|
||||
/**
|
||||
* Scope queries to both the light dom and view of an element and its
|
||||
* children.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* {@example core/debug/ts/debug_element/debug_element.ts region='scope_all'}
|
||||
*/
|
||||
static all(debugElement: DebugElement): DebugElement[] {
|
||||
var scope = [];
|
||||
scope.push(debugElement);
|
||||
|
||||
debugElement.children.forEach(child => scope = scope.concat(Scope.all(child)));
|
||||
|
||||
debugElement.componentViewChildren.forEach(child => scope = scope.concat(Scope.all(child)));
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope queries to the light dom of an element and its children.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* {@example core/debug/ts/debug_element/debug_element.ts region='scope_light'}
|
||||
*/
|
||||
static light(debugElement: DebugElement): DebugElement[] {
|
||||
var scope = [];
|
||||
debugElement.children.forEach(child => {
|
||||
scope.push(child);
|
||||
scope = scope.concat(Scope.light(child));
|
||||
});
|
||||
return scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope queries to the view of an element of its children.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* {@example core/debug/ts/debug_element/debug_element.ts region='scope_view'}
|
||||
*/
|
||||
static view(debugElement: DebugElement): DebugElement[] {
|
||||
var scope = [];
|
||||
|
||||
debugElement.componentViewChildren.forEach(child => {
|
||||
scope.push(child);
|
||||
scope = scope.concat(Scope.light(child));
|
||||
});
|
||||
return scope;
|
||||
}
|
||||
}
|
171
modules/angular2/src/core/debug/debug_node.ts
Normal file
171
modules/angular2/src/core/debug/debug_node.ts
Normal file
@ -0,0 +1,171 @@
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {Predicate} from 'angular2/src/facade/collection';
|
||||
import {Injector} from 'angular2/src/core/di';
|
||||
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {RenderDebugInfo} from 'angular2/src/core/render/api';
|
||||
|
||||
export class EventListener { constructor(public name: string, public callback: Function){}; }
|
||||
|
||||
export class DebugNode {
|
||||
nativeNode: any;
|
||||
listeners: EventListener[];
|
||||
parent: DebugElement;
|
||||
providerTokens: any[];
|
||||
locals: Map<string, any>;
|
||||
injector: Injector;
|
||||
componentInstance: any;
|
||||
|
||||
constructor(nativeNode: any, parent: DebugNode) {
|
||||
this.nativeNode = nativeNode;
|
||||
if (isPresent(parent) && parent instanceof DebugElement) {
|
||||
parent.addChild(this);
|
||||
} else {
|
||||
this.parent = null;
|
||||
}
|
||||
this.listeners = [];
|
||||
this.providerTokens = [];
|
||||
}
|
||||
|
||||
setDebugInfo(info: RenderDebugInfo) {
|
||||
this.injector = info.injector;
|
||||
this.providerTokens = info.providerTokens;
|
||||
this.locals = info.locals;
|
||||
this.componentInstance = info.component;
|
||||
}
|
||||
|
||||
inject(token: any): any { return this.injector.get(token); }
|
||||
|
||||
getLocal(name: string): any { return this.locals.get(name); }
|
||||
}
|
||||
|
||||
export class DebugElement extends DebugNode {
|
||||
name: string;
|
||||
properties: Map<string, any>;
|
||||
attributes: Map<string, any>;
|
||||
childNodes: DebugNode[];
|
||||
nativeElement: any;
|
||||
|
||||
constructor(nativeNode: any, parent: any) {
|
||||
super(nativeNode, parent);
|
||||
this.properties = new Map<string, any>();
|
||||
this.attributes = new Map<string, any>();
|
||||
this.childNodes = [];
|
||||
this.nativeElement = nativeNode;
|
||||
}
|
||||
|
||||
addChild(child: DebugNode) {
|
||||
if (isPresent(child)) {
|
||||
this.childNodes.push(child);
|
||||
child.parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
removeChild(child: DebugNode) {
|
||||
var childIndex = this.childNodes.indexOf(child);
|
||||
if (childIndex !== -1) {
|
||||
child.parent = null;
|
||||
this.childNodes.splice(childIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
insertChildrenAfter(child: DebugNode, newChildren: DebugNode[]) {
|
||||
var siblingIndex = this.childNodes.indexOf(child);
|
||||
if (siblingIndex !== -1) {
|
||||
var previousChildren = this.childNodes.slice(0, siblingIndex + 1);
|
||||
var nextChildren = this.childNodes.slice(siblingIndex + 1);
|
||||
this.childNodes =
|
||||
ListWrapper.concat(ListWrapper.concat(previousChildren, newChildren), nextChildren);
|
||||
for (var i = 0; i < newChildren.length; ++i) {
|
||||
var newChild = newChildren[i];
|
||||
if (isPresent(newChild.parent)) {
|
||||
newChild.parent.removeChild(newChild);
|
||||
}
|
||||
newChild.parent = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query(predicate: Predicate<DebugElement>): DebugElement {
|
||||
var results = this.queryAll(predicate);
|
||||
return results.length > 0 ? results[0] : null;
|
||||
}
|
||||
|
||||
queryAll(predicate: Predicate<DebugElement>): DebugElement[] {
|
||||
var matches = [];
|
||||
_queryElementChildren(this, predicate, matches);
|
||||
return matches;
|
||||
}
|
||||
|
||||
queryAllNodes(predicate: Predicate<DebugNode>): DebugNode[] {
|
||||
var matches = [];
|
||||
_queryNodeChildren(this, predicate, matches);
|
||||
return matches;
|
||||
}
|
||||
|
||||
get children(): DebugElement[] {
|
||||
var children = [];
|
||||
this.childNodes.forEach((node) => {
|
||||
if (node instanceof DebugElement) {
|
||||
children.push(node);
|
||||
}
|
||||
});
|
||||
return children;
|
||||
}
|
||||
|
||||
triggerEventHandler(eventName: string, eventObj: Event) {
|
||||
this.listeners.forEach((listener) => {
|
||||
if (listener.name == eventName) {
|
||||
listener.callback(eventObj);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function asNativeElements(debugEls: DebugElement[]): any {
|
||||
return debugEls.map((el) => el.nativeElement);
|
||||
}
|
||||
|
||||
function _queryElementChildren(element: DebugElement, predicate: Predicate<DebugElement>,
|
||||
matches: DebugElement[]) {
|
||||
element.childNodes.forEach(node => {
|
||||
if (node instanceof DebugElement) {
|
||||
if (predicate(node)) {
|
||||
matches.push(node);
|
||||
}
|
||||
_queryElementChildren(node, predicate, matches);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _queryNodeChildren(parentNode: DebugNode, predicate: Predicate<DebugNode>,
|
||||
matches: DebugNode[]) {
|
||||
if (parentNode instanceof DebugElement) {
|
||||
parentNode.childNodes.forEach(node => {
|
||||
if (predicate(node)) {
|
||||
matches.push(node);
|
||||
}
|
||||
if (node instanceof DebugElement) {
|
||||
_queryNodeChildren(node, predicate, matches);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Need to keep the nodes in a global Map so that multiple angular apps are supported.
|
||||
var _nativeNodeToDebugNode = new Map<any, DebugNode>();
|
||||
|
||||
export function getDebugNode(nativeNode: any): DebugNode {
|
||||
return _nativeNodeToDebugNode.get(nativeNode);
|
||||
}
|
||||
|
||||
export function getAllDebugNodes(): DebugNode[] {
|
||||
return MapWrapper.values(_nativeNodeToDebugNode);
|
||||
}
|
||||
|
||||
export function indexDebugNode(node: DebugNode) {
|
||||
_nativeNodeToDebugNode.set(node.nativeNode, node);
|
||||
}
|
||||
|
||||
export function removeDebugNodeFromIndex(node: DebugNode) {
|
||||
_nativeNodeToDebugNode.delete(node.nativeNode);
|
||||
}
|
157
modules/angular2/src/core/debug/debug_renderer.ts
Normal file
157
modules/angular2/src/core/debug/debug_renderer.ts
Normal file
@ -0,0 +1,157 @@
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
Renderer,
|
||||
RootRenderer,
|
||||
RenderComponentType,
|
||||
RenderDebugInfo
|
||||
} from 'angular2/src/core/render/api';
|
||||
import {
|
||||
DebugNode,
|
||||
DebugElement,
|
||||
EventListener,
|
||||
getDebugNode,
|
||||
indexDebugNode,
|
||||
removeDebugNodeFromIndex
|
||||
} from 'angular2/src/core/debug/debug_node';
|
||||
|
||||
export class DebugDomRootRenderer implements RootRenderer {
|
||||
constructor(private _delegate: RootRenderer) {}
|
||||
|
||||
renderComponent(componentProto: RenderComponentType): Renderer {
|
||||
return new DebugDomRenderer(this, this._delegate.renderComponent(componentProto));
|
||||
}
|
||||
}
|
||||
|
||||
export class DebugDomRenderer implements Renderer {
|
||||
constructor(private _rootRenderer: DebugDomRootRenderer, private _delegate: Renderer) {}
|
||||
|
||||
renderComponent(componentType: RenderComponentType): Renderer {
|
||||
return this._rootRenderer.renderComponent(componentType);
|
||||
}
|
||||
|
||||
selectRootElement(selector: string): any {
|
||||
var nativeEl = this._delegate.selectRootElement(selector);
|
||||
var debugEl = new DebugElement(nativeEl, null);
|
||||
indexDebugNode(debugEl);
|
||||
return nativeEl;
|
||||
}
|
||||
|
||||
createElement(parentElement: any, name: string): any {
|
||||
var nativeEl = this._delegate.createElement(parentElement, name);
|
||||
var debugEl = new DebugElement(nativeEl, getDebugNode(parentElement));
|
||||
debugEl.name = name;
|
||||
indexDebugNode(debugEl);
|
||||
return nativeEl;
|
||||
}
|
||||
|
||||
createViewRoot(hostElement: any): any { return this._delegate.createViewRoot(hostElement); }
|
||||
|
||||
createTemplateAnchor(parentElement: any): any {
|
||||
var comment = this._delegate.createTemplateAnchor(parentElement);
|
||||
var debugEl = new DebugNode(comment, getDebugNode(parentElement));
|
||||
indexDebugNode(debugEl);
|
||||
return comment;
|
||||
}
|
||||
|
||||
createText(parentElement: any, value: string): any {
|
||||
var text = this._delegate.createText(parentElement, value);
|
||||
var debugEl = new DebugNode(text, getDebugNode(parentElement));
|
||||
indexDebugNode(debugEl);
|
||||
return text;
|
||||
}
|
||||
|
||||
projectNodes(parentElement: any, nodes: any[]) {
|
||||
var debugParent = getDebugNode(parentElement);
|
||||
if (isPresent(debugParent) && debugParent instanceof DebugElement) {
|
||||
nodes.forEach((node) => { debugParent.addChild(getDebugNode(node)); });
|
||||
}
|
||||
return this._delegate.projectNodes(parentElement, nodes);
|
||||
}
|
||||
|
||||
attachViewAfter(node: any, viewRootNodes: any[]) {
|
||||
var debugNode = getDebugNode(node);
|
||||
if (isPresent(debugNode)) {
|
||||
var debugParent = debugNode.parent;
|
||||
if (viewRootNodes.length > 0 && isPresent(debugParent)) {
|
||||
var debugViewRootNodes = [];
|
||||
viewRootNodes.forEach((rootNode) => debugViewRootNodes.push(getDebugNode(rootNode)));
|
||||
debugParent.insertChildrenAfter(debugNode, debugViewRootNodes);
|
||||
}
|
||||
}
|
||||
return this._delegate.attachViewAfter(node, viewRootNodes);
|
||||
}
|
||||
|
||||
detachView(viewRootNodes: any[]) {
|
||||
viewRootNodes.forEach((node) => {
|
||||
var debugNode = getDebugNode(node);
|
||||
if (isPresent(debugNode) && isPresent(debugNode.parent)) {
|
||||
debugNode.parent.removeChild(debugNode);
|
||||
}
|
||||
});
|
||||
return this._delegate.detachView(viewRootNodes);
|
||||
}
|
||||
|
||||
destroyView(hostElement: any, viewAllNodes: any[]) {
|
||||
viewAllNodes.forEach((node) => { removeDebugNodeFromIndex(getDebugNode(node)); });
|
||||
return this._delegate.destroyView(hostElement, viewAllNodes);
|
||||
}
|
||||
|
||||
listen(renderElement: any, name: string, callback: Function) {
|
||||
var debugEl = getDebugNode(renderElement);
|
||||
if (isPresent(debugEl)) {
|
||||
debugEl.listeners.push(new EventListener(name, callback));
|
||||
}
|
||||
return this._delegate.listen(renderElement, name, callback);
|
||||
}
|
||||
|
||||
listenGlobal(target: string, name: string, callback: Function): Function {
|
||||
return this._delegate.listenGlobal(target, name, callback);
|
||||
}
|
||||
|
||||
setElementProperty(renderElement: any, propertyName: string, propertyValue: any) {
|
||||
var debugEl = getDebugNode(renderElement);
|
||||
if (isPresent(debugEl) && debugEl instanceof DebugElement) {
|
||||
debugEl.properties.set(propertyName, propertyValue);
|
||||
}
|
||||
return this._delegate.setElementProperty(renderElement, propertyName, propertyValue);
|
||||
}
|
||||
|
||||
setElementAttribute(renderElement: any, attributeName: string, attributeValue: string) {
|
||||
var debugEl = getDebugNode(renderElement);
|
||||
if (isPresent(debugEl) && debugEl instanceof DebugElement) {
|
||||
debugEl.attributes.set(attributeName, attributeValue);
|
||||
}
|
||||
return this._delegate.setElementAttribute(renderElement, attributeName, attributeValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used only in debug mode to serialize property changes to comment nodes,
|
||||
* such as <template> placeholders.
|
||||
*/
|
||||
setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string) {
|
||||
return this._delegate.setBindingDebugInfo(renderElement, propertyName, propertyValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used only in development mode to set information needed by the DebugNode for this element.
|
||||
*/
|
||||
setElementDebugInfo(renderElement: any, info: RenderDebugInfo) {
|
||||
var debugEl = getDebugNode(renderElement);
|
||||
debugEl.setDebugInfo(info);
|
||||
return this._delegate.setElementDebugInfo(renderElement, info);
|
||||
}
|
||||
|
||||
setElementClass(renderElement: any, className: string, isAdd: boolean) {
|
||||
return this._delegate.setElementClass(renderElement, className, isAdd);
|
||||
}
|
||||
|
||||
setElementStyle(renderElement: any, styleName: string, styleValue: string) {
|
||||
return this._delegate.setElementStyle(renderElement, styleName, styleValue);
|
||||
}
|
||||
|
||||
invokeElementMethod(renderElement: any, methodName: string, args: any[]) {
|
||||
return this._delegate.invokeElementMethod(renderElement, methodName, args);
|
||||
}
|
||||
|
||||
setText(renderNode: any, text: string) { return this._delegate.setText(renderNode, text); }
|
||||
}
|
@ -27,7 +27,7 @@ import {
|
||||
CONST_EXPR
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {Renderer, RootRenderer} from 'angular2/src/core/render/api';
|
||||
import {Renderer, RootRenderer, RenderDebugInfo} from 'angular2/src/core/render/api';
|
||||
import {ViewRef_, HostViewFactoryRef} from './view_ref';
|
||||
import {ProtoPipes} from 'angular2/src/core/pipes/pipes';
|
||||
import {camelCaseToDashCase} from 'angular2/src/core/render/util';
|
||||
@ -119,6 +119,12 @@ export class AppView implements ChangeDispatcher {
|
||||
(templateName, _) => { localsMap.set(templateName, null); });
|
||||
for (var i = 0; i < appElements.length; i++) {
|
||||
var appEl = appElements[i];
|
||||
var providerTokens = [];
|
||||
if (isPresent(appEl.proto.protoInjector)) {
|
||||
for (var j = 0; j < appEl.proto.protoInjector.numberOfProviders; j++) {
|
||||
providerTokens.push(appEl.proto.protoInjector.getProviderAtIndex(j).key.token);
|
||||
}
|
||||
}
|
||||
StringMapWrapper.forEach(appEl.proto.directiveVariableBindings, (directiveIndex, name) => {
|
||||
if (isBlank(directiveIndex)) {
|
||||
localsMap.set(name, appEl.nativeElement);
|
||||
@ -126,6 +132,9 @@ export class AppView implements ChangeDispatcher {
|
||||
localsMap.set(name, appEl.getDirectiveAtIndex(directiveIndex));
|
||||
}
|
||||
});
|
||||
this.renderer.setElementDebugInfo(
|
||||
appEl.nativeElement, new RenderDebugInfo(appEl.getInjector(), appEl.getComponent(),
|
||||
providerTokens, localsMap));
|
||||
}
|
||||
var parentLocals = null;
|
||||
if (this.proto.type !== ViewType.COMPONENT) {
|
||||
|
@ -1,11 +0,0 @@
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import * as viewModule from './view';
|
||||
|
||||
/**
|
||||
* Listener for view creation / destruction.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AppViewListener {
|
||||
onViewCreated(view: viewModule.AppView) {}
|
||||
onViewDestroyed(view: viewModule.AppView) {}
|
||||
}
|
@ -22,7 +22,6 @@ import {
|
||||
} from './view_ref';
|
||||
import {ViewContainerRef} from './view_container_ref';
|
||||
import {TemplateRef, TemplateRef_} from './template_ref';
|
||||
import {AppViewListener} from './view_listener';
|
||||
import {RootRenderer, RenderComponentType} from 'angular2/src/core/render/api';
|
||||
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
|
||||
import {APP_ID} from 'angular2/src/core/application_tokens';
|
||||
@ -185,10 +184,7 @@ export abstract class AppViewManager {
|
||||
export class AppViewManager_ extends AppViewManager {
|
||||
private _nextCompTypeId: number = 0;
|
||||
|
||||
constructor(private _renderer: RootRenderer, private _viewListener: AppViewListener,
|
||||
@Inject(APP_ID) private _appId: string) {
|
||||
super();
|
||||
}
|
||||
constructor(private _renderer: RootRenderer, @Inject(APP_ID) private _appId: string) { super(); }
|
||||
|
||||
getViewContainer(location: ElementRef): ViewContainerRef {
|
||||
return (<ElementRef_>location).internalElement.getViewContainerRef();
|
||||
@ -316,10 +312,10 @@ export class AppViewManager_ extends AppViewManager {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
onViewCreated(view: AppView) { this._viewListener.onViewCreated(view); }
|
||||
onViewCreated(view: AppView) {}
|
||||
|
||||
/** @internal */
|
||||
onViewDestroyed(view: AppView) { this._viewListener.onViewDestroyed(view); }
|
||||
onViewDestroyed(view: AppView) {}
|
||||
|
||||
/** @internal */
|
||||
createRenderComponentType(encapsulation: ViewEncapsulation,
|
||||
|
@ -1,10 +1,16 @@
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
import {Injector} from 'angular2/src/core/di/injector';
|
||||
|
||||
export class RenderComponentType {
|
||||
constructor(public id: string, public encapsulation: ViewEncapsulation,
|
||||
public styles: Array<string | any[]>) {}
|
||||
}
|
||||
|
||||
export class RenderDebugInfo {
|
||||
constructor(public injector: Injector, public component: any, public providerTokens: any[],
|
||||
public locals: Map<string, any>) {}
|
||||
}
|
||||
|
||||
export interface ParentRenderer { renderComponent(componentType: RenderComponentType): Renderer; }
|
||||
|
||||
export abstract class Renderer implements ParentRenderer {
|
||||
@ -42,6 +48,8 @@ export abstract class Renderer implements ParentRenderer {
|
||||
*/
|
||||
abstract setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string);
|
||||
|
||||
abstract setElementDebugInfo(renderElement: any, info: RenderDebugInfo);
|
||||
|
||||
abstract setElementClass(renderElement: any, className: string, isAdd: boolean);
|
||||
|
||||
abstract setElementStyle(renderElement: any, styleName: string, styleValue: string);
|
||||
|
@ -30,12 +30,12 @@ import {BrowserDomAdapter} from './browser/browser_adapter';
|
||||
import {BrowserGetTestability} from 'angular2/src/platform/browser/testability';
|
||||
import {wtfInit} from 'angular2/src/core/profile/wtf_init';
|
||||
import {EventManager, EVENT_MANAGER_PLUGINS} from "angular2/src/platform/dom/events/event_manager";
|
||||
import {ELEMENT_PROBE_PROVIDERS} from 'angular2/platform/common_dom';
|
||||
export {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
|
||||
export {Title} from 'angular2/src/platform/browser/title';
|
||||
export {
|
||||
DebugElementViewListener,
|
||||
ELEMENT_PROBE_PROVIDERS,
|
||||
ELEMENT_PROBE_BINDINGS,
|
||||
ELEMENT_PROBE_PROVIDERS_PROD_MODE,
|
||||
inspectNativeElement,
|
||||
By
|
||||
} from 'angular2/platform/common_dom';
|
||||
@ -84,7 +84,8 @@ export const BROWSER_APP_COMMON_PROVIDERS: Array<any /*Type | Provider | any[]*/
|
||||
Testability,
|
||||
BrowserDetails,
|
||||
AnimationBuilder,
|
||||
EventManager
|
||||
EventManager,
|
||||
ELEMENT_PROBE_PROVIDERS
|
||||
]);
|
||||
|
||||
export function initDomAdapter() {
|
||||
|
@ -39,6 +39,6 @@ export class By {
|
||||
* {@example platform/dom/debug/ts/by/by.ts region='by_directive'}
|
||||
*/
|
||||
static directive(type: Type): Predicate<DebugElement> {
|
||||
return (debugElement) => { return debugElement.hasDirective(type); };
|
||||
return (debugElement) => { return debugElement.providerTokens.indexOf(type) !== -1; };
|
||||
}
|
||||
}
|
||||
|
@ -1,89 +0,0 @@
|
||||
import {CONST_EXPR, isPresent, NumberWrapper, StringWrapper} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, Map, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Injectable, provide, Provider} from 'angular2/src/core/di';
|
||||
import {AppViewListener} from 'angular2/src/core/linker/view_listener';
|
||||
import {AppView} from 'angular2/src/core/linker/view';
|
||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||
import {DebugElement, DebugElement_} from 'angular2/src/core/debug/debug_element';
|
||||
|
||||
const NG_ID_PROPERTY = 'ngid';
|
||||
const INSPECT_GLOBAL_NAME = 'ng.probe';
|
||||
|
||||
const NG_ID_SEPARATOR = '#';
|
||||
|
||||
// Need to keep the views in a global Map so that multiple angular apps are supported
|
||||
var _allIdsByView = new Map<AppView, number>();
|
||||
var _allViewsById = new Map<number, AppView>();
|
||||
|
||||
var _nextId = 0;
|
||||
|
||||
function _setElementId(element, indices: number[]) {
|
||||
if (isPresent(element) && DOM.isElementNode(element)) {
|
||||
DOM.setData(element, NG_ID_PROPERTY, indices.join(NG_ID_SEPARATOR));
|
||||
}
|
||||
}
|
||||
|
||||
function _getElementId(element): number[] {
|
||||
var elId = DOM.getData(element, NG_ID_PROPERTY);
|
||||
if (isPresent(elId)) {
|
||||
return elId.split(NG_ID_SEPARATOR).map(partStr => NumberWrapper.parseInt(partStr, 10));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link DebugElement} for the given native DOM element, or
|
||||
* null if the given native element does not have an Angular view associated
|
||||
* with it.
|
||||
*/
|
||||
export function inspectNativeElement(element): DebugElement {
|
||||
var elId = _getElementId(element);
|
||||
if (isPresent(elId)) {
|
||||
var view = _allViewsById.get(elId[0]);
|
||||
if (isPresent(view)) {
|
||||
return new DebugElement_(view.appElements[elId[1]]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DebugElementViewListener implements AppViewListener {
|
||||
constructor() { DOM.setGlobalVar(INSPECT_GLOBAL_NAME, inspectNativeElement); }
|
||||
|
||||
onViewCreated(view: AppView) {
|
||||
var viewId = _nextId++;
|
||||
_allViewsById.set(viewId, view);
|
||||
_allIdsByView.set(view, viewId);
|
||||
for (var i = 0; i < view.appElements.length; i++) {
|
||||
var el = view.appElements[i];
|
||||
_setElementId(el.nativeElement, [viewId, i]);
|
||||
}
|
||||
}
|
||||
|
||||
onViewDestroyed(view: AppView) {
|
||||
var viewId = _allIdsByView.get(view);
|
||||
_allIdsByView.delete(view);
|
||||
_allViewsById.delete(viewId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Providers which support debugging Angular applications (e.g. via `ng.probe`).
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* {@example platform/dom/debug/ts/debug_element_view_listener/providers.ts region='providers'}
|
||||
*/
|
||||
export const ELEMENT_PROBE_PROVIDERS: any[] = CONST_EXPR([
|
||||
DebugElementViewListener,
|
||||
CONST_EXPR(new Provider(AppViewListener, {useExisting: DebugElementViewListener})),
|
||||
]);
|
||||
|
||||
/**
|
||||
* Use {@link ELEMENT_PROBE_PROVIDERS}.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export const ELEMENT_PROBE_BINDINGS = ELEMENT_PROBE_PROVIDERS;
|
42
modules/angular2/src/platform/dom/debug/ng_probe.ts
Normal file
42
modules/angular2/src/platform/dom/debug/ng_probe.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import {CONST_EXPR, assertionsEnabled, isPresent} from 'angular2/src/facade/lang';
|
||||
import {Injectable, provide, Provider} from 'angular2/src/core/di';
|
||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||
import {DebugNode, getDebugNode} from 'angular2/src/core/debug/debug_node';
|
||||
import {DomRootRenderer} from 'angular2/src/platform/dom/dom_renderer';
|
||||
import {RootRenderer} from 'angular2/core';
|
||||
import {DebugDomRootRenderer} from 'angular2/src/core/debug/debug_renderer';
|
||||
|
||||
|
||||
const INSPECT_GLOBAL_NAME = 'ng.probe';
|
||||
|
||||
/**
|
||||
* Returns a {@link DebugElement} for the given native DOM element, or
|
||||
* null if the given native element does not have an Angular view associated
|
||||
* with it.
|
||||
*/
|
||||
export function inspectNativeElement(element): DebugNode {
|
||||
return getDebugNode(element);
|
||||
}
|
||||
|
||||
function _createConditionalRootRenderer(rootRenderer) {
|
||||
if (assertionsEnabled()) {
|
||||
return _createRootRenderer(rootRenderer);
|
||||
}
|
||||
return rootRenderer;
|
||||
}
|
||||
|
||||
function _createRootRenderer(rootRenderer) {
|
||||
DOM.setGlobalVar(INSPECT_GLOBAL_NAME, inspectNativeElement);
|
||||
return new DebugDomRootRenderer(rootRenderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Providers which support debugging Angular applications (e.g. via `ng.probe`).
|
||||
*/
|
||||
export const ELEMENT_PROBE_PROVIDERS: any[] = CONST_EXPR([
|
||||
new Provider(RootRenderer,
|
||||
{useFactory: _createConditionalRootRenderer, deps: [DomRootRenderer]})
|
||||
]);
|
||||
|
||||
export const ELEMENT_PROBE_PROVIDERS_PROD_MODE: any[] = CONST_EXPR(
|
||||
[new Provider(RootRenderer, {useFactory: _createRootRenderer, deps: [DomRootRenderer]})]);
|
@ -14,7 +14,12 @@ import {
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {DomSharedStylesHost} from './shared_styles_host';
|
||||
|
||||
import {Renderer, RootRenderer, RenderComponentType} from 'angular2/core';
|
||||
import {
|
||||
Renderer,
|
||||
RootRenderer,
|
||||
RenderComponentType,
|
||||
RenderDebugInfo
|
||||
} from 'angular2/src/core/render/api';
|
||||
|
||||
import {EventManager} from './events/event_manager';
|
||||
|
||||
@ -201,6 +206,8 @@ export class DomRenderer implements Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
setElementDebugInfo(renderElement: any, info: RenderDebugInfo) {}
|
||||
|
||||
setElementClass(renderElement: any, className: string, isAdd: boolean): void {
|
||||
if (isAdd) {
|
||||
DOM.addClass(renderElement, className);
|
||||
|
@ -1,11 +1,11 @@
|
||||
import {
|
||||
ComponentRef,
|
||||
DebugElement,
|
||||
DirectiveResolver,
|
||||
DynamicComponentLoader,
|
||||
Injector,
|
||||
Injectable,
|
||||
ViewMetadata,
|
||||
ElementRef,
|
||||
EmbeddedViewRef,
|
||||
ViewResolver,
|
||||
provide
|
||||
@ -23,7 +23,7 @@ import {el} from './utils';
|
||||
import {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
|
||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||
|
||||
import {DebugElement_} from 'angular2/src/core/debug/debug_element';
|
||||
import {DebugNode, DebugElement, getDebugNode} from 'angular2/src/core/debug/debug_node';
|
||||
|
||||
|
||||
/**
|
||||
@ -45,6 +45,11 @@ export abstract class ComponentFixture {
|
||||
*/
|
||||
nativeElement: any;
|
||||
|
||||
/**
|
||||
* The ElementRef for the element at the root of the component.
|
||||
*/
|
||||
elementRef: ElementRef;
|
||||
|
||||
/**
|
||||
* Trigger a change detection cycle for the component.
|
||||
*/
|
||||
@ -66,7 +71,9 @@ export class ComponentFixture_ extends ComponentFixture {
|
||||
constructor(componentRef: ComponentRef) {
|
||||
super();
|
||||
this._componentParentView = (<ViewRef_>componentRef.hostView).internalView;
|
||||
this.debugElement = new DebugElement_(this._componentParentView.appElements[0]);
|
||||
this.elementRef = this._componentParentView.appElements[0].ref;
|
||||
this.debugElement = <DebugElement>getDebugNode(
|
||||
this._componentParentView.rootNodesOrAppElements[0].nativeElement);
|
||||
this.componentInstance = this.debugElement.componentInstance;
|
||||
this.nativeElement = this.debugElement.nativeElement;
|
||||
this._componentRef = componentRef;
|
||||
|
@ -1,4 +1,9 @@
|
||||
import {Renderer, RootRenderer, RenderComponentType} from 'angular2/src/core/render/api';
|
||||
import {
|
||||
Renderer,
|
||||
RootRenderer,
|
||||
RenderComponentType,
|
||||
RenderDebugInfo
|
||||
} from 'angular2/src/core/render/api';
|
||||
import {
|
||||
ClientMessageBroker,
|
||||
ClientMessageBrokerFactory,
|
||||
@ -186,6 +191,8 @@ export class WebWorkerRenderer implements Renderer, RenderStoreObject {
|
||||
]);
|
||||
}
|
||||
|
||||
setElementDebugInfo(renderElement: any, info: RenderDebugInfo) {}
|
||||
|
||||
setElementClass(renderElement: any, className: string, isAdd: boolean) {
|
||||
this._runOnService('setElementClass', [
|
||||
new FnArg(renderElement, RenderStoreObject),
|
||||
|
Reference in New Issue
Block a user