Skip to content

Commit

Permalink
Revert "fix(upgrade): properly destroy upgraded component elements an…
Browse files Browse the repository at this point in the history
…d descendants (#26209)"

This reverts commit 623adbb.
  • Loading branch information
jasonaden committed Oct 8, 2018
1 parent 8a6f372 commit b22c376
Show file tree
Hide file tree
Showing 4 changed files with 9 additions and 307 deletions.
11 changes: 3 additions & 8 deletions packages/upgrade/src/common/angular1.ts
Expand Up @@ -223,17 +223,14 @@ let angular: {
bootstrap: (e: Element, modules: (string | IInjectable)[], config?: IAngularBootstrapConfig) =>
IInjectorService,
module: (prefix: string, dependencies?: string[]) => IModule,
element: {
(e: string | Element | Document | IAugmentedJQuery): IAugmentedJQuery;
cleanData: (nodes: Node[] | NodeList) => void;
},
element: (e: string | Element | Document | IAugmentedJQuery) => IAugmentedJQuery,
version: {major: number},
resumeBootstrap: () => void,
getTestability: (e: Element) => ITestabilityService
} = <any>{
bootstrap: noNg,
module: noNg,
element: Object.assign(() => noNg(), {cleanData: noNg}),
element: noNg,
version: undefined,
resumeBootstrap: noNg,
getTestability: noNg
Expand Down Expand Up @@ -284,9 +281,7 @@ export const bootstrap: typeof angular.bootstrap = (e, modules, config?) =>
export const module: typeof angular.module = (prefix, dependencies?) =>
angular.module(prefix, dependencies);

export const element: typeof angular.element = Object.assign(
(e: string | Element | Document | IAugmentedJQuery) => angular.element(e),
{cleanData: (nodes: Node[] | NodeList) => angular.element.cleanData(nodes)});
export const element: typeof angular.element = e => angular.element(e);

export const resumeBootstrap: typeof angular.resumeBootstrap = () => angular.resumeBootstrap();

Expand Down
10 changes: 1 addition & 9 deletions packages/upgrade/src/common/upgrade_helper.ts
Expand Up @@ -124,15 +124,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
angular.element.cleanData([this.element]);
angular.element.cleanData(this.element.querySelectorAll('*'));
this.$element.triggerHandler !('$destroy');
}

prepareTransclusion(): angular.ILinkFn|undefined {
Expand Down
148 changes: 3 additions & 145 deletions packages/upgrade/test/dynamic/upgrade_spec.ts
Expand Up @@ -12,7 +12,6 @@ import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import * as angular from '@angular/upgrade/src/common/angular1';
import {UpgradeAdapter, UpgradeAdapterRef} from '@angular/upgrade/src/dynamic/upgrade_adapter';

import {$apply, $digest, html, multiTrim, withEachNg1Version} from './test_helpers';

declare global {
Expand Down Expand Up @@ -2178,10 +2177,9 @@ withEachNg1Version(() => {
});
}));

