angular/modules/@angular/upgrade/test/static/integration/content_projection_spec.ts
Georgios Kalpakas 1367cd9569 fix(upgrade): respect hierarchical injectors for downgraded components (#14037)
Correctly wire up hierarchical injectors for downgraded components in
`upgrade/static`: Downgraded components inherit the injector of the first
downgraded component up the DOM tree.

This is similar to (part of) d91a86a, but for `upgrade/static`.

POSSIBLE BREAKING CHANGE:

In order to enable more control over the wiring of downgraded components and
their content (which eventually allows better control over features like
injector setup and content projection), it was necessary to change the
implementation of the directives generated for downgraed components.

The directives are now terminal and manually take care of projecting and
compiling their contents in the post-linking function. This is similar to how
the dynamic version of `upgrade` does it.

This is not expected to affect apps, since the relative order of individual
operations is preserved. Still, it is difficult to predict how every possible
usecase may be affected.
2017-02-01 09:29:51 -06:00

107 lines
4.0 KiB
TypeScript

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component, Directive, ElementRef, Injector, NgModule, destroyPlatform} from '@angular/core';
import {async} from '@angular/core/testing';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import * as angular from '@angular/upgrade/src/common/angular1';
import {UpgradeComponent, UpgradeModule, downgradeComponent} from '@angular/upgrade/static';
import {bootstrap, html} from '../test_helpers';
export function main() {
describe('content projection', () => {
beforeEach(() => destroyPlatform());
afterEach(() => destroyPlatform());
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';
}
// 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';
});
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~)]');
});
}));
it('should instantiate ng1 in ng2 template and project content', async(() => {
@Component({
selector: 'ng2',
template: `{{ 'ng2(' }}<ng1>{{ transclude }}</ng1>{{ ')' }}`,
})
class Ng2Component {
prop = 'ng2';
transclude = 'ng2-transclude';
}
@Directive({selector: 'ng1'})
class Ng1WrapperComponent extends UpgradeComponent {
constructor(elementRef: ElementRef, injector: Injector) {
super('ng1', elementRef, injector);
}
}
@NgModule({
declarations: [Ng1WrapperComponent, Ng2Component],
entryComponents: [Ng2Component],
imports: [BrowserModule, UpgradeModule]
})
class Ng2Module {
ngDoBootstrap() {}
}
const ng1Module =
angular.module('ng1', [])
.directive('ng1', () => ({
transclude: true,
template: '{{ prop }}(<ng-transclude></ng-transclude>)'
}))
.directive('ng2', downgradeComponent({component: Ng2Component}))
.run(($rootScope: angular.IRootScopeService) => {
$rootScope['prop'] = 'ng1';
$rootScope['transclude'] = 'ng1-transclude';
});
const element = html('<div>{{ \'ng1(\' }}<ng2></ng2>{{ \')\' }}</div>');
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
expect(document.body.textContent).toEqual('ng1(ng2(ng1(ng2-transclude)))');
});
}));
});
}