feat(core): introduce getDirectiveMetadata global debugging utility (#41525)
This commit introduces a global debugging method `ng.getDirectiveMetadata` which returns the metadata for a directive or component instance. PR Close #41525
This commit is contained in:
parent
1d12c50f63
commit
a07f303708
|
@ -1,10 +1,22 @@
|
||||||
export declare function applyChanges(component: {}): void;
|
export declare function applyChanges(component: {}): void;
|
||||||
|
|
||||||
|
export interface ComponentDebugMetadata extends DirectiveDebugMetadata {
|
||||||
|
changeDetection: ChangeDetectionStrategy;
|
||||||
|
encapsulation: ViewEncapsulation;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectiveDebugMetadata {
|
||||||
|
inputs: Record<string, string>;
|
||||||
|
outputs: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
export declare function getComponent<T>(element: Element): T | null;
|
export declare function getComponent<T>(element: Element): T | null;
|
||||||
|
|
||||||
export declare function getContext<T>(element: Element): T | null;
|
export declare function getContext<T>(element: Element): T | null;
|
||||||
|
|
||||||
export declare function getDirectives(element: Element): {}[];
|
export declare function getDirectiveMetadata(directiveOrComponentInstance: any): ComponentDebugMetadata | DirectiveDebugMetadata | null;
|
||||||
|
|
||||||
|
export declare function getDirectives(node: Node): {}[];
|
||||||
|
|
||||||
export declare function getHostElement(componentOrDirective: {}): Element;
|
export declare function getHostElement(componentOrDirective: {}): Element;
|
||||||
|
|
||||||
|
|
|
@ -16,4 +16,4 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {applyChanges} from './util/change_detection_utils';
|
export {applyChanges} from './util/change_detection_utils';
|
||||||
export {getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getOwningComponent, getRootComponents, Listener} from './util/discovery_utils';
|
export {ComponentDebugMetadata, DirectiveDebugMetadata, getComponent, getContext, getDirectiveMetadata, getDirectives, getHostElement, getInjector, getListeners, getOwningComponent, getRootComponents, Listener} from './util/discovery_utils';
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {ɵɵNgOnChangesFeature} from './features/ng_onchanges_feature';
|
||||||
import {ɵɵProvidersFeature} from './features/providers_feature';
|
import {ɵɵProvidersFeature} from './features/providers_feature';
|
||||||
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, PipeDef} from './interfaces/definition';
|
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, PipeDef} from './interfaces/definition';
|
||||||
import {ɵɵComponentDeclaration, ɵɵDirectiveDeclaration, ɵɵFactoryDeclaration, ɵɵInjectorDeclaration, ɵɵNgModuleDeclaration, ɵɵPipeDeclaration} from './interfaces/public_definitions';
|
import {ɵɵComponentDeclaration, ɵɵDirectiveDeclaration, ɵɵFactoryDeclaration, ɵɵInjectorDeclaration, ɵɵNgModuleDeclaration, ɵɵPipeDeclaration} from './interfaces/public_definitions';
|
||||||
import {getComponent, getDirectives, getHostElement, getRenderedText} from './util/discovery_utils';
|
import {ComponentDebugMetadata, DirectiveDebugMetadata, getComponent, getDirectiveMetadata, getDirectives, getHostElement, getRenderedText} from './util/discovery_utils';
|
||||||
|
|
||||||
export {NgModuleType} from '../metadata/ng_module_def';
|
export {NgModuleType} from '../metadata/ng_module_def';
|
||||||
export {ComponentFactory, ComponentFactoryResolver, ComponentRef, injectComponentFactoryResolver} from './component_ref';
|
export {ComponentFactory, ComponentFactoryResolver, ComponentRef, injectComponentFactoryResolver} from './component_ref';
|
||||||
|
@ -176,12 +176,15 @@ export { ɵɵtemplateRefExtractor} from './view_engine_compatibility_prebound';
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
ComponentDebugMetadata,
|
||||||
ComponentDef,
|
ComponentDef,
|
||||||
ComponentTemplate,
|
ComponentTemplate,
|
||||||
ComponentType,
|
ComponentType,
|
||||||
|
DirectiveDebugMetadata,
|
||||||
DirectiveDef,
|
DirectiveDef,
|
||||||
DirectiveType,
|
DirectiveType,
|
||||||
getComponent,
|
getComponent,
|
||||||
|
getDirectiveMetadata,
|
||||||
getDirectives,
|
getDirectives,
|
||||||
getHostElement,
|
getHostElement,
|
||||||
getRenderedText,
|
getRenderedText,
|
||||||
|
|
|
@ -6,10 +6,13 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {ChangeDetectionStrategy} from '../../change_detection/constants';
|
||||||
import {Injector} from '../../di/injector';
|
import {Injector} from '../../di/injector';
|
||||||
|
import {ViewEncapsulation} from '../../metadata/view';
|
||||||
import {assertEqual} from '../../util/assert';
|
import {assertEqual} from '../../util/assert';
|
||||||
import {assertLView} from '../assert';
|
import {assertLView} from '../assert';
|
||||||
import {discoverLocalRefs, getComponentAtNodeIndex, getDirectivesAtNodeIndex, getLContext} from '../context_discovery';
|
import {discoverLocalRefs, getComponentAtNodeIndex, getDirectivesAtNodeIndex, getLContext} from '../context_discovery';
|
||||||
|
import {getComponentDef, getDirectiveDef} from '../definition';
|
||||||
import {NodeInjector} from '../di';
|
import {NodeInjector} from '../di';
|
||||||
import {buildDebugNode} from '../instructions/lview_debug';
|
import {buildDebugNode} from '../instructions/lview_debug';
|
||||||
import {LContext} from '../interfaces/context';
|
import {LContext} from '../interfaces/context';
|
||||||
|
@ -203,6 +206,70 @@ export function getDirectives(element: Element): {}[] {
|
||||||
return context.directives === null ? [] : [...context.directives];
|
return context.directives === null ? [] : [...context.directives];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Partial metadata for a given directive instance.
|
||||||
|
* This information might be useful for debugging purposes or tooling.
|
||||||
|
* Currently only `inputs` and `outputs` metadata is available.
|
||||||
|
*
|
||||||
|
* @publicApi
|
||||||
|
*/
|
||||||
|
export interface DirectiveDebugMetadata {
|
||||||
|
inputs: Record<string, string>;
|
||||||
|
outputs: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Partial metadata for a given component instance.
|
||||||
|
* This information might be useful for debugging purposes or tooling.
|
||||||
|
* Currently the following fields are available:
|
||||||
|
* - inputs
|
||||||
|
* - outputs
|
||||||
|
* - encapsulation
|
||||||
|
* - changeDetection
|
||||||
|
*
|
||||||
|
* @publicApi
|
||||||
|
*/
|
||||||
|
export interface ComponentDebugMetadata extends DirectiveDebugMetadata {
|
||||||
|
encapsulation: ViewEncapsulation;
|
||||||
|
changeDetection: ChangeDetectionStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the debug (partial) metadata for a particular directive or component instance.
|
||||||
|
* The function accepts an instance of a directive or component and returns the corresponding
|
||||||
|
* metadata.
|
||||||
|
*
|
||||||
|
* @param directiveOrComponentInstance Instance of a directive or component
|
||||||
|
* @returns metadata of the passed directive or component
|
||||||
|
*
|
||||||
|
* @publicApi
|
||||||
|
* @globalApi ng
|
||||||
|
*/
|
||||||
|
export function getDirectiveMetadata(directiveOrComponentInstance: any): ComponentDebugMetadata|
|
||||||
|
DirectiveDebugMetadata|null {
|
||||||
|
const {constructor} = directiveOrComponentInstance;
|
||||||
|
if (!constructor) {
|
||||||
|
throw new Error('Unable to find the instance constructor');
|
||||||
|
}
|
||||||
|
// In case a component inherits from a directive, we may have component and directive metadata
|
||||||
|
// To ensure we don't get the metadata of the directive, we want to call `getComponentDef` first.
|
||||||
|
const componentDef = getComponentDef(constructor);
|
||||||
|
if (componentDef) {
|
||||||
|
return {
|
||||||
|
inputs: componentDef.inputs,
|
||||||
|
outputs: componentDef.outputs,
|
||||||
|
encapsulation: componentDef.encapsulation,
|
||||||
|
changeDetection: componentDef.onPush ? ChangeDetectionStrategy.OnPush :
|
||||||
|
ChangeDetectionStrategy.Default
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const directiveDef = getDirectiveDef(constructor);
|
||||||
|
if (directiveDef) {
|
||||||
|
return {inputs: directiveDef.inputs, outputs: directiveDef.outputs};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns LContext associated with a target passed as an argument.
|
* Returns LContext associated with a target passed as an argument.
|
||||||
* Throws if a given target doesn't have associated LContext.
|
* Throws if a given target doesn't have associated LContext.
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {assertDefined} from '../../util/assert';
|
||||||
import {global} from '../../util/global';
|
import {global} from '../../util/global';
|
||||||
import {setProfiler} from '../profiler';
|
import {setProfiler} from '../profiler';
|
||||||
import {applyChanges} from './change_detection_utils';
|
import {applyChanges} from './change_detection_utils';
|
||||||
import {getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getOwningComponent, getRootComponents} from './discovery_utils';
|
import {getComponent, getContext, getDirectiveMetadata, getDirectives, getHostElement, getInjector, getListeners, getOwningComponent, getRootComponents} from './discovery_utils';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ export function publishDefaultGlobalUtils() {
|
||||||
* removed completely.
|
* removed completely.
|
||||||
*/
|
*/
|
||||||
publishGlobalUtil('ɵsetProfiler', setProfiler);
|
publishGlobalUtil('ɵsetProfiler', setProfiler);
|
||||||
|
publishGlobalUtil('getDirectiveMetadata', getDirectiveMetadata);
|
||||||
publishGlobalUtil('getComponent', getComponent);
|
publishGlobalUtil('getComponent', getComponent);
|
||||||
publishGlobalUtil('getContext', getContext);
|
publishGlobalUtil('getContext', getContext);
|
||||||
publishGlobalUtil('getListeners', getListeners);
|
publishGlobalUtil('getListeners', getListeners);
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
*/
|
*/
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {Component, Directive, HostBinding, InjectionToken, ViewChild} from '@angular/core';
|
import {Component, Directive, HostBinding, InjectionToken, ViewChild} from '@angular/core';
|
||||||
|
import {ChangeDetectionStrategy} from '@angular/core/src/change_detection';
|
||||||
|
import {EventEmitter} from '@angular/core/src/event_emitter';
|
||||||
|
import {Input, Output, ViewEncapsulation} from '@angular/core/src/metadata';
|
||||||
import {isLView} from '@angular/core/src/render3/interfaces/type_checks';
|
import {isLView} from '@angular/core/src/render3/interfaces/type_checks';
|
||||||
import {CONTEXT} from '@angular/core/src/render3/interfaces/view';
|
import {CONTEXT} from '@angular/core/src/render3/interfaces/view';
|
||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
@ -15,13 +18,13 @@ import {expect} from '@angular/core/testing/src/testing_internal';
|
||||||
import {onlyInIvy} from '@angular/private/testing';
|
import {onlyInIvy} from '@angular/private/testing';
|
||||||
|
|
||||||
import {getHostElement, markDirty} from '../../src/render3/index';
|
import {getHostElement, markDirty} from '../../src/render3/index';
|
||||||
import {getComponent, getComponentLView, getContext, getDebugNode, getDirectives, getInjectionTokens, getInjector, getListeners, getLocalRefs, getOwningComponent, getRootComponents, loadLContext} from '../../src/render3/util/discovery_utils';
|
import {ComponentDebugMetadata, getComponent, getComponentLView, getContext, getDebugNode, getDirectiveMetadata, getDirectives, getInjectionTokens, getInjector, getListeners, getLocalRefs, getOwningComponent, getRootComponents, loadLContext} from '../../src/render3/util/discovery_utils';
|
||||||
|
|
||||||
onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
||||||
let fixture: ComponentFixture<MyApp>;
|
let fixture: ComponentFixture<MyApp>;
|
||||||
let myApp: MyApp;
|
let myApp: MyApp;
|
||||||
let dirA: DirectiveA[];
|
let dirA: DirectiveA[];
|
||||||
let childComponent: DirectiveA[];
|
let childComponent: (DirectiveA|Child)[];
|
||||||
let child: NodeListOf<Element>;
|
let child: NodeListOf<Element>;
|
||||||
let span: NodeListOf<Element>;
|
let span: NodeListOf<Element>;
|
||||||
let div: NodeListOf<Element>;
|
let div: NodeListOf<Element>;
|
||||||
|
@ -55,6 +58,8 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
||||||
|
|
||||||
@Directive({selector: '[dirA]', exportAs: 'dirA'})
|
@Directive({selector: '[dirA]', exportAs: 'dirA'})
|
||||||
class DirectiveA {
|
class DirectiveA {
|
||||||
|
@Input('a') b = 2;
|
||||||
|
@Output('c') d = new EventEmitter();
|
||||||
constructor() {
|
constructor() {
|
||||||
dirA.push(this);
|
dirA.push(this);
|
||||||
}
|
}
|
||||||
|
@ -73,6 +78,8 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
||||||
})
|
})
|
||||||
class MyApp {
|
class MyApp {
|
||||||
text: string = 'INIT';
|
text: string = 'INIT';
|
||||||
|
@Input('a') b = 2;
|
||||||
|
@Output('c') d = new EventEmitter();
|
||||||
constructor() {
|
constructor() {
|
||||||
myApp = this;
|
myApp = this;
|
||||||
}
|
}
|
||||||
|
@ -288,6 +295,23 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
||||||
expect(lContext.native as any).toBe(ngContainerComment);
|
expect(lContext.native as any).toBe(ngContainerComment);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getDirectiveMetadata', () => {
|
||||||
|
it('should work with components', () => {
|
||||||
|
const metadata = getDirectiveMetadata(myApp);
|
||||||
|
expect(metadata!.inputs).toEqual({a: 'b'});
|
||||||
|
expect(metadata!.outputs).toEqual({c: 'd'});
|
||||||
|
expect((metadata as ComponentDebugMetadata).changeDetection)
|
||||||
|
.toBe(ChangeDetectionStrategy.Default);
|
||||||
|
expect((metadata as ComponentDebugMetadata).encapsulation).toBe(ViewEncapsulation.None);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with directives', () => {
|
||||||
|
const metadata = getDirectiveMetadata(getDirectives(div[0])[0]);
|
||||||
|
expect(metadata!.inputs).toEqual({a: 'b'});
|
||||||
|
expect(metadata!.outputs).toEqual({c: 'd'});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () => {
|
onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () => {
|
||||||
|
@ -359,6 +383,14 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
|
||||||
const elm2Dirs = getDirectives(elm2);
|
const elm2Dirs = getDirectives(elm2);
|
||||||
expect(elm2Dirs).toContain(fixture.componentInstance.myDir3Instance!);
|
expect(elm2Dirs).toContain(fixture.componentInstance.myDir3Instance!);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not throw if it cannot find LContext', () => {
|
||||||
|
let result: any;
|
||||||
|
expect(() => {
|
||||||
|
result = getDirectives(document.createElement('div'));
|
||||||
|
}).not.toThrow();
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getInjector', () => {
|
describe('getInjector', () => {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import {setProfiler} from '@angular/core/src/render3/profiler';
|
import {setProfiler} from '@angular/core/src/render3/profiler';
|
||||||
import {applyChanges} from '../../src/render3/util/change_detection_utils';
|
import {applyChanges} from '../../src/render3/util/change_detection_utils';
|
||||||
import {getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getOwningComponent, getRootComponents} from '../../src/render3/util/discovery_utils';
|
import {getComponent, getContext, getDirectiveMetadata, getDirectives, getHostElement, getInjector, getListeners, getOwningComponent, getRootComponents} from '../../src/render3/util/discovery_utils';
|
||||||
import {GLOBAL_PUBLISH_EXPANDO_KEY, GlobalDevModeContainer, publishDefaultGlobalUtils, publishGlobalUtil} from '../../src/render3/util/global_utils';
|
import {GLOBAL_PUBLISH_EXPANDO_KEY, GlobalDevModeContainer, publishDefaultGlobalUtils, publishGlobalUtil} from '../../src/render3/util/global_utils';
|
||||||
import {global} from '../../src/util/global';
|
import {global} from '../../src/util/global';
|
||||||
|
|
||||||
|
@ -62,6 +62,10 @@ describe('global utils', () => {
|
||||||
assertPublished('applyChanges', applyChanges);
|
assertPublished('applyChanges', applyChanges);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should publish getDirectiveMetadata', () => {
|
||||||
|
assertPublished('getDirectiveMetadata', getDirectiveMetadata);
|
||||||
|
});
|
||||||
|
|
||||||
it('should publish ɵsetProfiler', () => {
|
it('should publish ɵsetProfiler', () => {
|
||||||
assertPublished('ɵsetProfiler', setProfiler);
|
assertPublished('ɵsetProfiler', setProfiler);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue