2016-10-19 16:41:04 -04:00
|
|
|
/**
|
|
|
|
* @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
|
|
|
|
*/
|
|
|
|
|
2017-02-03 05:11:16 -05:00
|
|
|
import {Component, Directive, ElementRef, Injector, Input, NgModule, destroyPlatform} from '@angular/core';
|
2016-10-19 16:41:04 -04:00
|
|
|
import {async} from '@angular/core/testing';
|
|
|
|
import {BrowserModule} from '@angular/platform-browser';
|
|
|
|
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
feat(upgrade): return a function (instead of array) from `downgradeInjectable()` (#14037)
This makes it more consistent with the dynamic version of `upgrade` and makes it
possible to share code between the dynamic and static versions.
This commit also refactors the file layout, moving common and dynamic-specific
files to `common/` and `dynamic/` directories respectively and renaming `aot/`
to `static/`.
Some private keys, used as AngularJS DI tokens, have also been renamed, but this
should not affect apps, since these keys are undocumented and not supposed to
be used externally.
BREAKING CHANGE:
Previously, `upgrade/static/downgradeInjectable` returned an array of the form:
```js
['dep1', 'dep2', ..., function factory(dep1, dep2, ...) { ... }]
```
Now it returns a function with an `$inject` property:
```js
factory.$inject = ['dep1', 'dep2', ...];
function factory(dep1, dep2, ...) { ... }
```
It shouldn't affect the behavior of apps, since both forms are equally suitable
to be used for registering AngularJS injectable services, but it is possible
that type-checking might fail or that current code breaks if it relies on the
returned value being an array.
2017-01-13 09:20:28 -05:00
|
|
|
import * as angular from '@angular/upgrade/src/common/angular1';
|
2016-10-20 22:35:35 -04:00
|
|
|
import {UpgradeComponent, UpgradeModule, downgradeComponent} from '@angular/upgrade/static';
|
2016-10-19 16:41:04 -04:00
|
|
|
|
2017-02-03 05:11:16 -05:00
|
|
|
import {bootstrap, html, multiTrim} from '../test_helpers';
|
2016-10-19 16:41:04 -04:00
|
|
|
|
|
|
|
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)
|
2017-01-16 16:52:42 -05:00
|
|
|
@Component({selector: 'ng2', template: `{{ prop }}(<ng-content></ng-content>)`})
|
2016-10-19 16:41:04 -04:00
|
|
|
class Ng2Component {
|
2017-01-16 16:52:42 -05:00
|
|
|
prop = 'NG2';
|
|
|
|
ngContent = 'ng2-content';
|
2016-10-19 16:41:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2017-01-16 16:52:42 -05:00
|
|
|
.directive('ng2', downgradeComponent({component: Ng2Component}))
|
|
|
|
.run(($rootScope: angular.IRootScopeService) => {
|
|
|
|
$rootScope['prop'] = 'NG1';
|
|
|
|
$rootScope['ngContent'] = 'ng1-content';
|
|
|
|
});
|
2016-10-19 16:41:04 -04:00
|
|
|
|
2017-01-16 16:52:42 -05:00
|
|
|
const element = html('<div>{{ \'ng1[\' }}<ng2>~{{ ngContent }}~</ng2>{{ \']\' }}</div>');
|
2016-10-19 16:41:04 -04:00
|
|
|
|
|
|
|
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
2017-01-16 16:52:42 -05:00
|
|
|
expect(document.body.textContent).toEqual('ng1[NG2(~ng1-content~)]');
|
2016-10-19 16:41:04 -04:00
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
2017-02-03 05:11:16 -05:00
|
|
|
it('should correctly project structural directives', async(() => {
|
|
|
|
@Component({selector: 'ng2', template: 'ng2-{{ itemId }}(<ng-content></ng-content>)'})
|
|
|
|
class Ng2Component {
|
|
|
|
@Input() itemId: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
@NgModule({
|
|
|
|
imports: [BrowserModule, UpgradeModule],
|
|
|
|
declarations: [Ng2Component],
|
|
|
|
entryComponents: [Ng2Component]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
ngDoBootstrap() {}
|
|
|
|
}
|
|
|
|
|
2017-03-14 17:55:37 -04:00
|
|
|
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]}
|
|
|
|
];
|
|
|
|
});
|
2017-02-03 05:11:16 -05:00
|
|
|
|
|
|
|
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 )');
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
2016-10-19 16:41:04 -04:00
|
|
|
it('should instantiate ng1 in ng2 template and project content', async(() => {
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
selector: 'ng2',
|
2017-01-16 16:52:42 -05:00
|
|
|
template: `{{ 'ng2(' }}<ng1>{{ transclude }}</ng1>{{ ')' }}`,
|
2016-10-19 16:41:04 -04:00
|
|
|
})
|
|
|
|
class Ng2Component {
|
2017-01-16 16:52:42 -05:00
|
|
|
prop = 'ng2';
|
|
|
|
transclude = 'ng2-transclude';
|
2016-10-19 16:41:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@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() {}
|
|
|
|
}
|
|
|
|
|
2017-01-16 16:52:42 -05:00
|
|
|
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';
|
|
|
|
});
|
2016-10-19 16:41:04 -04:00
|
|
|
|
2017-01-16 16:52:42 -05:00
|
|
|
const element = html('<div>{{ \'ng1(\' }}<ng2></ng2>{{ \')\' }}</div>');
|
2016-10-19 16:41:04 -04:00
|
|
|
|
|
|
|
bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => {
|
2017-01-16 16:52:42 -05:00
|
|
|
expect(document.body.textContent).toEqual('ng1(ng2(ng1(ng2-transclude)))');
|
2016-10-19 16:41:04 -04:00
|
|
|
});
|
|
|
|
}));
|
2017-03-13 20:34:53 -04:00
|
|
|
|
|
|
|
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() {}
|
|
|
|
}
|
|
|
|
|
|
|
|
@NgModule({
|
|
|
|
declarations: [Ng2Component],
|
|
|
|
entryComponents: [Ng2Component],
|
|
|
|
imports: [BrowserModule, UpgradeModule]
|
|
|
|
})
|
|
|
|
class Ng2Module {
|
|
|
|
ngDoBootstrap() {}
|
|
|
|
}
|
|
|
|
|
|
|
|
const ng1Module = angular.module('ng1', []).directive(
|
2017-03-14 17:55:37 -04:00
|
|
|
'ng2', downgradeComponent({component: Ng2Component}));
|
2017-03-13 20:34:53 -04:00
|
|
|
|
|
|
|
// 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)');
|
|
|
|
});
|
|
|
|
}));
|
2016-10-19 16:41:04 -04:00
|
|
|
});
|
|
|
|
}
|