test(upgrade): disable failing ngUpgrade tests when in ivy mode (#27132)
PR Close #27132
This commit is contained in:
parent
a4462c24fa
commit
bc0fbfc93e
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
import {Compiler, Component, ComponentFactory, Injector, NgModule, TestabilityRegistry} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {fixmeIvy} from '@angular/private/testing';
|
||||
import * as angular from '@angular/upgrade/src/common/angular1';
|
||||
import {DowngradeComponentAdapter, groupNodesBySelector} from '@angular/upgrade/src/common/downgrade_component_adapter';
|
||||
|
||||
|
@ -177,26 +178,28 @@ withEachNg1Version(() => {
|
|||
beforeEach(() => registry.unregisterAllApplications());
|
||||
afterEach(() => registry.unregisterAllApplications());
|
||||
|
||||
it('should add testabilities hook when creating components', () => {
|
||||
fixmeIvy('FW-561: Runtime compiler is not loaded') &&
|
||||
it('should add testabilities hook when creating components', () => {
|
||||
|
||||
let registry = TestBed.get(TestabilityRegistry);
|
||||
adapter.createComponent([]);
|
||||
expect(registry.getAllTestabilities().length).toEqual(1);
|
||||
let registry = TestBed.get(TestabilityRegistry);
|
||||
adapter.createComponent([]);
|
||||
expect(registry.getAllTestabilities().length).toEqual(1);
|
||||
|
||||
adapter = getAdaptor(); // get a new adaptor to creat a new component
|
||||
adapter.createComponent([]);
|
||||
expect(registry.getAllTestabilities().length).toEqual(2);
|
||||
});
|
||||
adapter = getAdaptor(); // get a new adaptor to creat a new component
|
||||
adapter.createComponent([]);
|
||||
expect(registry.getAllTestabilities().length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should remove the testability hook when destroy a component', () => {
|
||||
const registry = TestBed.get(TestabilityRegistry);
|
||||
expect(registry.getAllTestabilities().length).toEqual(0);
|
||||
adapter.createComponent([]);
|
||||
expect(registry.getAllTestabilities().length).toEqual(1);
|
||||
adapter.registerCleanup();
|
||||
element.remove !();
|
||||
expect(registry.getAllTestabilities().length).toEqual(0);
|
||||
});
|
||||
fixmeIvy('FW-561: Runtime compiler is not loaded') &&
|
||||
it('should remove the testability hook when destroy a component', () => {
|
||||
const registry = TestBed.get(TestabilityRegistry);
|
||||
expect(registry.getAllTestabilities().length).toEqual(0);
|
||||
adapter.createComponent([]);
|
||||
expect(registry.getAllTestabilities().length).toEqual(1);
|
||||
adapter.registerCleanup();
|
||||
element.remove !();
|
||||
expect(registry.getAllTestabilities().length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -10,6 +10,7 @@ import {Component, Directive, ElementRef, Injector, Input, NgModule, NgZone, Sim
|
|||
import {async} from '@angular/core/testing';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
import {fixmeIvy} from '@angular/private/testing';
|
||||
import {UpgradeComponent, UpgradeModule, downgradeComponent} from '@angular/upgrade/static';
|
||||
import * as angular from '@angular/upgrade/static/src/common/angular1';
|
||||
|
||||
|
@ -75,57 +76,60 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should propagate changes to a downgraded component inside the ngZone', async(() => {
|
||||
let appComponent: AppComponent;
|
||||
fixmeIvy(
|
||||
'FW-712: Rendering is being run on next "animation frame" rather than "Zone.microTaskEmpty" trigger') &&
|
||||
it('should propagate changes to a downgraded component inside the ngZone', async(() => {
|
||||
const element = html('<my-app></my-app>');
|
||||
let appComponent: AppComponent;
|
||||
|
||||
@Component({selector: 'my-app', template: '<my-child [value]="value"></my-child>'})
|
||||
class AppComponent {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
value !: number;
|
||||
constructor() { appComponent = this; }
|
||||
}
|
||||
@Component({selector: 'my-app', template: '<my-child [value]="value"></my-child>'})
|
||||
class AppComponent {
|
||||
value?: number;
|
||||
constructor() { appComponent = this; }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-child',
|
||||
template: '<div>{{ valueFromPromise }}</div>',
|
||||
})
|
||||
class ChildComponent {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
valueFromPromise !: number;
|
||||
@Input()
|
||||
set value(v: number) { expect(NgZone.isInAngularZone()).toBe(true); }
|
||||
@Component({
|
||||
selector: 'my-child',
|
||||
template: '<div>{{ valueFromPromise }}</div>',
|
||||
})
|
||||
class ChildComponent {
|
||||
valueFromPromise?: number;
|
||||
@Input()
|
||||
set value(v: number) { expect(NgZone.isInAngularZone()).toBe(true); }
|
||||
|
||||
constructor(private zone: NgZone) {}
|
||||
constructor(private zone: NgZone) {}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes['value'].isFirstChange()) return;
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes['value'].isFirstChange()) return;
|
||||
|
||||
this.zone.onMicrotaskEmpty.subscribe(
|
||||
() => { expect(element.textContent).toEqual('5'); });
|
||||
// HACK(ivy): Using setTimeout allows this test to pass but hides the ivy renderer
|
||||
// timing BC.
|
||||
// setTimeout(() => expect(element.textContent).toEqual('5'), 0);
|
||||
this.zone.onMicrotaskEmpty.subscribe(
|
||||
() => { expect(element.textContent).toEqual('5'); });
|
||||
|
||||
Promise.resolve().then(() => this.valueFromPromise = changes['value'].currentValue);
|
||||
}
|
||||
}
|
||||
// Create a micro-task to update the value to be rendered asynchronously.
|
||||
Promise.resolve().then(
|
||||
() => this.valueFromPromise = changes['value'].currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent, ChildComponent],
|
||||
entryComponents: [AppComponent],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [AppComponent, ChildComponent],
|
||||
entryComponents: [AppComponent],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const ng1Module = angular.module('ng1', []).directive(
|
||||
'myApp', downgradeComponent({component: AppComponent}));
|
||||
const ng1Module = angular.module('ng1', []).directive(
|
||||
'myApp', downgradeComponent({component: AppComponent}));
|
||||
|
||||
|
||||
const element = html('<my-app></my-app>');
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
appComponent.value = 5;
|
||||
});
|
||||
}));
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
appComponent.value = 5;
|
||||
});
|
||||
}));
|
||||
|
||||
// This test demonstrates https://github.com/angular/angular/issues/6385
|
||||
// which was invalidly fixed by https://github.com/angular/angular/pull/6386
|
||||
|
|
|
@ -10,6 +10,7 @@ import {Component, Directive, ElementRef, Injector, Input, NgModule, destroyPlat
|
|||
import {async} from '@angular/core/testing';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
import {fixmeIvy} from '@angular/private/testing';
|
||||
import {UpgradeComponent, UpgradeModule, downgradeComponent} from '@angular/upgrade/static';
|
||||
import * as angular from '@angular/upgrade/static/src/common/angular1';
|
||||
|
||||
|
@ -21,78 +22,82 @@ withEachNg1Version(() => {
|
|||
beforeEach(() => destroyPlatform());
|
||||
afterEach(() => destroyPlatform());
|
||||
|
||||
it('should instantiate ng2 in ng1 template and project content', async(() => {
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
||||
it('should instantiate ng2 in ng1 template and project content', async(() => {
|
||||
|
||||
// the ng2 component that will be used in ng1 (downgraded)
|
||||
@Component({selector: 'ng2', template: `{{ prop }}(<ng-content></ng-content>)`})
|
||||
class Ng2Component {
|
||||
prop = 'NG2';
|
||||
ngContent = 'ng2-content';
|
||||
}
|
||||
// the ng2 component that will be used in ng1 (downgraded)
|
||||
@Component({selector: 'ng2', template: `{{ prop }}(<ng-content></ng-content>)`})
|
||||
class Ng2Component {
|
||||
prop = 'NG2';
|
||||
ngContent = 'ng2-content';
|
||||
}
|
||||
|
||||
// our upgrade module to host the component to downgrade
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
// our upgrade module to host the component to downgrade
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
// the ng1 app module that will consume the downgraded component
|
||||
const ng1Module = angular
|
||||
.module('ng1', [])
|
||||
// create an ng1 facade of the ng2 component
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
$rootScope['prop'] = 'NG1';
|
||||
$rootScope['ngContent'] = 'ng1-content';
|
||||
});
|
||||
// the ng1 app module that will consume the downgraded component
|
||||
const ng1Module = angular
|
||||
.module('ng1', [])
|
||||
// create an ng1 facade of the ng2 component
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
$rootScope['prop'] = 'NG1';
|
||||
$rootScope['ngContent'] = 'ng1-content';
|
||||
});
|
||||
|
||||
const element = html('<div>{{ \'ng1[\' }}<ng2>~{{ ngContent }}~</ng2>{{ \']\' }}</div>');
|
||||
const element =
|
||||
html('<div>{{ \'ng1[\' }}<ng2>~{{ ngContent }}~</ng2>{{ \']\' }}</div>');
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
expect(document.body.textContent).toEqual('ng1[NG2(~ng1-content~)]');
|
||||
});
|
||||
}));
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
expect(document.body.textContent).toEqual('ng1[NG2(~ng1-content~)]');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should correctly project structural directives', async(() => {
|
||||
@Component({selector: 'ng2', template: 'ng2-{{ itemId }}(<ng-content></ng-content>)'})
|
||||
class Ng2Component {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() itemId !: string;
|
||||
}
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
||||
it('should correctly project structural directives', async(() => {
|
||||
@Component({selector: 'ng2', template: 'ng2-{{ itemId }}(<ng-content></ng-content>)'})
|
||||
class Ng2Component {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() itemId !: string;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const ng1Module = angular.module('ng1', [])
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
$rootScope['items'] = [
|
||||
{id: 'a', subitems: [1, 2, 3]}, {id: 'b', subitems: [4, 5, 6]},
|
||||
{id: 'c', subitems: [7, 8, 9]}
|
||||
];
|
||||
});
|
||||
const ng1Module =
|
||||
angular.module('ng1', [])
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
$rootScope['items'] = [
|
||||
{id: 'a', subitems: [1, 2, 3]}, {id: 'b', subitems: [4, 5, 6]},
|
||||
{id: 'c', subitems: [7, 8, 9]}
|
||||
];
|
||||
});
|
||||
|
||||
const element = html(`
|
||||
const element = html(`
|
||||
<ng2 ng-repeat="item in items" [item-id]="item.id">
|
||||
<div ng-repeat="subitem in item.subitems">{{ subitem }}</div>
|
||||
</ng2>
|
||||
`);
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2-a( 123 )ng2-b( 456 )ng2-c( 789 )');
|
||||
});
|
||||
}));
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2-a( 123 )ng2-b( 456 )ng2-c( 789 )');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should instantiate ng1 in ng2 template and project content', async(() => {
|
||||
|
||||
|
@ -140,38 +145,39 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should support multi-slot projection', async(() => {
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
||||
it('should support multi-slot projection', async(() => {
|
||||
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: '2a(<ng-content select=".ng1a"></ng-content>)' +
|
||||
'2b(<ng-content select=".ng1b"></ng-content>)'
|
||||
})
|
||||
class Ng2Component {
|
||||
constructor() {}
|
||||
}
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: '2a(<ng-content select=".ng1a"></ng-content>)' +
|
||||
'2b(<ng-content select=".ng1b"></ng-content>)'
|
||||
})
|
||||
class Ng2Component {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const ng1Module = angular.module('ng1', []).directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component}));
|
||||
const ng1Module = angular.module('ng1', []).directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component}));
|
||||
|
||||
// The ng-if on one of the projected children is here to make sure
|
||||
// the correct slot is targeted even with structural directives in play.
|
||||
const element = html(
|
||||
'<ng2><div ng-if="true" class="ng1a">1a</div><div' +
|
||||
' class="ng1b">1b</div></ng2>');
|
||||
// The ng-if on one of the projected children is here to make sure
|
||||
// the correct slot is targeted even with structural directives in play.
|
||||
const element = html(
|
||||
'<ng2><div ng-if="true" class="ng1a">1a</div><div' +
|
||||
' class="ng1b">1b</div></ng2>');
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
expect(document.body.textContent).toEqual('2a(1a)2b(1b)');
|
||||
});
|
||||
}));
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
expect(document.body.textContent).toEqual('2a(1a)2b(1b)');
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Compiler, Component, Compone
|
|||
import {async, fakeAsync, tick} from '@angular/core/testing';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
import {fixmeIvy} from '@angular/private/testing';
|
||||
import {UpgradeComponent, UpgradeModule, downgradeComponent} from '@angular/upgrade/static';
|
||||
import * as angular from '@angular/upgrade/static/src/common/angular1';
|
||||
|
||||
|
@ -21,104 +22,106 @@ withEachNg1Version(() => {
|
|||
beforeEach(() => destroyPlatform());
|
||||
afterEach(() => destroyPlatform());
|
||||
|
||||
it('should bind properties, events', async(() => {
|
||||
const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => {
|
||||
$rootScope['name'] = 'world';
|
||||
$rootScope['dataA'] = 'A';
|
||||
$rootScope['dataB'] = 'B';
|
||||
$rootScope['modelA'] = 'initModelA';
|
||||
$rootScope['modelB'] = 'initModelB';
|
||||
$rootScope['eventA'] = '?';
|
||||
$rootScope['eventB'] = '?';
|
||||
});
|
||||
fixmeIvy('FW-716: Error: [$rootScope:inprog] $digest already in progress') &&
|
||||
it('should bind properties, events', async(() => {
|
||||
const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => {
|
||||
$rootScope['name'] = 'world';
|
||||
$rootScope['dataA'] = 'A';
|
||||
$rootScope['dataB'] = 'B';
|
||||
$rootScope['modelA'] = 'initModelA';
|
||||
$rootScope['modelB'] = 'initModelB';
|
||||
$rootScope['eventA'] = '?';
|
||||
$rootScope['eventB'] = '?';
|
||||
});
|
||||
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
inputs: ['literal', 'interpolate', 'oneWayA', 'oneWayB', 'twoWayA', 'twoWayB'],
|
||||
outputs: [
|
||||
'eventA', 'eventB', 'twoWayAEmitter: twoWayAChange', 'twoWayBEmitter: twoWayBChange'
|
||||
],
|
||||
template: 'ignore: {{ignore}}; ' +
|
||||
'literal: {{literal}}; interpolate: {{interpolate}}; ' +
|
||||
'oneWayA: {{oneWayA}}; oneWayB: {{oneWayB}}; ' +
|
||||
'twoWayA: {{twoWayA}}; twoWayB: {{twoWayB}}; ({{ngOnChangesCount}})'
|
||||
})
|
||||
class Ng2Component implements OnChanges {
|
||||
ngOnChangesCount = 0;
|
||||
ignore = '-';
|
||||
literal = '?';
|
||||
interpolate = '?';
|
||||
oneWayA = '?';
|
||||
oneWayB = '?';
|
||||
twoWayA = '?';
|
||||
twoWayB = '?';
|
||||
eventA = new EventEmitter();
|
||||
eventB = new EventEmitter();
|
||||
twoWayAEmitter = new EventEmitter();
|
||||
twoWayBEmitter = new EventEmitter();
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
inputs: ['literal', 'interpolate', 'oneWayA', 'oneWayB', 'twoWayA', 'twoWayB'],
|
||||
outputs: [
|
||||
'eventA', 'eventB', 'twoWayAEmitter: twoWayAChange',
|
||||
'twoWayBEmitter: twoWayBChange'
|
||||
],
|
||||
template: 'ignore: {{ignore}}; ' +
|
||||
'literal: {{literal}}; interpolate: {{interpolate}}; ' +
|
||||
'oneWayA: {{oneWayA}}; oneWayB: {{oneWayB}}; ' +
|
||||
'twoWayA: {{twoWayA}}; twoWayB: {{twoWayB}}; ({{ngOnChangesCount}})'
|
||||
})
|
||||
class Ng2Component implements OnChanges {
|
||||
ngOnChangesCount = 0;
|
||||
ignore = '-';
|
||||
literal = '?';
|
||||
interpolate = '?';
|
||||
oneWayA = '?';
|
||||
oneWayB = '?';
|
||||
twoWayA = '?';
|
||||
twoWayB = '?';
|
||||
eventA = new EventEmitter();
|
||||
eventB = new EventEmitter();
|
||||
twoWayAEmitter = new EventEmitter();
|
||||
twoWayBEmitter = new EventEmitter();
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
const assert = (prop: string, value: any) => {
|
||||
const propVal = (this as any)[prop];
|
||||
if (propVal != value) {
|
||||
throw new Error(`Expected: '${prop}' to be '${value}' but was '${propVal}'`);
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
const assert = (prop: string, value: any) => {
|
||||
const propVal = (this as any)[prop];
|
||||
if (propVal != value) {
|
||||
throw new Error(`Expected: '${prop}' to be '${value}' but was '${propVal}'`);
|
||||
}
|
||||
};
|
||||
|
||||
const assertChange = (prop: string, value: any) => {
|
||||
assert(prop, value);
|
||||
if (!changes[prop]) {
|
||||
throw new Error(`Changes record for '${prop}' not found.`);
|
||||
}
|
||||
const actualValue = changes[prop].currentValue;
|
||||
if (actualValue != value) {
|
||||
throw new Error(
|
||||
`Expected changes record for'${prop}' to be '${value}' but was '${actualValue}'`);
|
||||
}
|
||||
};
|
||||
|
||||
switch (this.ngOnChangesCount++) {
|
||||
case 0:
|
||||
assert('ignore', '-');
|
||||
assertChange('literal', 'Text');
|
||||
assertChange('interpolate', 'Hello world');
|
||||
assertChange('oneWayA', 'A');
|
||||
assertChange('oneWayB', 'B');
|
||||
assertChange('twoWayA', 'initModelA');
|
||||
assertChange('twoWayB', 'initModelB');
|
||||
|
||||
this.twoWayAEmitter.emit('newA');
|
||||
this.twoWayBEmitter.emit('newB');
|
||||
this.eventA.emit('aFired');
|
||||
this.eventB.emit('bFired');
|
||||
break;
|
||||
case 1:
|
||||
assertChange('twoWayA', 'newA');
|
||||
assertChange('twoWayB', 'newB');
|
||||
break;
|
||||
case 2:
|
||||
assertChange('interpolate', 'Hello everyone');
|
||||
break;
|
||||
default:
|
||||
throw new Error('Called too many times! ' + JSON.stringify(changes));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const assertChange = (prop: string, value: any) => {
|
||||
assert(prop, value);
|
||||
if (!changes[prop]) {
|
||||
throw new Error(`Changes record for '${prop}' not found.`);
|
||||
}
|
||||
const actualValue = changes[prop].currentValue;
|
||||
if (actualValue != value) {
|
||||
throw new Error(
|
||||
`Expected changes record for'${prop}' to be '${value}' but was '${actualValue}'`);
|
||||
}
|
||||
};
|
||||
|
||||
switch (this.ngOnChangesCount++) {
|
||||
case 0:
|
||||
assert('ignore', '-');
|
||||
assertChange('literal', 'Text');
|
||||
assertChange('interpolate', 'Hello world');
|
||||
assertChange('oneWayA', 'A');
|
||||
assertChange('oneWayB', 'B');
|
||||
assertChange('twoWayA', 'initModelA');
|
||||
assertChange('twoWayB', 'initModelB');
|
||||
|
||||
this.twoWayAEmitter.emit('newA');
|
||||
this.twoWayBEmitter.emit('newB');
|
||||
this.eventA.emit('aFired');
|
||||
this.eventB.emit('bFired');
|
||||
break;
|
||||
case 1:
|
||||
assertChange('twoWayA', 'newA');
|
||||
assertChange('twoWayB', 'newB');
|
||||
break;
|
||||
case 2:
|
||||
assertChange('interpolate', 'Hello everyone');
|
||||
break;
|
||||
default:
|
||||
throw new Error('Called too many times! ' + JSON.stringify(changes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ng1Module.directive('ng2', downgradeComponent({
|
||||
component: Ng2Component,
|
||||
}));
|
||||
ng1Module.directive('ng2', downgradeComponent({
|
||||
component: Ng2Component,
|
||||
}));
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const element = html(`
|
||||
const element = html(`
|
||||
<div>
|
||||
<ng2 literal="Text" interpolate="Hello {{name}}"
|
||||
bind-one-way-a="dataA" [one-way-b]="dataB"
|
||||
|
@ -127,23 +130,23 @@ withEachNg1Version(() => {
|
|||
| modelA: {{modelA}}; modelB: {{modelB}}; eventA: {{eventA}}; eventB: {{eventB}};
|
||||
</div>`);
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toEqual(
|
||||
'ignore: -; ' +
|
||||
'literal: Text; interpolate: Hello world; ' +
|
||||
'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (2) | ' +
|
||||
'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;');
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toEqual(
|
||||
'ignore: -; ' +
|
||||
'literal: Text; interpolate: Hello world; ' +
|
||||
'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (2) | ' +
|
||||
'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;');
|
||||
|
||||
$apply(upgrade, 'name = "everyone"');
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toEqual(
|
||||
'ignore: -; ' +
|
||||
'literal: Text; interpolate: Hello everyone; ' +
|
||||
'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (3) | ' +
|
||||
'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;');
|
||||
});
|
||||
}));
|
||||
$apply(upgrade, 'name = "everyone"');
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toEqual(
|
||||
'ignore: -; ' +
|
||||
'literal: Text; interpolate: Hello everyone; ' +
|
||||
'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (3) | ' +
|
||||
'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should bind properties to onpush components', async(() => {
|
||||
const ng1Module = angular.module('ng1', []).run(
|
||||
|
@ -186,57 +189,58 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should support two-way binding and event listener', async(() => {
|
||||
const listenerSpy = jasmine.createSpy('$rootScope.listener');
|
||||
const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => {
|
||||
$rootScope['value'] = 'world';
|
||||
$rootScope['listener'] = listenerSpy;
|
||||
});
|
||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly') &&
|
||||
it('should support two-way binding and event listener', async(() => {
|
||||
const listenerSpy = jasmine.createSpy('$rootScope.listener');
|
||||
const ng1Module = angular.module('ng1', []).run(($rootScope: angular.IScope) => {
|
||||
$rootScope['value'] = 'world';
|
||||
$rootScope['listener'] = listenerSpy;
|
||||
});
|
||||
|
||||
@Component({selector: 'ng2', template: `model: {{model}};`})
|
||||
class Ng2Component implements OnChanges {
|
||||
ngOnChangesCount = 0;
|
||||
@Input() model = '?';
|
||||
@Output() modelChange = new EventEmitter();
|
||||
@Component({selector: 'ng2', template: `model: {{model}};`})
|
||||
class Ng2Component implements OnChanges {
|
||||
ngOnChangesCount = 0;
|
||||
@Input() model = '?';
|
||||
@Output() modelChange = new EventEmitter();
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
switch (this.ngOnChangesCount++) {
|
||||
case 0:
|
||||
expect(changes.model.currentValue).toBe('world');
|
||||
this.modelChange.emit('newC');
|
||||
break;
|
||||
case 1:
|
||||
expect(changes.model.currentValue).toBe('newC');
|
||||
break;
|
||||
default:
|
||||
throw new Error('Called too many times! ' + JSON.stringify(changes));
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
switch (this.ngOnChangesCount++) {
|
||||
case 0:
|
||||
expect(changes.model.currentValue).toBe('world');
|
||||
this.modelChange.emit('newC');
|
||||
break;
|
||||
case 1:
|
||||
expect(changes.model.currentValue).toBe('newC');
|
||||
break;
|
||||
default:
|
||||
throw new Error('Called too many times! ' + JSON.stringify(changes));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ng1Module.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
ng1Module.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const element = html(`
|
||||
const element = html(`
|
||||
<div>
|
||||
<ng2 [(model)]="value" (model-change)="listener($event)"></ng2>
|
||||
| value: {{value}}
|
||||
</div>
|
||||
`);
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
expect(multiTrim(element.textContent)).toEqual('model: newC; | value: newC');
|
||||
expect(listenerSpy).toHaveBeenCalledWith('newC');
|
||||
});
|
||||
}));
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
||||
expect(multiTrim(element.textContent)).toEqual('model: newC; | value: newC');
|
||||
expect(listenerSpy).toHaveBeenCalledWith('newC');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should run change-detection on every digest (by default)', async(() => {
|
||||
let ng2Component: Ng2Component;
|
||||
|
@ -400,65 +404,66 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should initialize inputs in time for `ngOnChanges`', async(() => {
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: `
|
||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly') &&
|
||||
it('should initialize inputs in time for `ngOnChanges`', async(() => {
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: `
|
||||
ngOnChangesCount: {{ ngOnChangesCount }} |
|
||||
firstChangesCount: {{ firstChangesCount }} |
|
||||
initialValue: {{ initialValue }}`
|
||||
})
|
||||
class Ng2Component implements OnChanges {
|
||||
ngOnChangesCount = 0;
|
||||
firstChangesCount = 0;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
initialValue !: string;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() foo !: string;
|
||||
})
|
||||
class Ng2Component implements OnChanges {
|
||||
ngOnChangesCount = 0;
|
||||
firstChangesCount = 0;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
initialValue !: string;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() foo !: string;
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
this.ngOnChangesCount++;
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
this.ngOnChangesCount++;
|
||||
|
||||
if (this.ngOnChangesCount === 1) {
|
||||
this.initialValue = this.foo;
|
||||
if (this.ngOnChangesCount === 1) {
|
||||
this.initialValue = this.foo;
|
||||
}
|
||||
|
||||
if (changes['foo'] && changes['foo'].isFirstChange()) {
|
||||
this.firstChangesCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changes['foo'] && changes['foo'].isFirstChange()) {
|
||||
this.firstChangesCount++;
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
const ng1Module = angular.module('ng1', []).directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component}));
|
||||
|
||||
const ng1Module = angular.module('ng1', []).directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component}));
|
||||
|
||||
const element = html(`
|
||||
const element = html(`
|
||||
<ng2 [foo]="'foo'"></ng2>
|
||||
<ng2 foo="bar"></ng2>
|
||||
<ng2 [foo]="'baz'" ng-if="true"></ng2>
|
||||
<ng2 foo="qux" ng-if="true"></ng2>
|
||||
`);
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
const nodes = element.querySelectorAll('ng2');
|
||||
const expectedTextWith = (value: string) =>
|
||||
`ngOnChangesCount: 1 | firstChangesCount: 1 | initialValue: ${value}`;
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
const nodes = element.querySelectorAll('ng2');
|
||||
const expectedTextWith = (value: string) =>
|
||||
`ngOnChangesCount: 1 | firstChangesCount: 1 | initialValue: ${value}`;
|
||||
|
||||
expect(multiTrim(nodes[0].textContent)).toBe(expectedTextWith('foo'));
|
||||
expect(multiTrim(nodes[1].textContent)).toBe(expectedTextWith('bar'));
|
||||
expect(multiTrim(nodes[2].textContent)).toBe(expectedTextWith('baz'));
|
||||
expect(multiTrim(nodes[3].textContent)).toBe(expectedTextWith('qux'));
|
||||
});
|
||||
}));
|
||||
expect(multiTrim(nodes[0].textContent)).toBe(expectedTextWith('foo'));
|
||||
expect(multiTrim(nodes[1].textContent)).toBe(expectedTextWith('bar'));
|
||||
expect(multiTrim(nodes[2].textContent)).toBe(expectedTextWith('baz'));
|
||||
expect(multiTrim(nodes[3].textContent)).toBe(expectedTextWith('qux'));
|
||||
});
|
||||
}));
|
||||
|
||||
it('should bind to ng-model', async(() => {
|
||||
const ng1Module = angular.module('ng1', []).run(
|
||||
|
@ -515,94 +520,96 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should properly run cleanup when ng1 directive is destroyed', async(() => {
|
||||
fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') &&
|
||||
it('should properly run cleanup when ng1 directive is destroyed', async(() => {
|
||||
|
||||
let destroyed = false;
|
||||
@Component({selector: 'ng2', template: 'test'})
|
||||
class Ng2Component implements OnDestroy {
|
||||
ngOnDestroy() { destroyed = true; }
|
||||
}
|
||||
let destroyed = false;
|
||||
@Component({selector: 'ng2', template: 'test'})
|
||||
class Ng2Component implements OnDestroy {
|
||||
ngOnDestroy() { destroyed = true; }
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const ng1Module =
|
||||
angular.module('ng1', [])
|
||||
.directive(
|
||||
'ng1',
|
||||
() => { return {template: '<div ng-if="!destroyIt"><ng2></ng2></div>'}; })
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
const element = html('<ng1></ng1>');
|
||||
platformBrowserDynamic().bootstrapModule(Ng2Module).then((ref) => {
|
||||
const adapter = ref.injector.get(UpgradeModule) as UpgradeModule;
|
||||
adapter.bootstrap(element, [ng1Module.name]);
|
||||
expect(element.textContent).toContain('test');
|
||||
expect(destroyed).toBe(false);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [])
|
||||
.directive(
|
||||
'ng1',
|
||||
() => { return {template: '<div ng-if="!destroyIt"><ng2></ng2></div>'}; })
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
const element = html('<ng1></ng1>');
|
||||
platformBrowserDynamic().bootstrapModule(Ng2Module).then((ref) => {
|
||||
const adapter = ref.injector.get(UpgradeModule) as UpgradeModule;
|
||||
adapter.bootstrap(element, [ng1Module.name]);
|
||||
expect(element.textContent).toContain('test');
|
||||
expect(destroyed).toBe(false);
|
||||
|
||||
const $rootScope = adapter.$injector.get('$rootScope');
|
||||
$rootScope.$apply('destroyIt = true');
|
||||
const $rootScope = adapter.$injector.get('$rootScope');
|
||||
$rootScope.$apply('destroyIt = true');
|
||||
|
||||
expect(element.textContent).not.toContain('test');
|
||||
expect(destroyed).toBe(true);
|
||||
});
|
||||
}));
|
||||
expect(element.textContent).not.toContain('test');
|
||||
expect(destroyed).toBe(true);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should properly run cleanup with multiple levels of nesting', async(() => {
|
||||
let destroyed = false;
|
||||
fixmeIvy('FW-642: ASSERTION ERROR: Slot should have been initialized to NO_CHANGE') &&
|
||||
it('should properly run cleanup with multiple levels of nesting', async(() => {
|
||||
let destroyed = false;
|
||||
|
||||
@Component({
|
||||
selector: 'ng2-outer',
|
||||
template: '<div *ngIf="!destroyIt"><ng1></ng1></div>',
|
||||
})
|
||||
class Ng2OuterComponent {
|
||||
@Input() destroyIt = false;
|
||||
}
|
||||
@Component({
|
||||
selector: 'ng2-outer',
|
||||
template: '<div *ngIf="!destroyIt"><ng1></ng1></div>',
|
||||
})
|
||||
class Ng2OuterComponent {
|
||||
@Input() destroyIt = false;
|
||||
}
|
||||
|
||||
@Component({selector: 'ng2-inner', template: 'test'})
|
||||
class Ng2InnerComponent implements OnDestroy {
|
||||
ngOnDestroy() { destroyed = true; }
|
||||
}
|
||||
@Component({selector: 'ng2-inner', template: 'test'})
|
||||
class Ng2InnerComponent implements OnDestroy {
|
||||
ngOnDestroy() { destroyed = true; }
|
||||
}
|
||||
|
||||
@Directive({selector: 'ng1'})
|
||||
class Ng1ComponentFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1', elementRef, injector);
|
||||
}
|
||||
}
|
||||
@Directive({selector: 'ng1'})
|
||||
class Ng1ComponentFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1', elementRef, injector);
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng1ComponentFacade, Ng2InnerComponent, Ng2OuterComponent],
|
||||
entryComponents: [Ng2InnerComponent, Ng2OuterComponent],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng1ComponentFacade, Ng2InnerComponent, Ng2OuterComponent],
|
||||
entryComponents: [Ng2InnerComponent, Ng2OuterComponent],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const ng1Module =
|
||||
angular.module('ng1', [])
|
||||
.directive('ng1', () => ({template: '<ng2-inner></ng2-inner>'}))
|
||||
.directive('ng2Inner', downgradeComponent({component: Ng2InnerComponent}))
|
||||
.directive('ng2Outer', downgradeComponent({component: Ng2OuterComponent}));
|
||||
const ng1Module =
|
||||
angular.module('ng1', [])
|
||||
.directive('ng1', () => ({template: '<ng2-inner></ng2-inner>'}))
|
||||
.directive('ng2Inner', downgradeComponent({component: Ng2InnerComponent}))
|
||||
.directive('ng2Outer', downgradeComponent({component: Ng2OuterComponent}));
|
||||
|
||||
const element = html('<ng2-outer [destroy-it]="destroyIt"></ng2-outer>');
|
||||
const element = html('<ng2-outer [destroy-it]="destroyIt"></ng2-outer>');
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
expect(element.textContent).toBe('test');
|
||||
expect(destroyed).toBe(false);
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
expect(element.textContent).toBe('test');
|
||||
expect(destroyed).toBe(false);
|
||||
|
||||
$apply(upgrade, 'destroyIt = true');
|
||||
$apply(upgrade, 'destroyIt = true');
|
||||
|
||||
expect(element.textContent).toBe('');
|
||||
expect(destroyed).toBe(true);
|
||||
});
|
||||
}));
|
||||
expect(element.textContent).toBe('');
|
||||
expect(destroyed).toBe(true);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should work when compiled outside the dom (by fallback to the root ng2.injector)',
|
||||
async(() => {
|
||||
|
@ -704,86 +711,88 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should respect hierarchical dependency injection for ng2', async(() => {
|
||||
@Component({selector: 'parent', template: 'parent(<ng-content></ng-content>)'})
|
||||
class ParentComponent {
|
||||
}
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
||||
it('should respect hierarchical dependency injection for ng2', async(() => {
|
||||
@Component({selector: 'parent', template: 'parent(<ng-content></ng-content>)'})
|
||||
class ParentComponent {
|
||||
}
|
||||
|
||||
@Component({selector: 'child', template: 'child'})
|
||||
class ChildComponent {
|
||||
constructor(parent: ParentComponent) {}
|
||||
}
|
||||
@Component({selector: 'child', template: 'child'})
|
||||
class ChildComponent {
|
||||
constructor(parent: ParentComponent) {}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [ParentComponent, ChildComponent],
|
||||
entryComponents: [ParentComponent, ChildComponent],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [ParentComponent, ChildComponent],
|
||||
entryComponents: [ParentComponent, ChildComponent],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const ng1Module =
|
||||
angular.module('ng1', [])
|
||||
.directive('parent', downgradeComponent({component: ParentComponent}))
|
||||
.directive('child', downgradeComponent({component: ChildComponent}));
|
||||
const ng1Module =
|
||||
angular.module('ng1', [])
|
||||
.directive('parent', downgradeComponent({component: ParentComponent}))
|
||||
.directive('child', downgradeComponent({component: ChildComponent}));
|
||||
|
||||
const element = html('<parent><child></child></parent>');
|
||||
const element = html('<parent><child></child></parent>');
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
expect(multiTrim(document.body.textContent)).toBe('parent(child)');
|
||||
});
|
||||
}));
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
expect(multiTrim(document.body.textContent)).toBe('parent(child)');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should work with ng2 lazy loaded components', async(() => {
|
||||
fixmeIvy('FW-561: Runtime compiler is not loaded') &&
|
||||
it('should work with ng2 lazy loaded components', async(() => {
|
||||
|
||||
let componentInjector: Injector;
|
||||
let componentInjector: Injector;
|
||||
|
||||
@Component({selector: 'ng2', template: ''})
|
||||
class Ng2Component {
|
||||
constructor(injector: Injector) { componentInjector = injector; }
|
||||
}
|
||||
@Component({selector: 'ng2', template: ''})
|
||||
class Ng2Component {
|
||||
constructor(injector: Injector) { componentInjector = injector; }
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
@Component({template: ''})
|
||||
class LazyLoadedComponent {
|
||||
constructor(public module: NgModuleRef<any>) {}
|
||||
}
|
||||
@Component({template: ''})
|
||||
class LazyLoadedComponent {
|
||||
constructor(public module: NgModuleRef<any>) {}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [LazyLoadedComponent],
|
||||
entryComponents: [LazyLoadedComponent],
|
||||
})
|
||||
class LazyLoadedModule {
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [LazyLoadedComponent],
|
||||
entryComponents: [LazyLoadedComponent],
|
||||
})
|
||||
class LazyLoadedModule {
|
||||
}
|
||||
|
||||
const ng1Module = angular.module('ng1', []).directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component}));
|
||||
const ng1Module = angular.module('ng1', []).directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component}));
|
||||
|
||||
const element = html('<ng2></ng2>');
|
||||
const element = html('<ng2></ng2>');
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
const modInjector = upgrade.injector;
|
||||
// Emulate the router lazy loading a module and creating a component
|
||||
const compiler = modInjector.get(Compiler);
|
||||
const modFactory = compiler.compileModuleSync(LazyLoadedModule);
|
||||
const childMod = modFactory.create(modInjector);
|
||||
const cmpFactory =
|
||||
childMod.componentFactoryResolver.resolveComponentFactory(LazyLoadedComponent) !;
|
||||
const lazyCmp = cmpFactory.create(componentInjector);
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
const modInjector = upgrade.injector;
|
||||
// Emulate the router lazy loading a module and creating a component
|
||||
const compiler = modInjector.get(Compiler);
|
||||
const modFactory = compiler.compileModuleSync(LazyLoadedModule);
|
||||
const childMod = modFactory.create(modInjector);
|
||||
const cmpFactory =
|
||||
childMod.componentFactoryResolver.resolveComponentFactory(LazyLoadedComponent) !;
|
||||
const lazyCmp = cmpFactory.create(componentInjector);
|
||||
|
||||
expect(lazyCmp.instance.module.injector).toBe(childMod.injector);
|
||||
});
|
||||
expect(lazyCmp.instance.module.injector).toBe(childMod.injector);
|
||||
});
|
||||
|
||||
}));
|
||||
}));
|
||||
|
||||
it('should throw if `downgradedModule` is specified', async(() => {
|
||||
@Component({selector: 'ng2', template: ''})
|
||||
|
|
|
@ -11,6 +11,7 @@ import {async, fakeAsync, tick} from '@angular/core/testing';
|
|||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
import {browserDetection} from '@angular/platform-browser/testing/src/browser_util';
|
||||
import {fixmeIvy} from '@angular/private/testing';
|
||||
import {downgradeComponent, downgradeModule} from '@angular/upgrade/static';
|
||||
import * as angular from '@angular/upgrade/static/src/common/angular1';
|
||||
import {$EXCEPTION_HANDLER, $ROOT_SCOPE, INJECTOR_KEY, LAZY_MODULE_REF} from '@angular/upgrade/static/src/common/constants';
|
||||
|
@ -131,62 +132,64 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should support using an upgraded service', async(() => {
|
||||
class Ng2Service {
|
||||
constructor(@Inject('ng1Value') private ng1Value: string) {}
|
||||
getValue = () => `${this.ng1Value}-bar`;
|
||||
}
|
||||
fixmeIvy('FW-718: upgraded service not being initialized correctly on the injector') &&
|
||||
it('should support using an upgraded service', async(() => {
|
||||
class Ng2Service {
|
||||
constructor(@Inject('ng1Value') private ng1Value: string) {}
|
||||
getValue = () => `${this.ng1Value}-bar`;
|
||||
}
|
||||
|
||||
@Component({selector: 'ng2', template: '{{ value }}'})
|
||||
class Ng2Component {
|
||||
value: string;
|
||||
constructor(ng2Service: Ng2Service) { this.value = ng2Service.getValue(); }
|
||||
}
|
||||
@Component({selector: 'ng2', template: '{{ value }}'})
|
||||
class Ng2Component {
|
||||
value: string;
|
||||
constructor(ng2Service: Ng2Service) { this.value = ng2Service.getValue(); }
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule],
|
||||
providers: [
|
||||
Ng2Service,
|
||||
{
|
||||
provide: 'ng1Value',
|
||||
useFactory: (i: angular.IInjectorService) => i.get('ng1Value'),
|
||||
deps: ['$injector'],
|
||||
},
|
||||
],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule],
|
||||
providers: [
|
||||
Ng2Service,
|
||||
{
|
||||
provide: 'ng1Value',
|
||||
useFactory: (i: angular.IInjectorService) => i.get('ng1Value'),
|
||||
deps: ['$injector'],
|
||||
},
|
||||
],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) =>
|
||||
platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module);
|
||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [lazyModuleName])
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component, propagateDigest}))
|
||||
.value('ng1Value', 'foo');
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) =>
|
||||
platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module);
|
||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [lazyModuleName])
|
||||
.directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component, propagateDigest}))
|
||||
.value('ng1Value', 'foo');
|
||||
|
||||
const element = html('<div><ng2 ng-if="loadNg2"></ng2></div>');
|
||||
const $injector = angular.bootstrap(element, [ng1Module.name]);
|
||||
const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService;
|
||||
const element = html('<div><ng2 ng-if="loadNg2"></ng2></div>');
|
||||
const $injector = angular.bootstrap(element, [ng1Module.name]);
|
||||
const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService;
|
||||
|
||||
expect(element.textContent).toBe('');
|
||||
expect(() => $injector.get(INJECTOR_KEY)).toThrowError();
|
||||
expect(element.textContent).toBe('');
|
||||
expect(() => $injector.get(INJECTOR_KEY)).toThrowError();
|
||||
|
||||
$rootScope.$apply('loadNg2 = true');
|
||||
expect(element.textContent).toBe('');
|
||||
expect(() => $injector.get(INJECTOR_KEY)).toThrowError();
|
||||
$rootScope.$apply('loadNg2 = true');
|
||||
expect(element.textContent).toBe('');
|
||||
expect(() => $injector.get(INJECTOR_KEY)).toThrowError();
|
||||
|
||||
// Wait for the module to be bootstrapped.
|
||||
setTimeout(() => {
|
||||
expect(() => $injector.get(INJECTOR_KEY)).not.toThrow();
|
||||
// Wait for the module to be bootstrapped.
|
||||
setTimeout(() => {
|
||||
expect(() => $injector.get(INJECTOR_KEY)).not.toThrow();
|
||||
|
||||
// Wait for `$evalAsync()` to propagate inputs.
|
||||
setTimeout(() => expect(element.textContent).toBe('foo-bar'));
|
||||
});
|
||||
}));
|
||||
// Wait for `$evalAsync()` to propagate inputs.
|
||||
setTimeout(() => expect(element.textContent).toBe('foo-bar'));
|
||||
});
|
||||
}));
|
||||
|
||||
it('should create components inside the Angular zone', async(() => {
|
||||
@Component({selector: 'ng2', template: 'In the zone: {{ inTheZone }}'})
|
||||
|
@ -222,99 +225,103 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should destroy components inside the Angular zone', async(() => {
|
||||
let destroyedInTheZone = false;
|
||||
fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') &&
|
||||
it('should destroy components inside the Angular zone', async(() => {
|
||||
let destroyedInTheZone = false;
|
||||
|
||||
@Component({selector: 'ng2', template: ''})
|
||||
class Ng2Component implements OnDestroy {
|
||||
ngOnDestroy() { destroyedInTheZone = NgZone.isInAngularZone(); }
|
||||
}
|
||||
@Component({selector: 'ng2', template: ''})
|
||||
class Ng2Component implements OnDestroy {
|
||||
ngOnDestroy() { destroyedInTheZone = NgZone.isInAngularZone(); }
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) =>
|
||||
platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module);
|
||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [lazyModuleName])
|
||||
.directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component, propagateDigest}));
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) =>
|
||||
platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module);
|
||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [lazyModuleName])
|
||||
.directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component, propagateDigest}));
|
||||
|
||||
const element = html('<ng2 ng-if="!hideNg2"></ng2>');
|
||||
const $injector = angular.bootstrap(element, [ng1Module.name]);
|
||||
const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService;
|
||||
const element = html('<ng2 ng-if="!hideNg2"></ng2>');
|
||||
const $injector = angular.bootstrap(element, [ng1Module.name]);
|
||||
const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService;
|
||||
|
||||
// Wait for the module to be bootstrapped.
|
||||
setTimeout(() => {
|
||||
$rootScope.$apply('hideNg2 = true');
|
||||
expect(destroyedInTheZone).toBe(true);
|
||||
});
|
||||
}));
|
||||
// Wait for the module to be bootstrapped.
|
||||
setTimeout(() => {
|
||||
$rootScope.$apply('hideNg2 = true');
|
||||
expect(destroyedInTheZone).toBe(true);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should propagate input changes inside the Angular zone', async(() => {
|
||||
let ng2Component: Ng2Component;
|
||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly') &&
|
||||
it('should propagate input changes inside the Angular zone', async(() => {
|
||||
let ng2Component: Ng2Component;
|
||||
|
||||
@Component({selector: 'ng2', template: ''})
|
||||
class Ng2Component implements OnChanges {
|
||||
@Input() attrInput = 'foo';
|
||||
@Input() propInput = 'foo';
|
||||
@Component({selector: 'ng2', template: ''})
|
||||
class Ng2Component implements OnChanges {
|
||||
@Input() attrInput = 'foo';
|
||||
@Input() propInput = 'foo';
|
||||
|
||||
constructor() { ng2Component = this; }
|
||||
ngOnChanges() {}
|
||||
}
|
||||
constructor() { ng2Component = this; }
|
||||
ngOnChanges() {}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) =>
|
||||
platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module);
|
||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [lazyModuleName])
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component, propagateDigest}))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
$rootScope.attrVal = 'bar';
|
||||
$rootScope.propVal = 'bar';
|
||||
});
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) =>
|
||||
platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module);
|
||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [lazyModuleName])
|
||||
.directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component, propagateDigest}))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
$rootScope.attrVal = 'bar';
|
||||
$rootScope.propVal = 'bar';
|
||||
});
|
||||
|
||||
const element = html('<ng2 attr-input="{{ attrVal }}" [prop-input]="propVal"></ng2>');
|
||||
const $injector = angular.bootstrap(element, [ng1Module.name]);
|
||||
const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService;
|
||||
const element =
|
||||
html('<ng2 attr-input="{{ attrVal }}" [prop-input]="propVal"></ng2>');
|
||||
const $injector = angular.bootstrap(element, [ng1Module.name]);
|
||||
const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService;
|
||||
|
||||
setTimeout(() => { // Wait for the module to be bootstrapped.
|
||||
setTimeout(() => { // Wait for `$evalAsync()` to propagate inputs.
|
||||
const expectToBeInNgZone = () => expect(NgZone.isInAngularZone()).toBe(true);
|
||||
const changesSpy =
|
||||
spyOn(ng2Component, 'ngOnChanges').and.callFake(expectToBeInNgZone);
|
||||
setTimeout(() => { // Wait for the module to be bootstrapped.
|
||||
setTimeout(() => { // Wait for `$evalAsync()` to propagate inputs.
|
||||
const expectToBeInNgZone = () => expect(NgZone.isInAngularZone()).toBe(true);
|
||||
const changesSpy =
|
||||
spyOn(ng2Component, 'ngOnChanges').and.callFake(expectToBeInNgZone);
|
||||
|
||||
expect(ng2Component.attrInput).toBe('bar');
|
||||
expect(ng2Component.propInput).toBe('bar');
|
||||
expect(ng2Component.attrInput).toBe('bar');
|
||||
expect(ng2Component.propInput).toBe('bar');
|
||||
|
||||
$rootScope.$apply('attrVal = "baz"');
|
||||
expect(ng2Component.attrInput).toBe('baz');
|
||||
expect(ng2Component.propInput).toBe('bar');
|
||||
expect(changesSpy).toHaveBeenCalledTimes(1);
|
||||
$rootScope.$apply('attrVal = "baz"');
|
||||
expect(ng2Component.attrInput).toBe('baz');
|
||||
expect(ng2Component.propInput).toBe('bar');
|
||||
expect(changesSpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
$rootScope.$apply('propVal = "qux"');
|
||||
expect(ng2Component.attrInput).toBe('baz');
|
||||
expect(ng2Component.propInput).toBe('qux');
|
||||
expect(changesSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
}));
|
||||
$rootScope.$apply('propVal = "qux"');
|
||||
expect(ng2Component.attrInput).toBe('baz');
|
||||
expect(ng2Component.propInput).toBe('qux');
|
||||
expect(changesSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should wire up the component for change detection', async(() => {
|
||||
@Component(
|
||||
|
@ -358,209 +365,215 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should run the lifecycle hooks in the correct order', async(() => {
|
||||
const logs: string[] = [];
|
||||
let rootScope: angular.IRootScopeService;
|
||||
fixmeIvy('FW-715: ngOnChanges being called a second time unexpectedly') &&
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
||||
it('should run the lifecycle hooks in the correct order', async(() => {
|
||||
const logs: string[] = [];
|
||||
let rootScope: angular.IRootScopeService;
|
||||
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: `
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: `
|
||||
{{ value }}
|
||||
<button (click)="value = 'qux'"></button>
|
||||
<ng-content></ng-content>
|
||||
`
|
||||
})
|
||||
class Ng2Component implements AfterContentChecked,
|
||||
AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, OnChanges, OnDestroy,
|
||||
OnInit {
|
||||
@Input() value = 'foo';
|
||||
})
|
||||
class Ng2Component implements AfterContentChecked,
|
||||
AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, OnChanges, OnDestroy,
|
||||
OnInit {
|
||||
@Input() value = 'foo';
|
||||
|
||||
ngAfterContentChecked() { this.log('AfterContentChecked'); }
|
||||
ngAfterContentInit() { this.log('AfterContentInit'); }
|
||||
ngAfterViewChecked() { this.log('AfterViewChecked'); }
|
||||
ngAfterViewInit() { this.log('AfterViewInit'); }
|
||||
ngDoCheck() { this.log('DoCheck'); }
|
||||
ngOnChanges() { this.log('OnChanges'); }
|
||||
ngOnDestroy() { this.log('OnDestroy'); }
|
||||
ngOnInit() { this.log('OnInit'); }
|
||||
ngAfterContentChecked() { this.log('AfterContentChecked'); }
|
||||
ngAfterContentInit() { this.log('AfterContentInit'); }
|
||||
ngAfterViewChecked() { this.log('AfterViewChecked'); }
|
||||
ngAfterViewInit() { this.log('AfterViewInit'); }
|
||||
ngDoCheck() { this.log('DoCheck'); }
|
||||
ngOnChanges() { this.log('OnChanges'); }
|
||||
ngOnDestroy() { this.log('OnDestroy'); }
|
||||
ngOnInit() { this.log('OnInit'); }
|
||||
|
||||
private log(hook: string) { logs.push(`${hook}(${this.value})`); }
|
||||
}
|
||||
private log(hook: string) { logs.push(`${hook}(${this.value})`); }
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) =>
|
||||
platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module);
|
||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [lazyModuleName])
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component, propagateDigest}))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
rootScope = $rootScope;
|
||||
rootScope.value = 'bar';
|
||||
});
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) =>
|
||||
platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module);
|
||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [lazyModuleName])
|
||||
.directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component, propagateDigest}))
|
||||
.run(($rootScope: angular.IRootScopeService) => {
|
||||
rootScope = $rootScope;
|
||||
rootScope.value = 'bar';
|
||||
});
|
||||
|
||||
const element =
|
||||
html('<div><ng2 value="{{ value }}" ng-if="!hideNg2">Content</ng2></div>');
|
||||
angular.bootstrap(element, [ng1Module.name]);
|
||||
const element =
|
||||
html('<div><ng2 value="{{ value }}" ng-if="!hideNg2">Content</ng2></div>');
|
||||
angular.bootstrap(element, [ng1Module.name]);
|
||||
|
||||
setTimeout(() => { // Wait for the module to be bootstrapped.
|
||||
setTimeout(() => { // Wait for `$evalAsync()` to propagate inputs.
|
||||
const button = element.querySelector('button') !;
|
||||
setTimeout(() => { // Wait for the module to be bootstrapped.
|
||||
setTimeout(() => { // Wait for `$evalAsync()` to propagate inputs.
|
||||
const button = element.querySelector('button') !;
|
||||
|
||||
// Once initialized.
|
||||
expect(multiTrim(element.textContent)).toBe('bar Content');
|
||||
expect(logs).toEqual([
|
||||
// `ngOnChanges()` call triggered directly through the `inputChanges` $watcher.
|
||||
'OnChanges(bar)',
|
||||
// Initial CD triggered directly through the `detectChanges()` or `inputChanges`
|
||||
// $watcher (for `propagateDigest` true/false respectively).
|
||||
'OnInit(bar)',
|
||||
'DoCheck(bar)',
|
||||
'AfterContentInit(bar)',
|
||||
'AfterContentChecked(bar)',
|
||||
'AfterViewInit(bar)',
|
||||
'AfterViewChecked(bar)',
|
||||
...(propagateDigest ?
|
||||
[
|
||||
// CD triggered directly through the `detectChanges()` $watcher (2nd
|
||||
// $digest).
|
||||
'DoCheck(bar)',
|
||||
'AfterContentChecked(bar)',
|
||||
'AfterViewChecked(bar)',
|
||||
] :
|
||||
[]),
|
||||
// CD triggered due to entering/leaving the NgZone (in `downgradeFn()`).
|
||||
'DoCheck(bar)',
|
||||
'AfterContentChecked(bar)',
|
||||
'AfterViewChecked(bar)',
|
||||
]);
|
||||
logs.length = 0;
|
||||
// Once initialized.
|
||||
expect(multiTrim(element.textContent)).toBe('bar Content');
|
||||
expect(logs).toEqual([
|
||||
// `ngOnChanges()` call triggered directly through the `inputChanges` $watcher.
|
||||
'OnChanges(bar)',
|
||||
// Initial CD triggered directly through the `detectChanges()` or
|
||||
// `inputChanges`
|
||||
// $watcher (for `propagateDigest` true/false respectively).
|
||||
'OnInit(bar)',
|
||||
'DoCheck(bar)',
|
||||
'AfterContentInit(bar)',
|
||||
'AfterContentChecked(bar)',
|
||||
'AfterViewInit(bar)',
|
||||
'AfterViewChecked(bar)',
|
||||
...(propagateDigest ?
|
||||
[
|
||||
// CD triggered directly through the `detectChanges()` $watcher (2nd
|
||||
// $digest).
|
||||
'DoCheck(bar)',
|
||||
'AfterContentChecked(bar)',
|
||||
'AfterViewChecked(bar)',
|
||||
] :
|
||||
[]),
|
||||
// CD triggered due to entering/leaving the NgZone (in `downgradeFn()`).
|
||||
'DoCheck(bar)',
|
||||
'AfterContentChecked(bar)',
|
||||
'AfterViewChecked(bar)',
|
||||
]);
|
||||
logs.length = 0;
|
||||
|
||||
// Change inputs and run `$digest`.
|
||||
rootScope.$apply('value = "baz"');
|
||||
expect(multiTrim(element.textContent)).toBe('baz Content');
|
||||
expect(logs).toEqual([
|
||||
// `ngOnChanges()` call triggered directly through the `inputChanges` $watcher.
|
||||
'OnChanges(baz)',
|
||||
// `propagateDigest: true` (3 CD runs):
|
||||
// - CD triggered due to entering/leaving the NgZone (in `inputChanges`
|
||||
// $watcher).
|
||||
// - CD triggered directly through the `detectChanges()` $watcher.
|
||||
// - CD triggered due to entering/leaving the NgZone (in `detectChanges`
|
||||
// $watcher).
|
||||
// `propagateDigest: false` (2 CD runs):
|
||||
// - CD triggered directly through the `inputChanges` $watcher.
|
||||
// - CD triggered due to entering/leaving the NgZone (in `inputChanges`
|
||||
// $watcher).
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
...(propagateDigest ?
|
||||
[
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
] :
|
||||
[]),
|
||||
]);
|
||||
logs.length = 0;
|
||||
// Change inputs and run `$digest`.
|
||||
rootScope.$apply('value = "baz"');
|
||||
expect(multiTrim(element.textContent)).toBe('baz Content');
|
||||
expect(logs).toEqual([
|
||||
// `ngOnChanges()` call triggered directly through the `inputChanges` $watcher.
|
||||
'OnChanges(baz)',
|
||||
// `propagateDigest: true` (3 CD runs):
|
||||
// - CD triggered due to entering/leaving the NgZone (in `inputChanges`
|
||||
// $watcher).
|
||||
// - CD triggered directly through the `detectChanges()` $watcher.
|
||||
// - CD triggered due to entering/leaving the NgZone (in `detectChanges`
|
||||
// $watcher).
|
||||
// `propagateDigest: false` (2 CD runs):
|
||||
// - CD triggered directly through the `inputChanges` $watcher.
|
||||
// - CD triggered due to entering/leaving the NgZone (in `inputChanges`
|
||||
// $watcher).
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
...(propagateDigest ?
|
||||
[
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
] :
|
||||
[]),
|
||||
]);
|
||||
logs.length = 0;
|
||||
|
||||
// Run `$digest` (without changing inputs).
|
||||
rootScope.$digest();
|
||||
expect(multiTrim(element.textContent)).toBe('baz Content');
|
||||
expect(logs).toEqual(
|
||||
propagateDigest ?
|
||||
[
|
||||
// CD triggered directly through the `detectChanges()` $watcher.
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
// CD triggered due to entering/leaving the NgZone (in the above $watcher).
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
] :
|
||||
[]);
|
||||
logs.length = 0;
|
||||
// Run `$digest` (without changing inputs).
|
||||
rootScope.$digest();
|
||||
expect(multiTrim(element.textContent)).toBe('baz Content');
|
||||
expect(logs).toEqual(
|
||||
propagateDigest ?
|
||||
[
|
||||
// CD triggered directly through the `detectChanges()` $watcher.
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
// CD triggered due to entering/leaving the NgZone (in the above
|
||||
// $watcher).
|
||||
'DoCheck(baz)',
|
||||
'AfterContentChecked(baz)',
|
||||
'AfterViewChecked(baz)',
|
||||
] :
|
||||
[]);
|
||||
logs.length = 0;
|
||||
|
||||
// Trigger change detection (without changing inputs).
|
||||
button.click();
|
||||
expect(multiTrim(element.textContent)).toBe('qux Content');
|
||||
expect(logs).toEqual([
|
||||
'DoCheck(qux)',
|
||||
'AfterContentChecked(qux)',
|
||||
'AfterViewChecked(qux)',
|
||||
]);
|
||||
logs.length = 0;
|
||||
// Trigger change detection (without changing inputs).
|
||||
button.click();
|
||||
expect(multiTrim(element.textContent)).toBe('qux Content');
|
||||
expect(logs).toEqual([
|
||||
'DoCheck(qux)',
|
||||
'AfterContentChecked(qux)',
|
||||
'AfterViewChecked(qux)',
|
||||
]);
|
||||
logs.length = 0;
|
||||
|
||||
// Destroy the component.
|
||||
rootScope.$apply('hideNg2 = true');
|
||||
expect(logs).toEqual([
|
||||
'OnDestroy(qux)',
|
||||
]);
|
||||
logs.length = 0;
|
||||
});
|
||||
});
|
||||
}));
|
||||
// Destroy the component.
|
||||
rootScope.$apply('hideNg2 = true');
|
||||
expect(logs).toEqual([
|
||||
'OnDestroy(qux)',
|
||||
]);
|
||||
logs.length = 0;
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should detach hostViews from the ApplicationRef once destroyed', async(() => {
|
||||
let ng2Component: Ng2Component;
|
||||
fixmeIvy('FW-717: Browser locks up and disconnects') &&
|
||||
it('should detach hostViews from the ApplicationRef once destroyed', async(() => {
|
||||
let ng2Component: Ng2Component;
|
||||
|
||||
@Component({selector: 'ng2', template: ''})
|
||||
class Ng2Component {
|
||||
constructor(public appRef: ApplicationRef) {
|
||||
ng2Component = this;
|
||||
spyOn(appRef, 'attachView').and.callThrough();
|
||||
spyOn(appRef, 'detachView').and.callThrough();
|
||||
}
|
||||
}
|
||||
@Component({selector: 'ng2', template: ''})
|
||||
class Ng2Component {
|
||||
constructor(public appRef: ApplicationRef) {
|
||||
ng2Component = this;
|
||||
spyOn(appRef, 'attachView').and.callThrough();
|
||||
spyOn(appRef, 'detachView').and.callThrough();
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) =>
|
||||
platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module);
|
||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [lazyModuleName])
|
||||
.directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component, propagateDigest}));
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) =>
|
||||
platformBrowserDynamic(extraProviders).bootstrapModule(Ng2Module);
|
||||
const lazyModuleName = downgradeModule<Ng2Module>(bootstrapFn);
|
||||
const ng1Module =
|
||||
angular.module('ng1', [lazyModuleName])
|
||||
.directive(
|
||||
'ng2', downgradeComponent({component: Ng2Component, propagateDigest}));
|
||||
|
||||
const element = html('<ng2 ng-if="!hideNg2"></ng2>');
|
||||
const $injector = angular.bootstrap(element, [ng1Module.name]);
|
||||
const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService;
|
||||
const element = html('<ng2 ng-if="!hideNg2"></ng2>');
|
||||
const $injector = angular.bootstrap(element, [ng1Module.name]);
|
||||
const $rootScope = $injector.get($ROOT_SCOPE) as angular.IRootScopeService;
|
||||
|
||||
setTimeout(() => { // Wait for the module to be bootstrapped.
|
||||
setTimeout(() => { // Wait for the hostView to be attached (during the `$digest`).
|
||||
const hostView: ViewRef =
|
||||
(ng2Component.appRef.attachView as jasmine.Spy).calls.mostRecent().args[0];
|
||||
setTimeout(() => { // Wait for the module to be bootstrapped.
|
||||
setTimeout(() => { // Wait for the hostView to be attached (during the `$digest`).
|
||||
const hostView: ViewRef =
|
||||
(ng2Component.appRef.attachView as jasmine.Spy).calls.mostRecent().args[0];
|
||||
|
||||
expect(hostView.destroyed).toBe(false);
|
||||
expect(hostView.destroyed).toBe(false);
|
||||
|
||||
$rootScope.$apply('hideNg2 = true');
|
||||
$rootScope.$apply('hideNg2 = true');
|
||||
|
||||
expect(hostView.destroyed).toBe(true);
|
||||
expect(ng2Component.appRef.detachView).toHaveBeenCalledWith(hostView);
|
||||
});
|
||||
});
|
||||
}));
|
||||
expect(hostView.destroyed).toBe(true);
|
||||
expect(ng2Component.appRef.detachView).toHaveBeenCalledWith(hostView);
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should only retrieve the Angular zone once (and cache it for later use)',
|
||||
fakeAsync(() => {
|
||||
|
|
|
@ -10,6 +10,7 @@ import {Component, Directive, ElementRef, Injector, Input, NgModule, destroyPlat
|
|||
import {async} from '@angular/core/testing';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
import {fixmeIvy} from '@angular/private/testing';
|
||||
import {UpgradeComponent, UpgradeModule, downgradeComponent} from '@angular/upgrade/static';
|
||||
import * as angular from '@angular/upgrade/static/src/common/angular1';
|
||||
|
||||
|
@ -23,67 +24,70 @@ withEachNg1Version(() => {
|
|||
|
||||
it('should have AngularJS loaded', () => expect(angular.version.major).toBe(1));
|
||||
|
||||
it('should verify UpgradeAdapter example', async(() => {
|
||||
fixmeIvy('FW-714: ng1 projected content is not being rendered') &&
|
||||
it('should verify UpgradeAdapter example', async(() => {
|
||||
|
||||
// This is wrapping (upgrading) an AngularJS component to be used in an Angular
|
||||
// component
|
||||
@Directive({selector: 'ng1'})
|
||||
class Ng1Component extends UpgradeComponent {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() title !: string;
|
||||
// This is wrapping (upgrading) an AngularJS component to be used in an Angular
|
||||
// component
|
||||
@Directive({selector: 'ng1'})
|
||||
class Ng1Component extends UpgradeComponent {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() title !: string;
|
||||
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1', elementRef, injector);
|
||||
}
|
||||
}
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1', elementRef, injector);
|
||||
}
|
||||
}
|
||||
|
||||
// This is an Angular component that will be downgraded
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: 'ng2[<ng1 [title]="nameProp">transclude</ng1>](<ng-content></ng-content>)'
|
||||
})
|
||||
class Ng2Component {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input('name') nameProp !: string;
|
||||
}
|
||||
// This is an Angular component that will be downgraded
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: 'ng2[<ng1 [title]="nameProp">transclude</ng1>](<ng-content></ng-content>)'
|
||||
})
|
||||
class Ng2Component {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input('name') nameProp !: string;
|
||||
}
|
||||
|
||||
// This module represents the Angular pieces of the application
|
||||
@NgModule({
|
||||
declarations: [Ng1Component, Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() { /* this is a placeholder to stop the bootstrapper from complaining */
|
||||
}
|
||||
}
|
||||
// This module represents the Angular pieces of the application
|
||||
@NgModule({
|
||||
declarations: [Ng1Component, Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() { /* this is a placeholder to stop the bootstrapper from
|
||||
complaining */
|
||||
}
|
||||
}
|
||||
|
||||
// This module represents the AngularJS pieces of the application
|
||||
const ng1Module =
|
||||
angular
|
||||
.module('myExample', [])
|
||||
// This is an AngularJS component that will be upgraded
|
||||
.directive(
|
||||
'ng1',
|
||||
() => {
|
||||
return {
|
||||
scope: {title: '='},
|
||||
transclude: true,
|
||||
template: 'ng1[Hello {{title}}!](<span ng-transclude></span>)'
|
||||
};
|
||||
})
|
||||
// This is wrapping (downgrading) an Angular component to be used in AngularJS
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
// This module represents the AngularJS pieces of the application
|
||||
const ng1Module =
|
||||
angular
|
||||
.module('myExample', [])
|
||||
// This is an AngularJS component that will be upgraded
|
||||
.directive(
|
||||
'ng1',
|
||||
() => {
|
||||
return {
|
||||
scope: {title: '='},
|
||||
transclude: true,
|
||||
template: 'ng1[Hello {{title}}!](<span ng-transclude></span>)'
|
||||
};
|
||||
})
|
||||
// This is wrapping (downgrading) an Angular component to be used in
|
||||
// AngularJS
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
|
||||
// This is the (AngularJS) application bootstrap element
|
||||
// Notice that it is actually a downgraded Angular component
|
||||
const element = html('<ng2 name="World">project</ng2>');
|
||||
// This is the (AngularJS) application bootstrap element
|
||||
// Notice that it is actually a downgraded Angular component
|
||||
const element = html('<ng2 name="World">project</ng2>');
|
||||
|
||||
// Let's use a helper function to make this simpler
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
expect(multiTrim(element.textContent))
|
||||
.toBe('ng2[ng1[Hello World!](transclude)](project)');
|
||||
});
|
||||
}));
|
||||
// Let's use a helper function to make this simpler
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(upgrade => {
|
||||
expect(multiTrim(element.textContent))
|
||||
.toBe('ng2[ng1[Hello World!](transclude)](project)');
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@ import {Component, Directive, ElementRef, ErrorHandler, EventEmitter, Inject, In
|
|||
import {async, fakeAsync, tick} from '@angular/core/testing';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
import {fixmeIvy} from '@angular/private/testing';
|
||||
import {UpgradeComponent, UpgradeModule, downgradeComponent} from '@angular/upgrade/static';
|
||||
import * as angular from '@angular/upgrade/static/src/common/angular1';
|
||||
import {$EXCEPTION_HANDLER, $SCOPE} from '@angular/upgrade/static/src/common/constants';
|
||||
|
@ -2133,72 +2134,77 @@ withEachNg1Version(() => {
|
|||
});
|
||||
|
||||
describe('transclusion', () => {
|
||||
it('should support single-slot transclusion', async(() => {
|
||||
let ng2ComponentAInstance: Ng2ComponentA;
|
||||
let ng2ComponentBInstance: Ng2ComponentB;
|
||||
fixmeIvy(
|
||||
`Error: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.`) &&
|
||||
it('should support single-slot transclusion', async(() => {
|
||||
let ng2ComponentAInstance: Ng2ComponentA;
|
||||
let ng2ComponentBInstance: Ng2ComponentB;
|
||||
|
||||
// Define `ng1Component`
|
||||
const ng1Component:
|
||||
angular.IComponent = {template: 'ng1(<div ng-transclude></div>)', transclude: true};
|
||||
// Define `ng1Component`
|
||||
const ng1Component: angular.IComponent = {
|
||||
template: 'ng1(<div ng-transclude></div>)',
|
||||
transclude: true
|
||||
};
|
||||
|
||||
// Define `Ng1ComponentFacade`
|
||||
@Directive({selector: 'ng1'})
|
||||
class Ng1ComponentFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1', elementRef, injector);
|
||||
}
|
||||
}
|
||||
// Define `Ng1ComponentFacade`
|
||||
@Directive({selector: 'ng1'})
|
||||
class Ng1ComponentFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1', elementRef, injector);
|
||||
}
|
||||
}
|
||||
|
||||
// Define `Ng2Component`
|
||||
@Component({
|
||||
selector: 'ng2A',
|
||||
template: 'ng2A(<ng1>{{ value }} | <ng2B *ngIf="showB"></ng2B></ng1>)'
|
||||
})
|
||||
class Ng2ComponentA {
|
||||
value = 'foo';
|
||||
showB = false;
|
||||
constructor() { ng2ComponentAInstance = this; }
|
||||
}
|
||||
// Define `Ng2Component`
|
||||
@Component({
|
||||
selector: 'ng2A',
|
||||
template: 'ng2A(<ng1>{{ value }} | <ng2B *ngIf="showB"></ng2B></ng1>)'
|
||||
})
|
||||
class Ng2ComponentA {
|
||||
value = 'foo';
|
||||
showB = false;
|
||||
constructor() { ng2ComponentAInstance = this; }
|
||||
}
|
||||
|
||||
@Component({selector: 'ng2B', template: 'ng2B({{ value }})'})
|
||||
class Ng2ComponentB {
|
||||
value = 'bar';
|
||||
constructor() { ng2ComponentBInstance = this; }
|
||||
}
|
||||
@Component({selector: 'ng2B', template: 'ng2B({{ value }})'})
|
||||
class Ng2ComponentB {
|
||||
value = 'bar';
|
||||
constructor() { ng2ComponentBInstance = this; }
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
const ng1Module = angular.module('ng1Module', [])
|
||||
.component('ng1', ng1Component)
|
||||
.directive('ng2A', downgradeComponent({component: Ng2ComponentA}));
|
||||
// Define `ng1Module`
|
||||
const ng1Module =
|
||||
angular.module('ng1Module', [])
|
||||
.component('ng1', ng1Component)
|
||||
.directive('ng2A', downgradeComponent({component: Ng2ComponentA}));
|
||||
|
||||
// Define `Ng2Module`
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB],
|
||||
entryComponents: [Ng2ComponentA]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
// Define `Ng2Module`
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB],
|
||||
entryComponents: [Ng2ComponentA]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
// Bootstrap
|
||||
const element = html(`<ng2-a></ng2-a>`);
|
||||
// Bootstrap
|
||||
const element = html(`<ng2-a></ng2-a>`);
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
|
||||
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(foo | ))');
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
|
||||
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(foo | ))');
|
||||
|
||||
ng2ComponentAInstance.value = 'baz';
|
||||
ng2ComponentAInstance.showB = true;
|
||||
$digest(adapter);
|
||||
ng2ComponentAInstance.value = 'baz';
|
||||
ng2ComponentAInstance.showB = true;
|
||||
$digest(adapter);
|
||||
|
||||
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(bar)))');
|
||||
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(bar)))');
|
||||
|
||||
ng2ComponentBInstance.value = 'qux';
|
||||
$digest(adapter);
|
||||
ng2ComponentBInstance.value = 'qux';
|
||||
$digest(adapter);
|
||||
|
||||
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(qux)))');
|
||||
});
|
||||
}));
|
||||
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(qux)))');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support single-slot transclusion with fallback content', async(() => {
|
||||
let ng1ControllerInstances: any[] = [];
|
||||
|
@ -2525,28 +2531,30 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should support structural directives in transcluded content', async(() => {
|
||||
let ng2ComponentInstance: Ng2Component;
|
||||
fixmeIvy(
|
||||
`Error: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.`) &&
|
||||
it('should support structural directives in transcluded content', async(() => {
|
||||
let ng2ComponentInstance: Ng2Component;
|
||||
|
||||
// Define `ng1Component`
|
||||
const ng1Component: angular.IComponent = {
|
||||
template:
|
||||
'ng1(x(<div ng-transclude="slotX"></div>) | default(<div ng-transclude=""></div>))',
|
||||
transclude: {slotX: 'contentX'}
|
||||
};
|
||||
// Define `ng1Component`
|
||||
const ng1Component: angular.IComponent = {
|
||||
template:
|
||||
'ng1(x(<div ng-transclude="slotX"></div>) | default(<div ng-transclude=""></div>))',
|
||||
transclude: {slotX: 'contentX'}
|
||||
};
|
||||
|
||||
// Define `Ng1ComponentFacade`
|
||||
@Directive({selector: 'ng1'})
|
||||
class Ng1ComponentFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1', elementRef, injector);
|
||||
}
|
||||
}
|
||||
// Define `Ng1ComponentFacade`
|
||||
@Directive({selector: 'ng1'})
|
||||
class Ng1ComponentFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1', elementRef, injector);
|
||||
}
|
||||
}
|
||||
|
||||
// Define `Ng2Component`
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: `
|
||||
// Define `Ng2Component`
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: `
|
||||
ng2(
|
||||
<ng1>
|
||||
<content-x><div *ngIf="show">{{ x }}1</div></content-x>
|
||||
|
@ -2555,49 +2563,53 @@ withEachNg1Version(() => {
|
|||
<div *ngIf="show">{{ y }}2</div>
|
||||
</ng1>
|
||||
)`
|
||||
})
|
||||
class Ng2Component {
|
||||
x = 'foo';
|
||||
y = 'bar';
|
||||
show = true;
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
}
|
||||
})
|
||||
class Ng2Component {
|
||||
x = 'foo';
|
||||
y = 'bar';
|
||||
show = true;
|
||||
constructor() { ng2ComponentInstance = this; }
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
const ng1Module = angular.module('ng1Module', [])
|
||||
.component('ng1', ng1Component)
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
// Define `ng1Module`
|
||||
const ng1Module =
|
||||
angular.module('ng1Module', [])
|
||||
.component('ng1', ng1Component)
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
|
||||
// Define `Ng2Module`
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng1ComponentFacade, Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
// Define `Ng2Module`
|
||||
@NgModule({
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
declarations: [Ng1ComponentFacade, Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
// Bootstrap
|
||||
const element = html(`<ng2></ng2>`);
|
||||
// Bootstrap
|
||||
const element = html(`<ng2></ng2>`);
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
|
||||
expect(multiTrim(element.textContent, true)).toBe('ng2(ng1(x(foo1)|default(bar2)))');
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
|
||||
expect(multiTrim(element.textContent, true))
|
||||
.toBe('ng2(ng1(x(foo1)|default(bar2)))');
|
||||
|
||||
ng2ComponentInstance.x = 'baz';
|
||||
ng2ComponentInstance.y = 'qux';
|
||||
ng2ComponentInstance.show = false;
|
||||
$digest(adapter);
|
||||
ng2ComponentInstance.x = 'baz';
|
||||
ng2ComponentInstance.y = 'qux';
|
||||
ng2ComponentInstance.show = false;
|
||||
$digest(adapter);
|
||||
|
||||
expect(multiTrim(element.textContent, true)).toBe('ng2(ng1(x(baz2)|default(qux1)))');
|
||||
expect(multiTrim(element.textContent, true))
|
||||
.toBe('ng2(ng1(x(baz2)|default(qux1)))');
|
||||
|
||||
ng2ComponentInstance.show = true;
|
||||
$digest(adapter);
|
||||
ng2ComponentInstance.show = true;
|
||||
$digest(adapter);
|
||||
|
||||
expect(multiTrim(element.textContent, true)).toBe('ng2(ng1(x(baz1)|default(qux2)))');
|
||||
});
|
||||
}));
|
||||
expect(multiTrim(element.textContent, true))
|
||||
.toBe('ng2(ng1(x(baz1)|default(qux2)))');
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('lifecycle hooks', () => {
|
||||
|
@ -3324,98 +3336,103 @@ withEachNg1Version(() => {
|
|||
}));
|
||||
|
||||
|
||||
it('should call `$onDestroy()` on controller', async(() => {
|
||||
const controllerOnDestroyA = jasmine.createSpy('controllerOnDestroyA');
|
||||
const controllerOnDestroyB = jasmine.createSpy('controllerOnDestroyB');
|
||||
fixmeIvy('FW-713: ngDestroy not being called when downgraded ng2 component is destroyed') &&
|
||||
it('should call `$onDestroy()` on controller', async(() => {
|
||||
const controllerOnDestroyA = jasmine.createSpy('controllerOnDestroyA');
|
||||
const controllerOnDestroyB = jasmine.createSpy('controllerOnDestroyB');
|
||||
|
||||
// Define `ng1Directive`
|
||||
const ng1DirectiveA: angular.IDirective = {
|
||||
template: 'ng1A',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {$onDestroy() { controllerOnDestroyA(); }}
|
||||
};
|
||||
// Define `ng1Directive`
|
||||
const ng1DirectiveA: angular.IDirective = {
|
||||
template: 'ng1A',
|
||||
scope: {},
|
||||
bindToController: false,
|
||||
controllerAs: '$ctrl',
|
||||
controller: class {$onDestroy() { controllerOnDestroyA(); }}
|
||||
};
|
||||
|
||||
const ng1DirectiveB: angular.IDirective = {
|
||||
template: 'ng1B',
|
||||
scope: {},
|
||||
bindToController: true,
|
||||
controllerAs: '$ctrl',
|
||||
controller:
|
||||
class {constructor() { (this as any)['$onDestroy'] = controllerOnDestroyB; }}
|
||||
};
|
||||
const ng1DirectiveB: angular.IDirective = {
|
||||
template: 'ng1B',
|
||||
scope: {},
|
||||
bindToController: true,
|
||||
controllerAs: '$ctrl',
|
||||
controller:
|
||||
class {constructor() { (this as any)['$onDestroy'] = controllerOnDestroyB; }}
|
||||
};
|
||||
|
||||
// Define `Ng1ComponentFacade`
|
||||
@Directive({selector: 'ng1A'})
|
||||
class Ng1ComponentAFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1A', elementRef, injector);
|
||||
}
|
||||
}
|
||||
// Define `Ng1ComponentFacade`
|
||||
@Directive({selector: 'ng1A'})
|
||||
class Ng1ComponentAFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1A', elementRef, injector);
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: 'ng1B'})
|
||||
class Ng1ComponentBFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1B', elementRef, injector);
|
||||
}
|
||||
}
|
||||
@Directive({selector: 'ng1B'})
|
||||
class Ng1ComponentBFacade extends UpgradeComponent {
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1B', elementRef, injector);
|
||||
}
|
||||
}
|
||||
|
||||
// Define `Ng2Component`
|
||||
@Component(
|
||||
{selector: 'ng2', template: '<div *ngIf="show"><ng1A></ng1A> | <ng1B></ng1B></div>'})
|
||||
class Ng2Component {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() show !: boolean;
|
||||
}
|
||||
// Define `Ng2Component`
|
||||
@Component({
|
||||
selector: 'ng2',
|
||||
template: '<div *ngIf="show"><ng1A></ng1A> | <ng1B></ng1B></div>'
|
||||
})
|
||||
class Ng2Component {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() show !: boolean;
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
const ng1Module = angular.module('ng1Module', [])
|
||||
.directive('ng1A', () => ng1DirectiveA)
|
||||
.directive('ng1B', () => ng1DirectiveB)
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
// Define `ng1Module`
|
||||
const ng1Module =
|
||||
angular.module('ng1Module', [])
|
||||
.directive('ng1A', () => ng1DirectiveA)
|
||||
.directive('ng1B', () => ng1DirectiveB)
|
||||
.directive('ng2', downgradeComponent({component: Ng2Component}));
|
||||
|
||||
// Define `Ng2Module`
|
||||
@NgModule({
|
||||
declarations: [Ng1ComponentAFacade, Ng1ComponentBFacade, Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
// Define `Ng2Module`
|
||||
@NgModule({
|
||||
declarations: [Ng1ComponentAFacade, Ng1ComponentBFacade, Ng2Component],
|
||||
entryComponents: [Ng2Component],
|
||||
imports: [BrowserModule, UpgradeModule]
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
// Bootstrap
|
||||
const element = html('<ng2 [show]="!destroyFromNg2" ng-if="!destroyFromNg1"></ng2>');
|
||||
// Bootstrap
|
||||
const element = html('<ng2 [show]="!destroyFromNg2" ng-if="!destroyFromNg1"></ng2>');
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
|
||||
const $rootScope = adapter.$injector.get('$rootScope') as angular.IRootScopeService;
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
|
||||
const $rootScope =
|
||||
adapter.$injector.get('$rootScope') as angular.IRootScopeService;
|
||||
|
||||
expect(multiTrim(document.body.textContent)).toBe('ng1A | ng1B');
|
||||
expect(controllerOnDestroyA).not.toHaveBeenCalled();
|
||||
expect(controllerOnDestroyB).not.toHaveBeenCalled();
|
||||
expect(multiTrim(document.body.textContent)).toBe('ng1A | ng1B');
|
||||
expect(controllerOnDestroyA).not.toHaveBeenCalled();
|
||||
expect(controllerOnDestroyB).not.toHaveBeenCalled();
|
||||
|
||||
$rootScope.$apply('destroyFromNg1 = true');
|
||||
$rootScope.$apply('destroyFromNg1 = true');
|
||||
|
||||
expect(multiTrim(document.body.textContent)).toBe('');
|
||||
expect(controllerOnDestroyA).toHaveBeenCalled();
|
||||
expect(controllerOnDestroyB).toHaveBeenCalled();
|
||||
expect(multiTrim(document.body.textContent)).toBe('');
|
||||
expect(controllerOnDestroyA).toHaveBeenCalled();
|
||||
expect(controllerOnDestroyB).toHaveBeenCalled();
|
||||
|
||||
controllerOnDestroyA.calls.reset();
|
||||
controllerOnDestroyB.calls.reset();
|
||||
$rootScope.$apply('destroyFromNg1 = false');
|
||||
controllerOnDestroyA.calls.reset();
|
||||
controllerOnDestroyB.calls.reset();
|
||||
$rootScope.$apply('destroyFromNg1 = false');
|
||||
|
||||
expect(multiTrim(document.body.textContent)).toBe('ng1A | ng1B');
|
||||
expect(controllerOnDestroyA).not.toHaveBeenCalled();
|
||||
expect(controllerOnDestroyB).not.toHaveBeenCalled();
|
||||
expect(multiTrim(document.body.textContent)).toBe('ng1A | ng1B');
|
||||
expect(controllerOnDestroyA).not.toHaveBeenCalled();
|
||||
expect(controllerOnDestroyB).not.toHaveBeenCalled();
|
||||
|
||||
$rootScope.$apply('destroyFromNg2 = true');
|
||||
$rootScope.$apply('destroyFromNg2 = true');
|
||||
|
||||
expect(multiTrim(document.body.textContent)).toBe('');
|
||||
expect(controllerOnDestroyA).toHaveBeenCalled();
|
||||
expect(controllerOnDestroyB).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
expect(multiTrim(document.body.textContent)).toBe('');
|
||||
expect(controllerOnDestroyA).toHaveBeenCalled();
|
||||
expect(controllerOnDestroyB).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not call `$onDestroy()` on scope', async(() => {
|
||||
const scopeOnDestroy = jasmine.createSpy('scopeOnDestroy');
|
||||
|
@ -3948,22 +3965,23 @@ withEachNg1Version(() => {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should support ng2 > ng1 > ng2 (with inputs/outputs)', fakeAsync(() => {
|
||||
let ng2ComponentAInstance: Ng2ComponentA;
|
||||
let ng2ComponentBInstance: Ng2ComponentB;
|
||||
let ng1ControllerXInstance: Ng1ControllerX;
|
||||
fixmeIvy('FW-642: ASSERTION ERROR: Slot should have been initialized to NO_CHANGE') &&
|
||||
it('should support ng2 > ng1 > ng2 (with inputs/outputs)', fakeAsync(() => {
|
||||
let ng2ComponentAInstance: Ng2ComponentA;
|
||||
let ng2ComponentBInstance: Ng2ComponentB;
|
||||
let ng1ControllerXInstance: Ng1ControllerX;
|
||||
|
||||
// Define `ng1Component`
|
||||
class Ng1ControllerX {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
ng1XInputA !: string;
|
||||
ng1XInputB: any;
|
||||
ng1XInputC: any;
|
||||
// Define `ng1Component`
|
||||
class Ng1ControllerX {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
ng1XInputA !: string;
|
||||
ng1XInputB: any;
|
||||
ng1XInputC: any;
|
||||
|
||||
constructor() { ng1ControllerXInstance = this; }
|
||||
}
|
||||
const ng1Component: angular.IComponent = {
|
||||
template: `
|
||||
constructor() { ng1ControllerXInstance = this; }
|
||||
}
|
||||
const ng1Component: angular.IComponent = {
|
||||
template: `
|
||||
ng1X({{ $ctrl.ng1XInputA }}, {{ $ctrl.ng1XInputB.value }}, {{ $ctrl.ng1XInputC.value }}) |
|
||||
<ng2-b
|
||||
[ng2-b-input1]="$ctrl.ng1XInputA"
|
||||
|
@ -3971,39 +3989,39 @@ withEachNg1Version(() => {
|
|||
(ng2-b-output-c)="$ctrl.ng1XInputC = {value: $event}">
|
||||
</ng2-b>
|
||||
`,
|
||||
bindings: {
|
||||
ng1XInputA: '@',
|
||||
ng1XInputB: '<',
|
||||
ng1XInputC: '=',
|
||||
ng1XOutputA: '&',
|
||||
ng1XOutputB: '&'
|
||||
},
|
||||
controller: Ng1ControllerX
|
||||
};
|
||||
bindings: {
|
||||
ng1XInputA: '@',
|
||||
ng1XInputB: '<',
|
||||
ng1XInputC: '=',
|
||||
ng1XOutputA: '&',
|
||||
ng1XOutputB: '&'
|
||||
},
|
||||
controller: Ng1ControllerX
|
||||
};
|
||||
|
||||
// Define `Ng1ComponentFacade`
|
||||
@Directive({selector: 'ng1X'})
|
||||
class Ng1ComponentXFacade extends UpgradeComponent {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() ng1XInputA !: string;
|
||||
@Input() ng1XInputB: any;
|
||||
@Input() ng1XInputC: any;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Output() ng1XInputCChange !: EventEmitter<any>;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Output() ng1XOutputA !: EventEmitter<any>;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Output() ng1XOutputB !: EventEmitter<any>;
|
||||
// Define `Ng1ComponentFacade`
|
||||
@Directive({selector: 'ng1X'})
|
||||
class Ng1ComponentXFacade extends UpgradeComponent {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() ng1XInputA !: string;
|
||||
@Input() ng1XInputB: any;
|
||||
@Input() ng1XInputC: any;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Output() ng1XInputCChange !: EventEmitter<any>;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Output() ng1XOutputA !: EventEmitter<any>;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Output() ng1XOutputB !: EventEmitter<any>;
|
||||
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1X', elementRef, injector);
|
||||
}
|
||||
}
|
||||
constructor(elementRef: ElementRef, injector: Injector) {
|
||||
super('ng1X', elementRef, injector);
|
||||
}
|
||||
}
|
||||
|
||||
// Define `Ng2Component`
|
||||
@Component({
|
||||
selector: 'ng2-a',
|
||||
template: `
|
||||
// Define `Ng2Component`
|
||||
@Component({
|
||||
selector: 'ng2-a',
|
||||
template: `
|
||||
ng2A({{ ng2ADataA.value }}, {{ ng2ADataB.value }}, {{ ng2ADataC.value }}) |
|
||||
<ng1X
|
||||
ng1XInputA="{{ ng2ADataA.value }}"
|
||||
|
@ -4013,129 +4031,131 @@ withEachNg1Version(() => {
|
|||
on-ng1XOutputB="ng2ADataB.value = $event">
|
||||
</ng1X>
|
||||
`
|
||||
})
|
||||
class Ng2ComponentA {
|
||||
ng2ADataA = {value: 'foo'};
|
||||
ng2ADataB = {value: 'bar'};
|
||||
ng2ADataC = {value: 'baz'};
|
||||
})
|
||||
class Ng2ComponentA {
|
||||
ng2ADataA = {value: 'foo'};
|
||||
ng2ADataB = {value: 'bar'};
|
||||
ng2ADataC = {value: 'baz'};
|
||||
|
||||
constructor() { ng2ComponentAInstance = this; }
|
||||
}
|
||||
constructor() { ng2ComponentAInstance = this; }
|
||||
}
|
||||
|
||||
@Component({selector: 'ng2-b', template: 'ng2B({{ ng2BInputA }}, {{ ng2BInputC }})'})
|
||||
class Ng2ComponentB {
|
||||
@Input('ng2BInput1') ng2BInputA: any;
|
||||
@Input() ng2BInputC: any;
|
||||
@Output() ng2BOutputC = new EventEmitter();
|
||||
@Component({selector: 'ng2-b', template: 'ng2B({{ ng2BInputA }}, {{ ng2BInputC }})'})
|
||||
class Ng2ComponentB {
|
||||
@Input('ng2BInput1') ng2BInputA: any;
|
||||
@Input() ng2BInputC: any;
|
||||
@Output() ng2BOutputC = new EventEmitter();
|
||||
|
||||
constructor() { ng2ComponentBInstance = this; }
|
||||
}
|
||||
constructor() { ng2ComponentBInstance = this; }
|
||||
}
|
||||
|
||||
// Define `ng1Module`
|
||||
const ng1Module = angular.module('ng1', [])
|
||||
.component('ng1X', ng1Component)
|
||||
.directive('ng2A', downgradeComponent({component: Ng2ComponentA}))
|
||||
.directive('ng2B', downgradeComponent({component: Ng2ComponentB}));
|
||||
// Define `ng1Module`
|
||||
const ng1Module =
|
||||
angular.module('ng1', [])
|
||||
.component('ng1X', ng1Component)
|
||||
.directive('ng2A', downgradeComponent({component: Ng2ComponentA}))
|
||||
.directive('ng2B', downgradeComponent({component: Ng2ComponentB}));
|
||||
|
||||
// Define `Ng2Module`
|
||||
@NgModule({
|
||||
declarations: [Ng1ComponentXFacade, Ng2ComponentA, Ng2ComponentB],
|
||||
entryComponents: [Ng2ComponentA, Ng2ComponentB],
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
// Define `Ng2Module`
|
||||
@NgModule({
|
||||
declarations: [Ng1ComponentXFacade, Ng2ComponentA, Ng2ComponentB],
|
||||
entryComponents: [Ng2ComponentA, Ng2ComponentB],
|
||||
imports: [BrowserModule, UpgradeModule],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
// Bootstrap
|
||||
const element = html(`<ng2-a></ng2-a>`);
|
||||
// Bootstrap
|
||||
const element = html(`<ng2-a></ng2-a>`);
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
|
||||
// Initial value propagation.
|
||||
// (ng2A > ng1X > ng2B)
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo, bar, baz) | ng1X(foo, bar, baz) | ng2B(foo, baz)');
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
|
||||
// Initial value propagation.
|
||||
// (ng2A > ng1X > ng2B)
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo, bar, baz) | ng1X(foo, bar, baz) | ng2B(foo, baz)');
|
||||
|
||||
// Update `ng2BInputA`/`ng2BInputC`.
|
||||
// (Should not propagate upwards.)
|
||||
ng2ComponentBInstance.ng2BInputA = 'foo2';
|
||||
ng2ComponentBInstance.ng2BInputC = 'baz2';
|
||||
$digest(adapter);
|
||||
tick();
|
||||
// Update `ng2BInputA`/`ng2BInputC`.
|
||||
// (Should not propagate upwards.)
|
||||
ng2ComponentBInstance.ng2BInputA = 'foo2';
|
||||
ng2ComponentBInstance.ng2BInputC = 'baz2';
|
||||
$digest(adapter);
|
||||
tick();
|
||||
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo, bar, baz) | ng1X(foo, bar, baz) | ng2B(foo2, baz2)');
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo, bar, baz) | ng1X(foo, bar, baz) | ng2B(foo2, baz2)');
|
||||
|
||||
// Emit from `ng2BOutputC`.
|
||||
// (Should propagate all the way up to `ng1ADataC` and back all the way down to
|
||||
// `ng2BInputC`.)
|
||||
ng2ComponentBInstance.ng2BOutputC.emit('baz3');
|
||||
$digest(adapter);
|
||||
tick();
|
||||
// Emit from `ng2BOutputC`.
|
||||
// (Should propagate all the way up to `ng1ADataC` and back all the way down to
|
||||
// `ng2BInputC`.)
|
||||
ng2ComponentBInstance.ng2BOutputC.emit('baz3');
|
||||
$digest(adapter);
|
||||
tick();
|
||||
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo, bar, baz3) | ng1X(foo, bar, baz3) | ng2B(foo2, baz3)');
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo, bar, baz3) | ng1X(foo, bar, baz3) | ng2B(foo2, baz3)');
|
||||
|
||||
// Update `ng1XInputA`/`ng1XInputB`.
|
||||
// (Should not propagate upwards, only downwards.)
|
||||
ng1ControllerXInstance.ng1XInputA = 'foo4';
|
||||
ng1ControllerXInstance.ng1XInputB = {value: 'bar4'};
|
||||
$digest(adapter);
|
||||
tick();
|
||||
// Update `ng1XInputA`/`ng1XInputB`.
|
||||
// (Should not propagate upwards, only downwards.)
|
||||
ng1ControllerXInstance.ng1XInputA = 'foo4';
|
||||
ng1ControllerXInstance.ng1XInputB = {value: 'bar4'};
|
||||
$digest(adapter);
|
||||
tick();
|
||||
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo, bar, baz3) | ng1X(foo4, bar4, baz3) | ng2B(foo4, baz3)');
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo, bar, baz3) | ng1X(foo4, bar4, baz3) | ng2B(foo4, baz3)');
|
||||
|
||||
// Update `ng1XInputC`.
|
||||
// (Should propagate upwards and downwards.)
|
||||
ng1ControllerXInstance.ng1XInputC = {value: 'baz5'};
|
||||
$digest(adapter);
|
||||
tick();
|
||||
// Update `ng1XInputC`.
|
||||
// (Should propagate upwards and downwards.)
|
||||
ng1ControllerXInstance.ng1XInputC = {value: 'baz5'};
|
||||
$digest(adapter);
|
||||
tick();
|
||||
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo, bar, baz5) | ng1X(foo4, bar4, baz5) | ng2B(foo4, baz5)');
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo, bar, baz5) | ng1X(foo4, bar4, baz5) | ng2B(foo4, baz5)');
|
||||
|
||||
// Update a property on `ng1XInputC`.
|
||||
// (Should propagate upwards and downwards.)
|
||||
ng1ControllerXInstance.ng1XInputC.value = 'baz6';
|
||||
$digest(adapter);
|
||||
tick();
|
||||
// Update a property on `ng1XInputC`.
|
||||
// (Should propagate upwards and downwards.)
|
||||
ng1ControllerXInstance.ng1XInputC.value = 'baz6';
|
||||
$digest(adapter);
|
||||
tick();
|
||||
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo, bar, baz6) | ng1X(foo4, bar4, baz6) | ng2B(foo4, baz6)');
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo, bar, baz6) | ng1X(foo4, bar4, baz6) | ng2B(foo4, baz6)');
|
||||
|
||||
// Emit from `ng1XOutputA`.
|
||||
// (Should propagate upwards to `ng1ADataA` and back all the way down to `ng2BInputA`.)
|
||||
(ng1ControllerXInstance as any).ng1XOutputA({value: 'foo7'});
|
||||
$digest(adapter);
|
||||
tick();
|
||||
// Emit from `ng1XOutputA`.
|
||||
// (Should propagate upwards to `ng1ADataA` and back all the way down to
|
||||
// `ng2BInputA`.)
|
||||
(ng1ControllerXInstance as any).ng1XOutputA({value: 'foo7'});
|
||||
$digest(adapter);
|
||||
tick();
|
||||
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo7, bar, baz6) | ng1X(foo7, bar4, baz6) | ng2B(foo7, baz6)');
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo7, bar, baz6) | ng1X(foo7, bar4, baz6) | ng2B(foo7, baz6)');
|
||||
|
||||
// Emit from `ng1XOutputB`.
|
||||
// (Should propagate upwards to `ng1ADataB`, but not downwards,
|
||||
// since `ng1XInputB` has been re-assigned (i.e. `ng2ADataB !== ng1XInputB`).)
|
||||
(ng1ControllerXInstance as any).ng1XOutputB('bar8');
|
||||
$digest(adapter);
|
||||
tick();
|
||||
// Emit from `ng1XOutputB`.
|
||||
// (Should propagate upwards to `ng1ADataB`, but not downwards,
|
||||
// since `ng1XInputB` has been re-assigned (i.e. `ng2ADataB !== ng1XInputB`).)
|
||||
(ng1ControllerXInstance as any).ng1XOutputB('bar8');
|
||||
$digest(adapter);
|
||||
tick();
|
||||
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo7, bar8, baz6) | ng1X(foo7, bar4, baz6) | ng2B(foo7, baz6)');
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo7, bar8, baz6) | ng1X(foo7, bar4, baz6) | ng2B(foo7, baz6)');
|
||||
|
||||
// Update `ng2ADataA`/`ng2ADataB`/`ng2ADataC`.
|
||||
// (Should propagate everywhere.)
|
||||
ng2ComponentAInstance.ng2ADataA = {value: 'foo9'};
|
||||
ng2ComponentAInstance.ng2ADataB = {value: 'bar9'};
|
||||
ng2ComponentAInstance.ng2ADataC = {value: 'baz9'};
|
||||
$digest(adapter);
|
||||
tick();
|
||||
// Update `ng2ADataA`/`ng2ADataB`/`ng2ADataC`.
|
||||
// (Should propagate everywhere.)
|
||||
ng2ComponentAInstance.ng2ADataA = {value: 'foo9'};
|
||||
ng2ComponentAInstance.ng2ADataB = {value: 'bar9'};
|
||||
ng2ComponentAInstance.ng2ADataC = {value: 'baz9'};
|
||||
$digest(adapter);
|
||||
tick();
|
||||
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo9, bar9, baz9) | ng1X(foo9, bar9, baz9) | ng2B(foo9, baz9)');
|
||||
});
|
||||
}));
|
||||
expect(multiTrim(document.body.textContent))
|
||||
.toBe('ng2A(foo9, bar9, baz9) | ng1X(foo9, bar9, baz9) | ng2B(foo9, baz9)');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support ng2 > ng1 > ng2 > ng1 (with `require`)', async(() => {
|
||||
// Define `ng1Component`
|
||||
|
|
Loading…
Reference in New Issue