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 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 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;
|
||||
|
||||
|
|
|
@ -16,4 +16,4 @@
|
|||
*/
|
||||
|
||||
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 {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, PipeDef} from './interfaces/definition';
|
||||
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 {ComponentFactory, ComponentFactoryResolver, ComponentRef, injectComponentFactoryResolver} from './component_ref';
|
||||
|
@ -176,12 +176,15 @@ export { ɵɵtemplateRefExtractor} from './view_engine_compatibility_prebound';
|
|||
// clang-format on
|
||||
|
||||
export {
|
||||
ComponentDebugMetadata,
|
||||
ComponentDef,
|
||||
ComponentTemplate,
|
||||
ComponentType,
|
||||
DirectiveDebugMetadata,
|
||||
DirectiveDef,
|
||||
DirectiveType,
|
||||
getComponent,
|
||||
getDirectiveMetadata,
|
||||
getDirectives,
|
||||
getHostElement,
|
||||
getRenderedText,
|
||||
|
|
|
@ -6,10 +6,13 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ChangeDetectionStrategy} from '../../change_detection/constants';
|
||||
import {Injector} from '../../di/injector';
|
||||
import {ViewEncapsulation} from '../../metadata/view';
|
||||
import {assertEqual} from '../../util/assert';
|
||||
import {assertLView} from '../assert';
|
||||
import {discoverLocalRefs, getComponentAtNodeIndex, getDirectivesAtNodeIndex, getLContext} from '../context_discovery';
|
||||
import {getComponentDef, getDirectiveDef} from '../definition';
|
||||
import {NodeInjector} from '../di';
|
||||
import {buildDebugNode} from '../instructions/lview_debug';
|
||||
import {LContext} from '../interfaces/context';
|
||||
|
@ -203,6 +206,70 @@ export function getDirectives(element: Element): {}[] {
|
|||
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.
|
||||
* 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 {setProfiler} from '../profiler';
|
||||
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.
|
||||
*/
|
||||
publishGlobalUtil('ɵsetProfiler', setProfiler);
|
||||
publishGlobalUtil('getDirectiveMetadata', getDirectiveMetadata);
|
||||
publishGlobalUtil('getComponent', getComponent);
|
||||
publishGlobalUtil('getContext', getContext);
|
||||
publishGlobalUtil('getListeners', getListeners);
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
*/
|
||||
import {CommonModule} from '@angular/common';
|
||||
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 {CONTEXT} from '@angular/core/src/render3/interfaces/view';
|
||||
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 {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', () => {
|
||||
let fixture: ComponentFixture<MyApp>;
|
||||
let myApp: MyApp;
|
||||
let dirA: DirectiveA[];
|
||||
let childComponent: DirectiveA[];
|
||||
let childComponent: (DirectiveA|Child)[];
|
||||
let child: NodeListOf<Element>;
|
||||
let span: NodeListOf<Element>;
|
||||
let div: NodeListOf<Element>;
|
||||
|
@ -55,6 +58,8 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
|||
|
||||
@Directive({selector: '[dirA]', exportAs: 'dirA'})
|
||||
class DirectiveA {
|
||||
@Input('a') b = 2;
|
||||
@Output('c') d = new EventEmitter();
|
||||
constructor() {
|
||||
dirA.push(this);
|
||||
}
|
||||
|
@ -73,6 +78,8 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
|||
})
|
||||
class MyApp {
|
||||
text: string = 'INIT';
|
||||
@Input('a') b = 2;
|
||||
@Output('c') d = new EventEmitter();
|
||||
constructor() {
|
||||
myApp = this;
|
||||
}
|
||||
|
@ -288,6 +295,23 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
|||
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', () => {
|
||||
|
@ -359,6 +383,14 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
|
|||
const elm2Dirs = getDirectives(elm2);
|
||||
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', () => {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import {setProfiler} from '@angular/core/src/render3/profiler';
|
||||
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} from '../../src/util/global';
|
||||
|
||||
|
@ -62,6 +62,10 @@ describe('global utils', () => {
|
|||
assertPublished('applyChanges', applyChanges);
|
||||
});
|
||||
|
||||
it('should publish getDirectiveMetadata', () => {
|
||||
assertPublished('getDirectiveMetadata', getDirectiveMetadata);
|
||||
});
|
||||
|
||||
it('should publish ɵsetProfiler', () => {
|
||||
assertPublished('ɵsetProfiler', setProfiler);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue