Skip to content

Commit 9b60ae4

Browse files
committed
perf(core): optimize getDirectives
This commit introduces the following optimizations: 1. We return an empty array for text nodes in `getDirectives` because Angular does not support attaching logic to them. This optimization improves performance of `getDirectives` significantly because text nodes often result in expensive calls to `loadLContext` since we can't resolve it from a parent node. 1. `getDirectives` now calls `loadLContext` with second argument `false` so it doesn't throw an error. This brings another significant improvement because prevents the VM from deoptimizing calls. BREAKING CHANGE: Previously the `ng.getDirectives` function threw an error in case a given DOM node had no Angular context associated with it (for example if a function was called for a DOM element outside of an Angular app). This behavior was inconsistent with other debugging utilities under `ng` namespace, which handled this situation without raising an exception. Now calling the `ng.getDirectives` function for such DOM nodes would result in an empty array returned from that function.
1 parent b619c08 commit 9b60ae4

File tree

2 files changed

+23
-7
lines changed

2 files changed

+23
-7
lines changed

packages/core/src/render3/util/discovery_utils.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ export function getInjectionTokens(element: Element): any[] {
172172
}
173173

174174
/**
175-
* Retrieves directive instances associated with a given DOM element. Does not include
175+
* Retrieves directive instances associated with a given DOM node. Does not include
176176
* component instances.
177177
*
178178
* @usageNotes
@@ -184,21 +184,35 @@ export function getInjectionTokens(element: Element): any[] {
184184
* </my-app>
185185
* ```
186186
* Calling `getDirectives` on `<button>` will return an array with an instance of the `MyButton`
187-
* directive that is associated with the DOM element.
187+
* directive that is associated with the DOM node.
188188
*
189189
* Calling `getDirectives` on `<my-comp>` will return an empty array.
190190
*
191-
* @param element DOM element for which to get the directives.
192-
* @returns Array of directives associated with the element.
191+
* @param node DOM node for which to get the directives.
192+
* @returns Array of directives associated with the node.
193193
*
194194
* @publicApi
195195
* @globalApi ng
196196
*/
197-
export function getDirectives(element: Element): {}[] {
198-
const context = loadLContext(element)!;
197+
export function getDirectives(node: Node): {}[] {
198+
// Skip text nodes because we can't have directives associated with them.
199+
if (node instanceof Text) {
200+
return [];
201+
}
202+
203+
const context = loadLContext(node, false);
204+
if (context === null) {
205+
return [];
206+
}
199207

208+
const lView = context.lView;
209+
const tView = lView[TVIEW];
210+
const nodeIndex = context.nodeIndex;
211+
if (!tView?.data[nodeIndex]) {
212+
return [];
213+
}
200214
if (context.directives === undefined) {
201-
context.directives = getDirectivesAtNodeIndex(context.nodeIndex, context.lView, false);
215+
context.directives = getDirectivesAtNodeIndex(nodeIndex, lView, false);
202216
}
203217

204218
// The `directives` in this case are a named array called `LComponentView`. Clone the

packages/core/test/acceptance/discover_utils_spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,9 +386,11 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
386386

387387
it('should not throw if it cannot find LContext', () => {
388388
let result: any;
389+
389390
expect(() => {
390391
result = getDirectives(document.createElement('div'));
391392
}).not.toThrow();
393+
392394
expect(result).toEqual([]);
393395
});
394396
});

0 commit comments

Comments
 (0)