it('should emit `$destroy` on `$element` and descendants', fakeAsync(() => {
it('should emit `$destroy` on `$element`', fakeAsync(() => {
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
const elementDestroyListener = jasmine.createSpy('elementDestroyListener');
const descendantDestroyListener = jasmine.createSpy('descendantDestroyListener');
let ng2ComponentInstance: Ng2Component;

@Component({selector: 'ng2', template: '<div *ngIf="!ng2Destroy"><ng1></ng1></div>'})
Expand All @@ -2196,13 +2194,9 @@ withEachNg1Version(() => {
// Mocking animations (via `ngAnimateMock`) avoids the issue.
angular.module('ng1', ['ngAnimateMock'])
.component('ng1', {
controller: class {
constructor(private $element: angular.IAugmentedJQuery) {} $onInit() {
this.$element.on !('$destroy', elementDestroyListener);
this.$element.contents !().on !('$destroy', descendantDestroyListener);
}
controller: function($element: angular.IAugmentedJQuery) {
$element.on !('$destroy', elementDestroyListener);
},
template: '<div></div>'
})
.directive('ng2', adapter.downgradeNg2Component(Ng2Component));

Expand All @@ -2216,150 +2210,14 @@ withEachNg1Version(() => {
const element = html('<ng2></ng2>');
adapter.bootstrap(element, ['ng1']).ready((ref) => {
const $rootScope = ref.ng1RootScope as any;
tick();
$rootScope.$digest();

expect(elementDestroyListener).not.toHaveBeenCalled();
expect(descendantDestroyListener).not.toHaveBeenCalled();

ng2ComponentInstance.ng2Destroy = true;
tick();
$rootScope.$digest();

expect(elementDestroyListener).toHaveBeenCalledTimes(1);
expect(descendantDestroyListener).toHaveBeenCalledTimes(1);
});
}));

it('should clear data on `$element` and descendants', fakeAsync(() => {
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
let ng1ComponentElement: angular.IAugmentedJQuery;
let ng2ComponentAInstance: Ng2ComponentA;

// Define `ng1Component`
const ng1Component: angular.IComponent = {
controller: class {
constructor(private $element: angular.IAugmentedJQuery) {} $onInit() {
this.$element.data !('test', 1);
this.$element.contents !().data !('test', 2);

ng1ComponentElement = this.$element;
}
},
template: '<div></div>'
};

// Define `Ng2Component`
@Component({selector: 'ng2A', template: '<ng2B *ngIf="!destroyIt"></ng2B>'})
class Ng2ComponentA {
destroyIt = false;

constructor() { ng2ComponentAInstance = this; }
}

@Component({selector: 'ng2B', template: '<ng1></ng1>'})
class Ng2ComponentB {
}

// Define `ng1Module`
angular.module('ng1Module', [])
.component('ng1', ng1Component)
.directive('ng2A', adapter.downgradeNg2Component(Ng2ComponentA));

// Define `Ng2Module`
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2ComponentA, Ng2ComponentB],
entryComponents: [Ng2ComponentA],
imports: [BrowserModule]
})
class Ng2Module {
ngDoBootstrap() {}
}

// Bootstrap
const element = html(`<ng2-a></ng2-a>`);

adapter.bootstrap(element, ['ng1Module']).ready((ref) => {
const $rootScope = ref.ng1RootScope as any;
tick();
$rootScope.$digest();
expect(ng1ComponentElement.data !('test')).toBe(1);
expect(ng1ComponentElement.contents !().data !('test')).toBe(2);

ng2ComponentAInstance.destroyIt = true;
tick();
$rootScope.$digest();

expect(ng1ComponentElement.data !('test')).toBeUndefined();
expect(ng1ComponentElement.contents !().data !('test')).toBeUndefined();
});
}));

it('should clear dom listeners on `$element` and descendants`', fakeAsync(() => {
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
const elementClickListener = jasmine.createSpy('elementClickListener');
const descendantClickListener = jasmine.createSpy('descendantClickListener');
let ng1DescendantElement: angular.IAugmentedJQuery;
let ng2ComponentAInstance: Ng2ComponentA;

// Define `ng1Component`
const ng1Component: angular.IComponent = {
controller: class {
constructor(private $element: angular.IAugmentedJQuery) {} $onInit() {
ng1DescendantElement = this.$element.contents !();

this.$element.on !('click', elementClickListener);
ng1DescendantElement.on !('click', descendantClickListener);
}
},
template: '<div></div>'
};

// Define `Ng2Component`
@Component({selector: 'ng2A', template: '<ng2B *ngIf="!destroyIt"></ng2B>'})
class Ng2ComponentA {
destroyIt = false;

constructor() { ng2ComponentAInstance = this; }
}

@Component({selector: 'ng2B', template: '<ng1></ng1>'})
class Ng2ComponentB {
}

// Define `ng1Module`
angular.module('ng1Module', [])
.component('ng1', ng1Component)
.directive('ng2A', adapter.downgradeNg2Component(Ng2ComponentA));

// Define `Ng2Module`
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2ComponentA, Ng2ComponentB],
entryComponents: [Ng2ComponentA],
imports: [BrowserModule]
})
class Ng2Module {
ngDoBootstrap() {}
}

// Bootstrap
const element = html(`<ng2-a></ng2-a>`);

adapter.bootstrap(element, ['ng1Module']).ready((ref) => {
const $rootScope = ref.ng1RootScope as any;
tick();
$rootScope.$digest();
(ng1DescendantElement[0] as HTMLElement).click();
expect(elementClickListener).toHaveBeenCalledTimes(1);
expect(descendantClickListener).toHaveBeenCalledTimes(1);

ng2ComponentAInstance.destroyIt = true;
tick();
$rootScope.$digest();

(ng1DescendantElement[0] as HTMLElement).click();
expect(elementClickListener).toHaveBeenCalledTimes(1);
expect(descendantClickListener).toHaveBeenCalledTimes(1);
});
}));
});
Expand Down

0 comments on commit b22c376

Please sign in to comment.