refactor(upgrade): create a helper for cleaning jqLite/jQuery data (#40045)

This commit moves the code for cleaning jqLite/jQuery data on an element
to a re-usable helper function. This way it is easier to keep the code
consistent across all places where we need to clean data (now and in the
future).

PR Close #40045
This commit is contained in:
George Kalpakas 2020-12-10 19:13:32 +02:00 committed by Alex Rickabaugh
parent 61376d5a6e
commit d08222157c
3 changed files with 29 additions and 19 deletions

View File

@ -8,10 +8,10 @@
import {ApplicationRef, ChangeDetectorRef, ComponentFactory, ComponentRef, EventEmitter, Injector, OnChanges, SimpleChange, SimpleChanges, StaticProvider, Testability, TestabilityRegistry, Type} from '@angular/core'; import {ApplicationRef, ChangeDetectorRef, ComponentFactory, ComponentRef, EventEmitter, Injector, OnChanges, SimpleChange, SimpleChanges, StaticProvider, Testability, TestabilityRegistry, Type} from '@angular/core';
import {element as angularElement, IAttributes, IAugmentedJQuery, ICompileService, INgModelController, IParseService, IScope} from './angular1'; import {IAttributes, IAugmentedJQuery, ICompileService, INgModelController, IParseService, IScope} from './angular1';
import {PropertyBinding} from './component_info'; import {PropertyBinding} from './component_info';
import {$SCOPE} from './constants'; import {$SCOPE} from './constants';
import {getTypeName, hookupNgModel, strictEquals} from './util'; import {cleanData, getTypeName, hookupNgModel, strictEquals} from './util';
const INITIAL_VALUE = { const INITIAL_VALUE = {
__UNINITIALIZED__: true __UNINITIALIZED__: true
@ -241,12 +241,7 @@ export class DowngradeComponentAdapter {
// //
// To ensure the element is always properly cleaned up, we manually call `cleanData()` on // To ensure the element is always properly cleaned up, we manually call `cleanData()` on
// this element and its descendants before destroying the `ComponentRef`. // this element and its descendants before destroying the `ComponentRef`.
// cleanData(this.element[0]);
// NOTE:
// `cleanData()` also will invoke the AngularJS `$destroy` event on the element:
// https://github.com/angular/angular.js/blob/2e72ea13fa98bebf6ed4b5e3c45eaf5f990ed16f/src/Angular.js#L1932-L1945
angularElement.cleanData(this.element);
angularElement.cleanData((this.element[0] as Element).querySelectorAll('*'));
destroyComponentRef(); destroyComponentRef();
} }

View File

@ -10,7 +10,7 @@ import {ElementRef, Injector, SimpleChanges} from '@angular/core';
import {DirectiveRequireProperty, element as angularElement, IAugmentedJQuery, ICloneAttachFunction, ICompileService, IController, IControllerService, IDirective, IHttpBackendService, IInjectorService, ILinkFn, IScope, ITemplateCacheService, SingleOrListOrMap} from './angular1'; import {DirectiveRequireProperty, element as angularElement, IAugmentedJQuery, ICloneAttachFunction, ICompileService, IController, IControllerService, IDirective, IHttpBackendService, IInjectorService, ILinkFn, IScope, ITemplateCacheService, SingleOrListOrMap} from './angular1';
import {$COMPILE, $CONTROLLER, $HTTP_BACKEND, $INJECTOR, $TEMPLATE_CACHE} from './constants'; import {$COMPILE, $CONTROLLER, $HTTP_BACKEND, $INJECTOR, $TEMPLATE_CACHE} from './constants';
import {controllerKey, directiveNormalize, isFunction} from './util'; import {cleanData, controllerKey, directiveNormalize, isFunction} from './util';
@ -125,15 +125,7 @@ export class UpgradeHelper {
controllerInstance.$onDestroy(); controllerInstance.$onDestroy();
} }
$scope.$destroy(); $scope.$destroy();
cleanData(this.element);
// Clean the jQuery/jqLite data on the component+child elements.
// Equivelent to how jQuery/jqLite invoke `cleanData` on an Element (this.element)
// https://github.com/jquery/jquery/blob/e743cbd28553267f955f71ea7248377915613fd9/src/manipulation.js#L223
// https://github.com/angular/angular.js/blob/26ddc5f830f902a3d22f4b2aab70d86d4d688c82/src/jqLite.js#L306-L312
// `cleanData` will invoke the AngularJS `$destroy` DOM event
// https://github.com/angular/angular.js/blob/26ddc5f830f902a3d22f4b2aab70d86d4d688c82/src/Angular.js#L1911-L1924
angularElement.cleanData([this.element]);
angularElement.cleanData(this.element.querySelectorAll('*'));
} }
prepareTransclusion(): ILinkFn|undefined { prepareTransclusion(): ILinkFn|undefined {

View File

@ -8,7 +8,7 @@
import {Injector, Type} from '@angular/core'; import {Injector, Type} from '@angular/core';
import {IInjectorService, INgModelController} from './angular1'; import {element as angularElement, IInjectorService, INgModelController} from './angular1';
import {DOWNGRADED_MODULE_COUNT_KEY, UPGRADE_APP_TYPE_KEY} from './constants'; import {DOWNGRADED_MODULE_COUNT_KEY, UPGRADE_APP_TYPE_KEY} from './constants';
const DIRECTIVE_PREFIX_REGEXP = /^(?:x|data)[:\-_]/i; const DIRECTIVE_PREFIX_REGEXP = /^(?:x|data)[:\-_]/i;
@ -25,6 +25,25 @@ export function onError(e: any) {
throw e; throw e;
} }
/**
* Clean the jqLite/jQuery data on the element and all its descendants.
* Equivalent to how jqLite/jQuery invoke `cleanData()` on an Element when removed:
* https://github.com/angular/angular.js/blob/2e72ea13fa98bebf6ed4b5e3c45eaf5f990ed16f/src/jqLite.js#L349-L355
* https://github.com/jquery/jquery/blob/6984d1747623dbc5e87fd6c261a5b6b1628c107c/src/manipulation.js#L182
*
* NOTE:
* `cleanData()` will also invoke the AngularJS `$destroy` DOM event on the element:
* https://github.com/angular/angular.js/blob/2e72ea13fa98bebf6ed4b5e3c45eaf5f990ed16f/src/Angular.js#L1932-L1945
*
* @param node The DOM node whose data needs to be cleaned.
*/
export function cleanData(node: Node): void {
angularElement.cleanData([node]);
if (isParentNode(node)) {
angularElement.cleanData(node.querySelectorAll('*'));
}
}
export function controllerKey(name: string): string { export function controllerKey(name: string): string {
return '$' + name + 'Controller'; return '$' + name + 'Controller';
} }
@ -53,6 +72,10 @@ export function isFunction(value: any): value is Function {
return typeof value === 'function'; return typeof value === 'function';
} }
function isParentNode(node: Node|ParentNode): node is ParentNode {
return isFunction((node as unknown as ParentNode).querySelectorAll);
}
export function validateInjectionKey( export function validateInjectionKey(
$injector: IInjectorService, downgradedModule: string, injectionKey: string, $injector: IInjectorService, downgradedModule: string, injectionKey: string,
attemptedAction: string): void { attemptedAction: string): void {