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:
Julie Ralph
2016-01-13 21:35:21 -08:00
committed by Alex Eagle
parent ae7d2ab515
commit e1bf3d33f8
45 changed files with 1243 additions and 1220 deletions

View File

@ -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() {

View File

@ -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; };
}
}

View File

@ -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;

View 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]})]);

View File

@ -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);