;
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({
selector: 'ng2',
template: `
|
{{ someText }} - Data: {{ dataA }} - Length: {{ dataA.length }}
`
})
class Ng2Component {
someText = 'ng2';
dataA = [1, 2, 3];
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.directive('ng1', () => ng1Directive)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentFacade, Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
const ng1 = element.querySelector('[ng1]') !;
const ng1Controller = angular.element(ng1).controller !('ng1');
expect(multiTrim(element.textContent))
.toBe('ng1 - Data: [1,2,3] - Length: 3 | ng2 - Data: 1,2,3 - Length: 3');
ng1Controller.$scope.inputA = [4, 5];
tick();
expect(multiTrim(element.textContent))
.toBe('ng1 - Data: [4,5] - Length: 2 | ng2 - Data: 4,5 - Length: 2');
ng1Controller.$scope.outputA(6);
$digest(adapter);
tick();
expect(ng1Controller.$scope.inputA).toEqual([4, 5, 6]);
expect(multiTrim(element.textContent))
.toBe('ng1 - Data: [4,5,6] - Length: 3 | ng2 - Data: 4,5,6 - Length: 3');
});
}));
});
describe('compiling', () => {
it('should compile the ng1 template in the correct DOM context', async(() => {
let grandParentNodeName: string;
// Define `ng1Component`
const ng1ComponentA: angular.IComponent = {template: 'ng1A()'};
const ng1DirectiveB: angular.IDirective = {
compile: tElem => {
grandParentNodeName = tElem.parent !().parent !()[0].nodeName;
return {};
}
};
// Define `Ng1ComponentAFacade`
@Directive({selector: 'ng1A'})
class Ng1ComponentAFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1A', elementRef, injector);
}
}
// Define `Ng2ComponentX`
@Component({selector: 'ng2-x', template: 'ng2X()'})
class Ng2ComponentX {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1', [])
.component('ng1A', ng1ComponentA)
.directive('ng1B', () => ng1DirectiveB)
.directive('ng2X', downgradeComponent({component: Ng2ComponentX}));
// Define `Ng2Module`
@NgModule({
imports: [BrowserModule, UpgradeModule],
declarations: [Ng1ComponentAFacade, Ng2ComponentX],
entryComponents: [Ng2ComponentX],
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(grandParentNodeName).toBe('NG2-X');
});
}));
});
describe('linking', () => {
it('should run the pre-linking after instantiating the controller', async(() => {
const log: string[] = [];
// Define `ng1Directive`
const ng1Directive: angular.IDirective = {
template: '',
link: {pre: () => log.push('ng1-pre')},
controller: class {constructor() { log.push('ng1-ctrl'); }}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class Ng2Component {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1', [])
.directive('ng1', () => ng1Directive)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
imports: [BrowserModule, UpgradeModule],
declarations: [Ng1ComponentFacade, Ng2Component],
entryComponents: [Ng2Component],
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(log).toEqual(['ng1-ctrl', 'ng1-pre']);
});
}));
it('should run the pre-linking function before linking', async(() => {
const log: string[] = [];
// Define `ng1Directive`
const ng1DirectiveA: angular.IDirective = {
template: '',
link: {pre: () => log.push('ng1A-pre')}
};
const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')};
// Define `Ng1ComponentAFacade`
@Directive({selector: 'ng1A'})
class Ng1ComponentAFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1A', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class Ng2Component {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1', [])
.directive('ng1A', () => ng1DirectiveA)
.directive('ng1B', () => ng1DirectiveB)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
imports: [BrowserModule, UpgradeModule],
declarations: [Ng1ComponentAFacade, Ng2Component],
entryComponents: [Ng2Component],
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(log).toEqual(['ng1A-pre', 'ng1B-post']);
});
}));
it('should run the post-linking function after linking (link: object)', async(() => {
const log: string[] = [];
// Define `ng1Directive`
const ng1DirectiveA: angular.IDirective = {
template: '',
link: {post: () => log.push('ng1A-post')}
};
const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')};
// Define `Ng1ComponentAFacade`
@Directive({selector: 'ng1A'})
class Ng1ComponentAFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1A', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class Ng2Component {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1', [])
.directive('ng1A', () => ng1DirectiveA)
.directive('ng1B', () => ng1DirectiveB)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
imports: [BrowserModule, UpgradeModule],
declarations: [Ng1ComponentAFacade, Ng2Component],
entryComponents: [Ng2Component],
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(log).toEqual(['ng1B-post', 'ng1A-post']);
});
}));
it('should run the post-linking function after linking (link: function)', async(() => {
const log: string[] = [];
// Define `ng1Directive`
const ng1DirectiveA: angular.IDirective = {
template: '',
link: () => log.push('ng1A-post')
};
const ng1DirectiveB: angular.IDirective = {link: () => log.push('ng1B-post')};
// Define `Ng1ComponentAFacade`
@Directive({selector: 'ng1A'})
class Ng1ComponentAFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1A', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class Ng2Component {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1', [])
.directive('ng1A', () => ng1DirectiveA)
.directive('ng1B', () => ng1DirectiveB)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
imports: [BrowserModule, UpgradeModule],
declarations: [Ng1ComponentAFacade, Ng2Component],
entryComponents: [Ng2Component],
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(log).toEqual(['ng1B-post', 'ng1A-post']);
});
}));
it('should run the post-linking function before `$postLink`', async(() => {
const log: string[] = [];
// Define `ng1Directive`
const ng1Directive: angular.IDirective = {
template: '',
link: () => log.push('ng1-post'),
controller: class {$postLink() { log.push('ng1-$post'); }}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class Ng2Component {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1', [])
.directive('ng1', () => ng1Directive)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
imports: [BrowserModule, UpgradeModule],
declarations: [Ng1ComponentFacade, Ng2Component],
entryComponents: [Ng2Component],
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(log).toEqual(['ng1-post', 'ng1-$post']);
});
}));
});
describe('controller', () => {
it('should support `controllerAs`', async(() => {
// Define `ng1Directive`
const ng1Directive: angular.IDirective = {
template:
'{{ vm.scope }}; {{ vm.isClass }}; {{ vm.hasElement }}; {{ vm.isPublished() }}',
scope: true,
controllerAs: 'vm',
controller: class {
hasElement: string; // TODO(issue/24571): remove '!'.
isClass !: string;
scope: string;
constructor(public $element: angular.IAugmentedJQuery, $scope: angular.IScope) {
this.hasElement = $element[0].nodeName;
this.scope = $scope.$parent.$parent === $scope.$root ? 'scope' : 'wrong-scope';
this.verifyIAmAClass();
}
isPublished() {
return this.$element.controller !('ng1') === this ? 'published' : 'not-published';
}
verifyIAmAClass() { this.isClass = 'isClass'; }
}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class Ng2Component {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.directive('ng1', () => ng1Directive)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentFacade, Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(element.textContent)).toBe('scope; isClass; NG1; published');
});
}));
it('should support `bindToController` (boolean)', async(() => {
// Define `ng1Directive`
const ng1DirectiveA: angular.IDirective = {
template: 'Scope: {{ title }}; Controller: {{ $ctrl.title }}',
scope: {title: '@'},
bindToController: false,
controllerAs: '$ctrl',
controller: class {}
};
const ng1DirectiveB: angular.IDirective = {
template: 'Scope: {{ title }}; Controller: {{ $ctrl.title }}',
scope: {title: '@'},
bindToController: true,
controllerAs: '$ctrl',
controller: class {}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1A'})
class Ng1ComponentAFacade extends UpgradeComponent {
// TODO(issue/24571): remove '!'.
@Input() title !: string;
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1A', elementRef, injector);
}
}
@Directive({selector: 'ng1B'})
class Ng1ComponentBFacade extends UpgradeComponent {
// TODO(issue/24571): remove '!'.
@Input() title !: string;
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1B', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({
selector: 'ng2',
template: `
|
`
})
class 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],
schemas: [NO_ERRORS_SCHEMA]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(element.textContent))
.toBe('Scope: WORKS; Controller: | Scope: ; Controller: WORKS');
});
}));
it('should support `bindToController` (object)', async(() => {
// Define `ng1Directive`
const ng1Directive: angular.IDirective = {
template: '{{ $ctrl.title }}',
scope: {},
bindToController: {title: '@'},
controllerAs: '$ctrl',
controller: class {}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
// TODO(issue/24571): remove '!'.
@Input() title !: string;
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class Ng2Component {
dataA = 'foo';
dataB = 'bar';
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.directive('ng1', () => ng1Directive)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentFacade, Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(element.textContent)).toBe('WORKS');
});
}));
it('should support `controller` as string', async(() => {
// Define `ng1Directive`
const ng1Directive: angular.IDirective = {
template: '{{ $ctrl.title }} {{ $ctrl.text }}',
scope: {title: '@'},
bindToController: true,
controller: 'Ng1Controller as $ctrl'
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
// TODO(issue/24571): remove '!'.
@Input() title !: string;
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class Ng2Component {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.controller('Ng1Controller', class { text = 'GREAT'; })
.directive('ng1', () => ng1Directive)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentFacade, Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(element.textContent)).toBe('WORKS GREAT');
});
}));
it('should insert the compiled content before instantiating the controller', async(() => {
let compiledContent: string;
let getCurrentContent: () => string;
// Define `ng1Component`
const ng1Component: angular.IComponent = {
template: 'Hello, {{ $ctrl.name }}!',
controller: class {
name = 'world';
constructor($element: angular.IAugmentedJQuery) {
getCurrentContent = () => $element.text !();
compiledContent = getCurrentContent();
}
}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class 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]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(compiledContent)).toBe('Hello, {{ $ctrl.name }}!');
expect(multiTrim(getCurrentContent())).toBe('Hello, world!');
});
}));
});
describe('require', () => {
// NOT YET SUPPORTED
xdescribe('in pre-/post-link', () => {
it('should resolve to its own controller if falsy', async(() => {
// Define `ng1Directive`
const ng1Directive: angular.IDirective = {
template: 'Pre: {{ pre }} | Post: {{ post }}',
controller: class {value = 'foo';},
link: {
pre: function(scope: any, elem: any, attrs: any, ctrl: any) {
scope['pre'] = ctrl.value;
},
post: function(scope: any, elem: any, attrs: any, ctrl: any) {
scope['post'] = ctrl.value;
}
}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class Ng2Component {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.directive('ng1', () => ng1Directive)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentFacade, Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(document.body.textContent)).toBe('Pre: foo | Post: foo');
});
}));
// TODO: Add more tests
});
describe('in controller', () => {
it('should be available to children', async(() => {
// Define `ng1Component`
const ng1ComponentA: angular.IComponent = {
template: '',
controller: class {value = 'ng1A';}
};
const ng1ComponentB: angular.IComponent = {
template: 'Required: {{ $ctrl.required.value }}',
require: {required: '^^ng1A'}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1A'})
class Ng1ComponentAFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1A', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class Ng2Component {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.component('ng1A', ng1ComponentA)
.component('ng1B', ng1ComponentB)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentAFacade, Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(element.textContent)).toBe('Required: ng1A');
});
}));
it('should throw if required controller cannot be found', async(() => {
// Define `ng1Component`
const ng1ComponentA: angular.IComponent = {require: {foo: 'iDoNotExist'}};
const ng1ComponentB: angular.IComponent = {require: {foo: '^iDoNotExist'}};
const ng1ComponentC: angular.IComponent = {require: {foo: '^^iDoNotExist'}};
// 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: 'ng1C'})
class Ng1ComponentCFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1C', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2-a', template: ''})
class Ng2ComponentA {
}
@Component({selector: 'ng2-b', template: ''})
class Ng2ComponentB {
}
@Component({selector: 'ng2-c', template: ''})
class Ng2ComponentC {
}
// Define `ng1Module`
const mockExceptionHandler = jasmine.createSpy('$exceptionHandler');
const ng1Module =
angular.module('ng1Module', [])
.component('ng1A', ng1ComponentA)
.component('ng1B', ng1ComponentB)
.component('ng1C', ng1ComponentC)
.directive('ng2A', downgradeComponent({component: Ng2ComponentA}))
.directive('ng2B', downgradeComponent({component: Ng2ComponentB}))
.directive('ng2C', downgradeComponent({component: Ng2ComponentC}))
.value('$exceptionHandler', mockExceptionHandler);
// Define `Ng2Module`
@NgModule({
declarations: [
Ng1ComponentAFacade, Ng1ComponentBFacade, Ng1ComponentCFacade, Ng2ComponentA,
Ng2ComponentB, Ng2ComponentC
],
entryComponents: [Ng2ComponentA, Ng2ComponentB, Ng2ComponentC],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const elementA = html(``);
const elementB = html(``);
const elementC = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, elementA, ng1Module).then(() => {
expect(mockExceptionHandler)
.toHaveBeenCalledWith(new Error(
'Unable to find required \'iDoNotExist\' in upgraded directive \'ng1A\'.'));
});
bootstrap(platformBrowserDynamic(), Ng2Module, elementB, ng1Module).then(() => {
expect(mockExceptionHandler)
.toHaveBeenCalledWith(new Error(
'Unable to find required \'^iDoNotExist\' in upgraded directive \'ng1B\'.'));
});
bootstrap(platformBrowserDynamic(), Ng2Module, elementC, ng1Module).then(() => {
expect(mockExceptionHandler)
.toHaveBeenCalledWith(new Error(
'Unable to find required \'^^iDoNotExist\' in upgraded directive \'ng1C\'.'));
});
}));
it('should not throw if missing required controller is optional', async(() => {
// Define `ng1Component`
const ng1Component: angular.IComponent = {
require: {
foo: '?iDoNotExist',
bar: '^?iDoNotExist',
baz: '?^^iDoNotExist',
}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class Ng2Component {
}
// Define `ng1Module`
const mockExceptionHandler = jasmine.createSpy('$exceptionHandler');
const ng1Module = angular.module('ng1Module', [])
.component('ng1', ng1Component)
.directive('ng2', downgradeComponent({component: Ng2Component}))
.value('$exceptionHandler', mockExceptionHandler);
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentFacade, Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(mockExceptionHandler).not.toHaveBeenCalled();
});
}));
it('should assign resolved values to the controller instance (if `require` is not object)',
async(() => {
// Define `ng1Component`
const ng1ComponentA: angular.IComponent = {
template: 'ng1A(
)',
controller: class {value = 'A';}
};
const ng1ComponentB: angular.IComponent = {
template: `ng1B({{ $ctrl.getProps() }})`,
require: '^ng1A',
controller: class {
getProps() {
// If all goes well, there should be no keys on `this`
return Object.keys(this).join(', ');
}
}
};
const ng1ComponentC: angular.IComponent = {
template: `ng1C({{ $ctrl.getProps() }})`,
require: ['?ng1A', '^ng1A', '^^ng1A', 'ng1C', '^ng1C', '?^^ng1C'],
controller: class {
getProps() {
// If all goes well, there should be no keys on `this`
return Object.keys(this).join(', ');
}
}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1B'})
class Ng1ComponentBFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1B', elementRef, injector);
}
}
@Directive({selector: 'ng1C'})
class Ng1ComponentCFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1C', elementRef, injector);
}
}
// Define `Ng2Component`
@Component(
{selector: 'ng2', template: 'ng2( |
)'})
class Ng2Component {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.component('ng1A', ng1ComponentA)
.component('ng1B', ng1ComponentB)
.component('ng1C', ng1ComponentC)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentBFacade, Ng1ComponentCFacade, Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(element.textContent)).toBe('ng1A(ng2(ng1B() | ng1C()))');
});
}));
it('should assign resolved values to the controller instance (if `require` is object)',
async(() => {
// Define `ng1Component`
const ng1ComponentA: angular.IComponent = {
template: 'ng1A(
)',
controller: class {value = 'A';}
};
const ng1ComponentB: angular.IComponent = {
template: `ng1B(
ng1A: {{ $ctrl.ng1ASelf.value }} |
^ng1A: {{ $ctrl.ng1ASelfUp.value }} |
^^ng1A: {{ $ctrl.ng1AParentUp.value }} |
ng1B: {{ $ctrl.ng1BSelf.value }} |
^ng1B: {{ $ctrl.ng1BSelfUp.value }} |
^^ng1B: {{ $ctrl.ng1BParentUp.value }}
)`,
require: {
ng1ASelf: '?ng1A',
ng1ASelfUp: '^ng1A',
ng1AParentUp: '^^ng1A',
ng1BSelf: 'ng1B',
ng1BSelfUp: '^ng1B',
ng1BParentUp: '?^^ng1B',
},
controller: class {value = 'B';}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1B'})
class Ng1ComponentBFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1B', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: 'ng2(
)'})
class Ng2Component {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.component('ng1A', ng1ComponentA)
.component('ng1B', ng1ComponentB)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentBFacade, Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(element.textContent))
.toBe(
'ng1A(ng2(ng1B( ng1A: | ^ng1A: A | ^^ng1A: A | ng1B: B | ^ng1B: B | ^^ng1B: )))');
});
}));
it('should assign to controller before calling `$onInit()`', async(() => {
// Define `ng1Component`
const ng1ComponentA: angular.IComponent = {
template: '',
controller: class {value = 'ng1A';}
};
const ng1ComponentB: angular.IComponent = {
template: '$onInit: {{ $ctrl.onInitValue }}',
require: {required: '^^ng1A'},
controller: class {
$onInit() {
const self = this as any;
self.onInitValue = self.required.value;
}
}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1B'})
class Ng1ComponentBFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1B', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class Ng2Component {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.component('ng1A', ng1ComponentA)
.component('ng1B', ng1ComponentB)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentBFacade, Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(element.textContent)).toBe('$onInit: ng1A');
});
}));
it('should use the key as name if the required controller name is omitted', async(() => {
// Define `ng1Component`
const ng1ComponentA: angular.IComponent = {
template: '',
controller: class {value = 'A';}
};
const ng1ComponentB:
angular.IComponent = {template: '', controller: class {value = 'B';}};
const ng1ComponentC: angular.IComponent = {
template:
'ng1A: {{ $ctrl.ng1A.value }} | ng1B: {{ $ctrl.ng1B.value }} | ng1C: {{ $ctrl.ng1C.value }}',
require: {
ng1A: '^^',
ng1B: '?^',
ng1C: '',
},
controller: class {value = 'C';}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1C'})
class Ng1ComponentCFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1C', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class Ng2Component {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.component('ng1A', ng1ComponentA)
.component('ng1B', ng1ComponentB)
.component('ng1C', ng1ComponentC)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentCFacade, Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html('');
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(element.textContent)).toBe('ng1A: A | ng1B: B | ng1C: C');
});
}));
});
});
describe('transclusion', () => {
it('should support single-slot transclusion', async(() => {
let ng2ComponentAInstance: Ng2ComponentA;
let ng2ComponentBInstance: Ng2ComponentB;
// Define `ng1Component`
const ng1Component:
angular.IComponent = {template: 'ng1()', transclude: true};
// 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({{ value }} | )'
})
class Ng2ComponentA {
value = 'foo';
showB = false;
constructor() { ng2ComponentAInstance = 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 `Ng2Module`
@NgModule({
imports: [BrowserModule, UpgradeModule],
declarations: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB],
entryComponents: [Ng2ComponentA]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(foo | ))');
ng2ComponentAInstance.value = 'baz';
ng2ComponentAInstance.showB = true;
$digest(adapter);
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(bar)))');
ng2ComponentBInstance.value = 'qux';
$digest(adapter);
expect(multiTrim(element.textContent)).toBe('ng2A(ng1(baz | ng2B(qux)))');
});
}));
it('should support single-slot transclusion with fallback content', async(() => {
let ng1ControllerInstances: any[] = [];
let ng2ComponentInstance: Ng2Component;
// Define `ng1Component`
const ng1Component: angular.IComponent = {
template: 'ng1({{ $ctrl.value }}
)',
transclude: true,
controller:
class {value = 'from-ng1'; constructor() { ng1ControllerInstances.push(this); }}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: 'ng2({{ value }} | )'})
class Ng2Component {
value = 'from-ng2';
constructor() { ng2ComponentInstance = this; }
}
// 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]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
expect(multiTrim(element.textContent)).toBe('ng2(ng1(from-ng2) | ng1(from-ng1))');
ng1ControllerInstances.forEach(ctrl => ctrl.value = 'ng1-foo');
ng2ComponentInstance.value = 'ng2-bar';
$digest(adapter);
expect(multiTrim(element.textContent)).toBe('ng2(ng1(ng2-bar) | ng1(ng1-foo))');
});
}));
it('should support multi-slot transclusion', async(() => {
let ng2ComponentInstance: Ng2Component;
// Define `ng1Component`
const ng1Component: angular.IComponent = {
template:
'ng1(x() | y())',
transclude: {slotX: 'contentX', slotY: 'contentY'}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({
selector: 'ng2',
template: `
ng2(
{{ x }}1
{{ y }}1
{{ x }}2
{{ y }}2
)`
})
class Ng2Component {
x = 'foo';
y = 'bar';
constructor() { ng2ComponentInstance = this; }
}
// 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() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
expect(multiTrim(element.textContent, true)).toBe('ng2(ng1(x(foo1foo2)|y(bar1bar2)))');
ng2ComponentInstance.x = 'baz';
ng2ComponentInstance.y = 'qux';
$digest(adapter);
expect(multiTrim(element.textContent, true)).toBe('ng2(ng1(x(baz1baz2)|y(qux1qux2)))');
});
}));
it('should support default slot (with fallback content)', async(() => {
let ng1ControllerInstances: any[] = [];
let ng2ComponentInstance: Ng2Component;
// Define `ng1Component`
const ng1Component: angular.IComponent = {
template: 'ng1(default(fallback-{{ $ctrl.value }}
))',
transclude: {slotX: 'contentX', slotY: 'contentY'},
controller:
class {value = 'ng1'; constructor() { ng1ControllerInstances.push(this); }}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({
selector: 'ng2',
template: `
ng2(
({{ x }})
ignored x
{{ x }}-{{ y }}
ignored y
({{ y }})
|
ignored xignored y
)`
})
class Ng2Component {
x = 'foo';
y = 'bar';
constructor() { ng2ComponentInstance = this; }
}
// 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() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
expect(multiTrim(element.textContent, true))
.toBe('ng2(ng1(default((foo)foo-bar(bar)))|ng1(default(fallback-ng1)))');
ng1ControllerInstances.forEach(ctrl => ctrl.value = 'ng1-plus');
ng2ComponentInstance.x = 'baz';
ng2ComponentInstance.y = 'qux';
$digest(adapter);
expect(multiTrim(element.textContent, true))
.toBe('ng2(ng1(default((baz)baz-qux(qux)))|ng1(default(fallback-ng1-plus)))');
});
}));
it('should support optional transclusion slots (with fallback content)', async(() => {
let ng1ControllerInstances: any[] = [];
let ng2ComponentInstance: Ng2Component;
// Define `ng1Component`
const ng1Component: angular.IComponent = {
template: `
ng1(
x({{ $ctrl.x }}
) |
y({{ $ctrl.y }}
)
)`,
transclude: {slotX: '?contentX', slotY: '?contentY'},
controller: class {
x = 'ng1X'; y = 'ng1Y'; constructor() { ng1ControllerInstances.push(this); }
}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({
selector: 'ng2',
template: `
ng2(
{{ x }} |
{{ y }}
)`
})
class Ng2Component {
x = 'ng2X';
y = 'ng2Y';
constructor() { ng2ComponentInstance = this; }
}
// 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() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
expect(multiTrim(element.textContent, true))
.toBe('ng2(ng1(x(ng2X)|y(ng1Y))|ng1(x(ng1X)|y(ng2Y)))');
ng1ControllerInstances.forEach(ctrl => {
ctrl.x = 'ng1X-foo';
ctrl.y = 'ng1Y-bar';
});
ng2ComponentInstance.x = 'ng2X-baz';
ng2ComponentInstance.y = 'ng2Y-qux';
$digest(adapter);
expect(multiTrim(element.textContent, true))
.toBe('ng2(ng1(x(ng2X-baz)|y(ng1Y-bar))|ng1(x(ng1X-foo)|y(ng2Y-qux)))');
});
}));
it('should throw if a non-optional slot is not filled', async(() => {
let errorMessage: string;
// Define `ng1Component`
const ng1Component: angular.IComponent = {
template: '',
transclude: {slotX: '?contentX', slotY: 'contentY'}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class Ng2Component {
}
// Define `ng1Module`
const ng1Module =
angular.module('ng1Module', [])
.value('$exceptionHandler', (error: Error) => errorMessage = error.message)
.component('ng1', ng1Component)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
imports: [BrowserModule, UpgradeModule],
declarations: [Ng1ComponentFacade, Ng2Component],
entryComponents: [Ng2Component]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
expect(errorMessage)
.toContain('Required transclusion slot \'slotY\' on directive: ng1');
});
}));
it('should support structural directives in transcluded content', async(() => {
let ng2ComponentInstance: Ng2Component;
// Define `ng1Component`
const ng1Component: angular.IComponent = {
template:
'ng1(x() | default())',
transclude: {slotX: 'contentX'}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({
selector: 'ng2',
template: `
ng2(
{{ x }}1
{{ y }}1
{{ x }}2
{{ y }}2
)`
})
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 `Ng2Module`
@NgModule({
imports: [BrowserModule, UpgradeModule],
declarations: [Ng1ComponentFacade, Ng2Component],
entryComponents: [Ng2Component],
schemas: [NO_ERRORS_SCHEMA]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
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);
expect(multiTrim(element.textContent, true)).toBe('ng2(ng1(x(baz2)|default(qux1)))');
ng2ComponentInstance.show = true;
$digest(adapter);
expect(multiTrim(element.textContent, true)).toBe('ng2(ng1(x(baz1)|default(qux2)))');
});
}));
});
describe('lifecycle hooks', () => {
it('should call `$onChanges()` on binding destination (prototype)', fakeAsync(() => {
const scopeOnChanges = jasmine.createSpy('scopeOnChanges');
const controllerOnChangesA = jasmine.createSpy('controllerOnChangesA');
const controllerOnChangesB = jasmine.createSpy('controllerOnChangesB');
let ng2ComponentInstance: Ng2Component;
// Define `ng1Directive`
const ng1DirectiveA: angular.IDirective = {
template: '',
scope: {inputA: '<'},
bindToController: false,
controllerAs: '$ctrl',
controller:
class {$onChanges(changes: SimpleChanges) { controllerOnChangesA(changes); }}
};
const ng1DirectiveB: angular.IDirective = {
template: '',
scope: {inputB: '<'},
bindToController: true,
controllerAs: '$ctrl',
controller:
class {$onChanges(changes: SimpleChanges) { controllerOnChangesB(changes); }}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1A'})
class Ng1ComponentAFacade extends UpgradeComponent {
@Input() inputA: any;
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1A', elementRef, injector);
}
}
@Directive({selector: 'ng1B'})
class Ng1ComponentBFacade extends UpgradeComponent {
@Input() inputB: any;
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1B', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({
selector: 'ng2',
template: ' | '
})
class Ng2Component {
data = {foo: 'bar'};
constructor() { ng2ComponentInstance = this; }
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.directive('ng1A', () => ng1DirectiveA)
.directive('ng1B', () => ng1DirectiveB)
.directive('ng2', downgradeComponent({component: Ng2Component}))
.run(($rootScope: angular.IRootScopeService) => {
Object.getPrototypeOf($rootScope)['$onChanges'] = scopeOnChanges;
});
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentAFacade, Ng1ComponentBFacade, Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
// Initial change
expect(scopeOnChanges.calls.count()).toBe(1);
expect(controllerOnChangesA).not.toHaveBeenCalled();
expect(controllerOnChangesB.calls.count()).toBe(1);
expect(scopeOnChanges.calls.argsFor(0)[0]).toEqual({inputA: jasmine.any(Object)});
expect(scopeOnChanges.calls.argsFor(0)[0].inputA.currentValue).toEqual({foo: 'bar'});
expect(scopeOnChanges.calls.argsFor(0)[0].inputA.isFirstChange()).toBe(true);
expect(controllerOnChangesB.calls.argsFor(0)[0].inputB.currentValue).toEqual({
foo: 'bar'
});
expect(controllerOnChangesB.calls.argsFor(0)[0].inputB.isFirstChange()).toBe(true);
// Change: Re-assign `data`
ng2ComponentInstance.data = {foo: 'baz'};
$digest(adapter);
tick();
expect(scopeOnChanges.calls.count()).toBe(2);
expect(controllerOnChangesA).not.toHaveBeenCalled();
expect(controllerOnChangesB.calls.count()).toBe(2);
expect(scopeOnChanges.calls.argsFor(1)[0]).toEqual({inputA: jasmine.any(Object)});
expect(scopeOnChanges.calls.argsFor(1)[0].inputA.previousValue).toEqual({foo: 'bar'});
expect(scopeOnChanges.calls.argsFor(1)[0].inputA.currentValue).toEqual({foo: 'baz'});
expect(scopeOnChanges.calls.argsFor(1)[0].inputA.isFirstChange()).toBe(false);
expect(controllerOnChangesB.calls.argsFor(1)[0].inputB.previousValue).toEqual({
foo: 'bar'
});
expect(controllerOnChangesB.calls.argsFor(1)[0].inputB.currentValue).toEqual({
foo: 'baz'
});
expect(controllerOnChangesB.calls.argsFor(1)[0].inputB.isFirstChange()).toBe(false);
// No change: Update internal property
ng2ComponentInstance.data.foo = 'qux';
$digest(adapter);
tick();
expect(scopeOnChanges.calls.count()).toBe(2);
expect(controllerOnChangesA).not.toHaveBeenCalled();
expect(controllerOnChangesB.calls.count()).toBe(2);
// Change: Re-assign `data` (even if it looks the same)
ng2ComponentInstance.data = {foo: 'qux'};
$digest(adapter);
tick();
expect(scopeOnChanges.calls.count()).toBe(3);
expect(controllerOnChangesA).not.toHaveBeenCalled();
expect(controllerOnChangesB.calls.count()).toBe(3);
expect(scopeOnChanges.calls.argsFor(2)[0]).toEqual({inputA: jasmine.any(Object)});
expect(scopeOnChanges.calls.argsFor(2)[0].inputA.previousValue).toEqual({foo: 'qux'});
expect(scopeOnChanges.calls.argsFor(2)[0].inputA.currentValue).toEqual({foo: 'qux'});
expect(scopeOnChanges.calls.argsFor(2)[0].inputA.isFirstChange()).toBe(false);
expect(controllerOnChangesB.calls.argsFor(2)[0].inputB.previousValue).toEqual({
foo: 'qux'
});
expect(controllerOnChangesB.calls.argsFor(2)[0].inputB.currentValue).toEqual({
foo: 'qux'
});
expect(controllerOnChangesB.calls.argsFor(2)[0].inputB.isFirstChange()).toBe(false);
});
}));
it('should call `$onChanges()` on binding destination (instance)', fakeAsync(() => {
const scopeOnChangesA = jasmine.createSpy('scopeOnChangesA');
const scopeOnChangesB = jasmine.createSpy('scopeOnChangesB');
const controllerOnChangesA = jasmine.createSpy('controllerOnChangesA');
const controllerOnChangesB = jasmine.createSpy('controllerOnChangesB');
let ng2ComponentInstance: Ng2Component;
// Define `ng1Directive`
const ng1DirectiveA: angular.IDirective = {
template: '',
scope: {inputA: '<'},
bindToController: false,
controllerAs: '$ctrl',
controller: class {
constructor($scope: angular.IScope) {
$scope['$onChanges'] = scopeOnChangesA;
(this as any).$onChanges = controllerOnChangesA;
}
}
};
const ng1DirectiveB: angular.IDirective = {
template: '',
scope: {inputB: '<'},
bindToController: true,
controllerAs: '$ctrl',
controller: class {
constructor($scope: angular.IScope) {
$scope['$onChanges'] = scopeOnChangesB;
(this as any).$onChanges = controllerOnChangesB;
}
}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1A'})
class Ng1ComponentAFacade extends UpgradeComponent {
@Input() inputA: any;
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1A', elementRef, injector);
}
}
@Directive({selector: 'ng1B'})
class Ng1ComponentBFacade extends UpgradeComponent {
@Input() inputB: any;
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1B', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({
selector: 'ng2',
template: ' | '
})
class Ng2Component {
data = {foo: 'bar'};
constructor() { ng2ComponentInstance = this; }
}
// 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() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
// Initial change
expect(scopeOnChangesA.calls.count()).toBe(1);
expect(scopeOnChangesB).not.toHaveBeenCalled();
expect(controllerOnChangesA).not.toHaveBeenCalled();
expect(controllerOnChangesB.calls.count()).toBe(1);
expect(scopeOnChangesA.calls.argsFor(0)[0].inputA.currentValue).toEqual({foo: 'bar'});
expect(scopeOnChangesA.calls.argsFor(0)[0].inputA.isFirstChange()).toBe(true);
expect(controllerOnChangesB.calls.argsFor(0)[0].inputB.currentValue).toEqual({
foo: 'bar'
});
expect(controllerOnChangesB.calls.argsFor(0)[0].inputB.isFirstChange()).toBe(true);
// Change: Re-assign `data`
ng2ComponentInstance.data = {foo: 'baz'};
$digest(adapter);
tick();
expect(scopeOnChangesA.calls.count()).toBe(2);
expect(scopeOnChangesB).not.toHaveBeenCalled();
expect(controllerOnChangesA).not.toHaveBeenCalled();
expect(controllerOnChangesB.calls.count()).toBe(2);
expect(scopeOnChangesA.calls.argsFor(1)[0].inputA.previousValue).toEqual({foo: 'bar'});
expect(scopeOnChangesA.calls.argsFor(1)[0].inputA.currentValue).toEqual({foo: 'baz'});
expect(scopeOnChangesA.calls.argsFor(1)[0].inputA.isFirstChange()).toBe(false);
expect(controllerOnChangesB.calls.argsFor(1)[0].inputB.previousValue).toEqual({
foo: 'bar'
});
expect(controllerOnChangesB.calls.argsFor(1)[0].inputB.currentValue).toEqual({
foo: 'baz'
});
expect(controllerOnChangesB.calls.argsFor(1)[0].inputB.isFirstChange()).toBe(false);
// No change: Update internal property
ng2ComponentInstance.data.foo = 'qux';
$digest(adapter);
tick();
expect(scopeOnChangesA.calls.count()).toBe(2);
expect(scopeOnChangesB).not.toHaveBeenCalled();
expect(controllerOnChangesA).not.toHaveBeenCalled();
expect(controllerOnChangesB.calls.count()).toBe(2);
// Change: Re-assign `data` (even if it looks the same)
ng2ComponentInstance.data = {foo: 'qux'};
$digest(adapter);
tick();
expect(scopeOnChangesA.calls.count()).toBe(3);
expect(scopeOnChangesB).not.toHaveBeenCalled();
expect(controllerOnChangesA).not.toHaveBeenCalled();
expect(controllerOnChangesB.calls.count()).toBe(3);
expect(scopeOnChangesA.calls.argsFor(2)[0].inputA.previousValue).toEqual({foo: 'qux'});
expect(scopeOnChangesA.calls.argsFor(2)[0].inputA.currentValue).toEqual({foo: 'qux'});
expect(scopeOnChangesA.calls.argsFor(2)[0].inputA.isFirstChange()).toBe(false);
expect(controllerOnChangesB.calls.argsFor(2)[0].inputB.previousValue).toEqual({
foo: 'qux'
});
expect(controllerOnChangesB.calls.argsFor(2)[0].inputB.currentValue).toEqual({
foo: 'qux'
});
expect(controllerOnChangesB.calls.argsFor(2)[0].inputB.isFirstChange()).toBe(false);
});
}));
it('should call `$onInit()` on controller', async(() => {
// Define `ng1Directive`
const ng1DirectiveA: angular.IDirective = {
template: 'Called: {{ called }}',
bindToController: false,
controller: class {
constructor(private $scope: angular.IScope) { $scope['called'] = 'no'; }
$onInit() { this.$scope['called'] = 'yes'; }
}
};
const ng1DirectiveB: angular.IDirective = {
template: 'Called: {{ called }}',
bindToController: true,
controller: class {
constructor($scope: angular.IScope) {
$scope['called'] = 'no';
(this as any)['$onInit'] = () => $scope['called'] = 'yes';
}
}
};
// 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);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ' | '})
class 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() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(element.textContent)).toBe('Called: yes | Called: yes');
});
}));
it('should not call `$onInit()` on scope', async(() => {
// Define `ng1Directive`
const ng1DirectiveA: angular.IDirective = {
template: 'Called: {{ called }}',
bindToController: false,
controller: class {
constructor($scope: angular.IScope) {
$scope['called'] = 'no';
$scope['$onInit'] = () => $scope['called'] = 'yes';
Object.getPrototypeOf($scope)['$onInit'] = () => $scope['called'] = 'yes';
}
}
};
const ng1DirectiveB: angular.IDirective = {
template: 'Called: {{ called }}',
bindToController: true,
controller: class {
constructor($scope: angular.IScope) {
$scope['called'] = 'no';
$scope['$onInit'] = () => $scope['called'] = 'yes';
Object.getPrototypeOf($scope)['$onInit'] = () => $scope['called'] = 'yes';
}
}
};
// 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);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ' | '})
class 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() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(element.textContent)).toBe('Called: no | Called: no');
});
}));
it('should call `$postLink()` on controller', async(() => {
// Define `ng1Directive`
const ng1DirectiveA: angular.IDirective = {
template: 'Called: {{ called }}',
bindToController: false,
controller: class {
constructor(private $scope: angular.IScope) { $scope['called'] = 'no'; }
$postLink() { this.$scope['called'] = 'yes'; }
}
};
const ng1DirectiveB: angular.IDirective = {
template: 'Called: {{ called }}',
bindToController: true,
controller: class {
constructor($scope: angular.IScope) {
$scope['called'] = 'no';
(this as any)['$postLink'] = () => $scope['called'] = 'yes';
}
}
};
// 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);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ' | '})
class 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() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(element.textContent)).toBe('Called: yes | Called: yes');
});
}));
it('should not call `$postLink()` on scope', async(() => {
// Define `ng1Directive`
const ng1DirectiveA: angular.IDirective = {
template: 'Called: {{ called }}',
bindToController: false,
controller: class {
constructor($scope: angular.IScope) {
$scope['called'] = 'no';
$scope['$postLink'] = () => $scope['called'] = 'yes';
Object.getPrototypeOf($scope)['$postLink'] = () => $scope['called'] = 'yes';
}
}
};
const ng1DirectiveB: angular.IDirective = {
template: 'Called: {{ called }}',
bindToController: true,
controller: class {
constructor($scope: angular.IScope) {
$scope['called'] = 'no';
$scope['$postLink'] = () => $scope['called'] = 'yes';
Object.getPrototypeOf($scope)['$postLink'] = () => $scope['called'] = 'yes';
}
}
};
// 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);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ' | '})
class 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() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(element.textContent)).toBe('Called: no | Called: no');
});
}));
it('should call `$doCheck()` on controller', async(() => {
const controllerDoCheckA = jasmine.createSpy('controllerDoCheckA');
const controllerDoCheckB = jasmine.createSpy('controllerDoCheckB');
// Define `ng1Directive`
const ng1DirectiveA: angular.IDirective = {
template: 'ng1A',
bindToController: false,
controller: class {$doCheck() { controllerDoCheckA(); }}
};
const ng1DirectiveB: angular.IDirective = {
template: 'ng1B',
bindToController: true,
controller: class {constructor() { (this as any)['$doCheck'] = controllerDoCheckB; }}
};
// 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);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ' | '})
class 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() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
// Get to a stable `$digest` state.
$digest(adapter);
// Initial change.
// (Do not use a specific number due to differences between AngularJS 1.5/1.6.)
expect(controllerDoCheckA.calls.count()).toBeGreaterThan(0);
expect(controllerDoCheckB.calls.count()).toBeGreaterThan(0);
controllerDoCheckA.calls.reset();
controllerDoCheckB.calls.reset();
// Run a `$digest`
$digest(adapter);
expect(controllerDoCheckA.calls.count()).toBe(1);
expect(controllerDoCheckB.calls.count()).toBe(1);
// Run another `$digest`
$digest(adapter);
expect(controllerDoCheckA.calls.count()).toBe(2);
expect(controllerDoCheckB.calls.count()).toBe(2);
});
}));
it('should not call `$doCheck()` on scope', async(() => {
const scopeDoCheck = jasmine.createSpy('scopeDoCheck');
// Define `ng1Directive`
const ng1DirectiveA: angular.IDirective = {
template: 'ng1A',
bindToController: false,
controller: class {
constructor(private $scope: angular.IScope) { $scope['$doCheck'] = scopeDoCheck; }
}
};
const ng1DirectiveB: angular.IDirective = {
template: 'ng1B',
bindToController: true,
controller: class {
constructor(private $scope: angular.IScope) { $scope['$doCheck'] = scopeDoCheck; }
}
};
// 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);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ' | '})
class 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() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
// Initial change
expect(scopeDoCheck).not.toHaveBeenCalled();
// Run a `$digest`
$digest(adapter);
expect(scopeDoCheck).not.toHaveBeenCalled();
// Run another `$digest`
$digest(adapter);
expect(scopeDoCheck).not.toHaveBeenCalled();
});
}));
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(); }}
};
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);
}
}
@Directive({selector: 'ng1B'})
class Ng1ComponentBFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1B', elementRef, injector);
}
}
// Define `Ng2Component`
@Component(
{selector: 'ng2', template: ' |
'})
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 `Ng2Module`
@NgModule({
declarations: [Ng1ComponentAFacade, Ng1ComponentBFacade, Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html('');
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();
$rootScope.$apply('destroyFromNg1 = true');
expect(multiTrim(document.body.textContent)).toBe('');
expect(controllerOnDestroyA).toHaveBeenCalled();
expect(controllerOnDestroyB).toHaveBeenCalled();
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();
$rootScope.$apply('destroyFromNg2 = true');
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');
// Define `ng1Directive`
const ng1DirectiveA: angular.IDirective = {
template: 'ng1A',
scope: {},
bindToController: false,
controllerAs: '$ctrl',
controller: class {
constructor($scope: angular.IScope) {
$scope['$onDestroy'] = scopeOnDestroy;
Object.getPrototypeOf($scope)['$onDestroy'] = scopeOnDestroy;
}
}
};
const ng1DirectiveB: angular.IDirective = {
template: 'ng1B',
scope: {},
bindToController: true,
controllerAs: '$ctrl',
controller: class {
constructor($scope: angular.IScope) {
$scope['$onDestroy'] = scopeOnDestroy;
Object.getPrototypeOf($scope)['$onDestroy'] = scopeOnDestroy;
}
}
};
// 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);
}
}
// Define `Ng2Component`
@Component(
{selector: 'ng2', template: ' |
'})
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 `Ng2Module`
@NgModule({
declarations: [Ng1ComponentAFacade, Ng1ComponentBFacade, Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html('');
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(scopeOnDestroy).not.toHaveBeenCalled();
$rootScope.$apply('destroyFromNg1 = true');
expect(multiTrim(document.body.textContent)).toBe('');
expect(scopeOnDestroy).not.toHaveBeenCalled();
$rootScope.$apply('destroyFromNg1 = false');
expect(multiTrim(document.body.textContent)).toBe('ng1A | ng1B');
expect(scopeOnDestroy).not.toHaveBeenCalled();
$rootScope.$apply('destroyFromNg2 = true');
expect(multiTrim(document.body.textContent)).toBe('');
expect(scopeOnDestroy).not.toHaveBeenCalled();
});
}));
it('should be called in order `$onChanges()` > `$onInit()` > `$doCheck()` > `$postLink()`',
async(() => {
// Define `ng1Component`
const ng1Component: angular.IComponent = {
// `$doCheck()` will keep getting called as long as the interpolated value keeps
// changing (by appending `> $doCheck`). Only care about the first 4 values.
template: '{{ $ctrl.calls.slice(0, 4).join(" > ") }}',
bindings: {value: '<'},
controller: class {
calls: string[] = [];
$onChanges() { this.calls.push('$onChanges'); }
$onInit() { this.calls.push('$onInit'); }
$doCheck() { this.calls.push('$doCheck'); }
$postLink() { this.calls.push('$postLink'); }
}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
@Input() value: any;
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class Ng2Component {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.component('ng1', ng1Component)
.directive('ng2', downgradeComponent({component: Ng2Component}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentFacade, Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(element.textContent))
.toBe('$onChanges > $onInit > $doCheck > $postLink');
});
}));
});
describe('destroying the upgraded component', () => {
it('should destroy `$componentScope`', async(() => {
const scopeDestroyListener = jasmine.createSpy('scopeDestroyListener');
let ng2ComponentAInstance: Ng2ComponentA;
// Define `ng1Component`
const ng1Component: angular.IComponent = {
controller: class {
constructor($scope: angular.IScope) { $scope.$on('$destroy', scopeDestroyListener); }
}
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2A', template: ''})
class Ng2ComponentA {
destroyIt = false;
constructor() { ng2ComponentAInstance = this; }
}
@Component({selector: 'ng2B', template: ''})
class Ng2ComponentB {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.component('ng1', ng1Component)
.directive('ng2A', downgradeComponent({component: Ng2ComponentA}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB],
entryComponents: [Ng2ComponentA],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
expect(scopeDestroyListener).not.toHaveBeenCalled();
ng2ComponentAInstance.destroyIt = true;
$digest(adapter);
expect(scopeDestroyListener).toHaveBeenCalledTimes(1);
});
}));
it('should emit `$destroy` on `$element` and descendants', async(() => {
const elementDestroyListener = jasmine.createSpy('elementDestroyListener');
const descendantDestroyListener = jasmine.createSpy('descendantDestroyListener');
let ng2ComponentAInstance: Ng2ComponentA;
// Define `ng1Component`
const ng1Component: angular.IComponent = {
controller: class {
constructor($element: angular.IAugmentedJQuery) {
$element.on !('$destroy', elementDestroyListener);
$element.contents !().on !('$destroy', descendantDestroyListener);
}
},
template: ''
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2A', template: ''})
class Ng2ComponentA {
destroyIt = false;
constructor() { ng2ComponentAInstance = this; }
}
@Component({selector: 'ng2B', template: ''})
class Ng2ComponentB {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.component('ng1', ng1Component)
.directive('ng2A', downgradeComponent({component: Ng2ComponentA}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB],
entryComponents: [Ng2ComponentA],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
expect(elementDestroyListener).not.toHaveBeenCalled();
expect(descendantDestroyListener).not.toHaveBeenCalled();
ng2ComponentAInstance.destroyIt = true;
$digest(adapter);
expect(elementDestroyListener).toHaveBeenCalledTimes(1);
expect(descendantDestroyListener).toHaveBeenCalledTimes(1);
});
}));
it('should clear data on `$element` and descendants`', async(() => {
let ng1ComponentElement: angular.IAugmentedJQuery;
let ng2ComponentAInstance: Ng2ComponentA;
// Define `ng1Component`
const ng1Component: angular.IComponent = {
controller: class {
constructor($element: angular.IAugmentedJQuery) {
$element.data !('test', 1);
$element.contents !().data !('test', 2);
ng1ComponentElement = $element;
}
},
template: ''
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2A', template: ''})
class Ng2ComponentA {
destroyIt = false;
constructor() { ng2ComponentAInstance = this; }
}
@Component({selector: 'ng2B', template: ''})
class Ng2ComponentB {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.component('ng1', ng1Component)
.directive('ng2A', downgradeComponent({component: Ng2ComponentA}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB],
entryComponents: [Ng2ComponentA],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
expect(ng1ComponentElement.data !('test')).toBe(1);
expect(ng1ComponentElement.contents !().data !('test')).toBe(2);
ng2ComponentAInstance.destroyIt = true;
$digest(adapter);
expect(ng1ComponentElement.data !('test')).toBeUndefined();
expect(ng1ComponentElement.contents !().data !('test')).toBeUndefined();
});
}));
it('should clear dom listeners on `$element` and descendants`', async(() => {
const elementClickListener = jasmine.createSpy('elementClickListener');
const descendantClickListener = jasmine.createSpy('descendantClickListener');
let ng1DescendantElement: angular.IAugmentedJQuery;
let ng2ComponentAInstance: Ng2ComponentA;
// Define `ng1Component`
const ng1Component: angular.IComponent = {
controller: class {
constructor($element: angular.IAugmentedJQuery) {
ng1DescendantElement = $element.contents !();
$element.on !('click', elementClickListener);
ng1DescendantElement.on !('click', descendantClickListener);
}
},
template: ''
};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2A', template: ''})
class Ng2ComponentA {
destroyIt = false;
constructor() { ng2ComponentAInstance = this; }
}
@Component({selector: 'ng2B', template: ''})
class Ng2ComponentB {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1Module', [])
.component('ng1', ng1Component)
.directive('ng2A', downgradeComponent({component: Ng2ComponentA}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB],
entryComponents: [Ng2ComponentA],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
(ng1DescendantElement[0] as HTMLElement).click();
expect(elementClickListener).toHaveBeenCalledTimes(1);
expect(descendantClickListener).toHaveBeenCalledTimes(1);
ng2ComponentAInstance.destroyIt = true;
$digest(adapter);
(ng1DescendantElement[0] as HTMLElement).click();
expect(elementClickListener).toHaveBeenCalledTimes(1);
expect(descendantClickListener).toHaveBeenCalledTimes(1);
});
}));
it('should clean up `$doCheck()` watchers from the parent scope', async(() => {
let ng2Component: Ng2Component;
// Define `ng1Component`
const ng1Component:
angular.IComponent = {template: 'ng1', controller: class {$doCheck() {}}};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2', template: ''})
class Ng2Component {
doShow: boolean = false;
constructor(@Inject($SCOPE) public $scope: angular.IScope) { ng2Component = this; }
}
// 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]
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(adapter => {
const getWatcherCount: () => number = () =>
(ng2Component.$scope as any).$$watchers.length;
const baseWatcherCount = getWatcherCount();
expect(multiTrim(document.body.textContent)).toBe('');
ng2Component.doShow = true;
$digest(adapter);
expect(multiTrim(document.body.textContent)).toBe('ng1');
expect(getWatcherCount()).toBe(baseWatcherCount + 1);
ng2Component.doShow = false;
$digest(adapter);
expect(multiTrim(document.body.textContent)).toBe('');
expect(getWatcherCount()).toBe(baseWatcherCount);
ng2Component.doShow = true;
$digest(adapter);
expect(multiTrim(document.body.textContent)).toBe('ng1');
expect(getWatcherCount()).toBe(baseWatcherCount + 1);
});
}));
});
it('should support ng2 > ng1 > ng2 (no inputs/outputs)', async(() => {
// Define `ng1Component`
const ng1Component: angular.IComponent = {template: 'ng1X()'};
// Define `Ng1ComponentFacade`
@Directive({selector: 'ng1X'})
class Ng1ComponentFacade extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1X', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2-a', template: 'ng2A()'})
class Ng2ComponentA {
}
@Component({selector: 'ng2-b', template: 'ng2B'})
class 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: [Ng1ComponentFacade, Ng2ComponentA, Ng2ComponentB],
entryComponents: [Ng2ComponentA, Ng2ComponentB],
imports: [BrowserModule, UpgradeModule],
schemas: [NO_ERRORS_SCHEMA],
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(document.body.textContent)).toBe('ng2A(ng1X(ng2B))');
});
}));
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;
constructor() { ng1ControllerXInstance = this; }
}
const ng1Component: angular.IComponent = {
template: `
ng1X({{ $ctrl.ng1XInputA }}, {{ $ctrl.ng1XInputB.value }}, {{ $ctrl.ng1XInputC.value }}) |
`,
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;
// TODO(issue/24571): remove '!'.
@Output() ng1XOutputA !: EventEmitter;
// TODO(issue/24571): remove '!'.
@Output() ng1XOutputB !: EventEmitter;
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1X', elementRef, injector);
}
}
// Define `Ng2Component`
@Component({
selector: 'ng2-a',
template: `
ng2A({{ ng2ADataA.value }}, {{ ng2ADataB.value }}, {{ ng2ADataC.value }}) |
`
})
class Ng2ComponentA {
ng2ADataA = {value: 'foo'};
ng2ADataB = {value: 'bar'};
ng2ADataC = {value: 'baz'};
constructor() { ng2ComponentAInstance = this; }
}
@Component({selector: 'ng2-b', template: 'ng2B({{ ng2BInputA }}, {{ ng2BInputC }})'})
class Ng2ComponentB {
@Input('ng2BInput1') ng2BInputA: any;
@Input() ng2BInputC: any;
@Output() ng2BOutputC = new EventEmitter();
constructor() { ng2ComponentBInstance = this; }
}
// 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() {}
}
// Bootstrap
const element = html(``);
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();
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();
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();
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();
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();
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();
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();
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();
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`
const ng1ComponentA: angular.IComponent = {
template: 'ng1A()',
controller: class {value = 'ng1A';}
};
const ng1ComponentB: angular.IComponent = {
template:
'ng1B(^^ng1A: {{ $ctrl.ng1A.value }} | ?^^ng1B: {{ $ctrl.ng1B.value }} | ^ng1B: {{ $ctrl.ng1BSelf.value }})',
require: {ng1A: '^^', ng1B: '?^^', ng1BSelf: '^ng1B'},
controller: class {value = 'ng1B';}
};
// 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);
}
}
// Define `Ng2Component`
@Component({selector: 'ng2-a', template: 'ng2A()'})
class Ng2ComponentA {
}
@Component({selector: 'ng2-b', template: 'ng2B()'})
class Ng2ComponentB {
}
// Define `ng1Module`
const ng1Module = angular.module('ng1', [])
.component('ng1A', ng1ComponentA)
.component('ng1B', ng1ComponentB)
.directive('ng2A', downgradeComponent({component: Ng2ComponentA}))
.directive('ng2B', downgradeComponent({component: Ng2ComponentB}));
// Define `Ng2Module`
@NgModule({
declarations: [Ng1ComponentAFacade, Ng1ComponentBFacade, Ng2ComponentA, Ng2ComponentB],
entryComponents: [Ng2ComponentA, Ng2ComponentB],
imports: [BrowserModule, UpgradeModule],
schemas: [NO_ERRORS_SCHEMA],
})
class Ng2Module {
ngDoBootstrap() {}
}
// Bootstrap
const element = html(``);
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then(() => {
expect(multiTrim(document.body.textContent))
.toBe('ng2A(ng1A(ng2B(ng1B(^^ng1A: ng1A | ?^^ng1B: | ^ng1B: ng1B))))');
});
}));
});
});