Skip to content

Commit

Permalink
refactor(upgrade): create a helper for cleaning jqLite/jQuery data (a…
Browse files Browse the repository at this point in the history
…ngular#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 angular#40045
  • Loading branch information
gkalpak authored and zarend committed Dec 11, 2020
1 parent fd573ad commit cab3559
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 19 deletions.
11 changes: 3 additions & 8 deletions packages/upgrade/src/common/src/downgrade_component_adapter.ts
Expand Up @@ -8,10 +8,10 @@

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 {$SCOPE} from './constants';
import {getTypeName, hookupNgModel, strictEquals} from './util';
import {cleanData, getTypeName, hookupNgModel, strictEquals} from './util';

const INITIAL_VALUE = {
__UNINITIALIZED__: true
Expand Down Expand Up @@ -241,12 +241,7 @@ export class DowngradeComponentAdapter {
//
// To ensure the element is always properly cleaned up, we manually call `cleanData()` on
// this element and its descendants before destroying the `ComponentRef`.
//
// 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('*'));
cleanData(this.element[0]);

destroyComponentRef();
}
Expand Down
12 changes: 2 additions & 10 deletions packages/upgrade/src/common/src/upgrade_helper.ts
Expand Up @@ -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 {$COMPILE, $CONTROLLER, $HTTP_BACKEND, $INJECTOR, $TEMPLATE_CACHE} from './constants';
import {controllerKey, directiveNormalize, isFunction} from './util';
import {cleanData, controllerKey, directiveNormalize, isFunction} from './util';



Expand Down Expand Up @@ -125,15 +125,7 @@ export class UpgradeHelper {
controllerInstance.$onDestroy();
}
$scope.$destroy();

// 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('*'));
cleanData(this.element);
}

prepareTransclusion(): ILinkFn|undefined {
Expand Down
25 changes: 24 additions & 1 deletion packages/upgrade/src/common/src/util.ts
Expand Up @@ -8,7 +8,7 @@

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

const DIRECTIVE_PREFIX_REGEXP = /^(?:x|data)[:\-_]/i;
Expand All @@ -25,6 +25,25 @@ export function onError(e: any) {
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 {
return '$' + name + 'Controller';
}
Expand Down Expand Up @@ -53,6 +72,10 @@ export function isFunction(value: any): value is Function {
return typeof value === 'function';
}

function isParentNode(node: Node|ParentNode): node is ParentNode {
return isFunction((node as unknown as ParentNode).querySelectorAll);
}

export function validateInjectionKey(
$injector: IInjectorService, downgradedModule: string, injectionKey: string,
attemptedAction: string): void {
Expand Down

0 comments on commit cab3559

Please sign in to comment.