feat(ngUpgrade): add support for upgrade/downgrade of injectables
Closes #4766
This commit is contained in:
parent
486c1eda8e
commit
d896e4350a
|
@ -7,10 +7,12 @@ import {
|
||||||
ApplicationRef,
|
ApplicationRef,
|
||||||
AppViewManager,
|
AppViewManager,
|
||||||
Compiler,
|
Compiler,
|
||||||
|
Inject,
|
||||||
Injector,
|
Injector,
|
||||||
NgZone,
|
NgZone,
|
||||||
PlatformRef,
|
PlatformRef,
|
||||||
ProtoViewRef,
|
ProtoViewRef,
|
||||||
|
Provider,
|
||||||
Type
|
Type
|
||||||
} from 'angular2/angular2';
|
} from 'angular2/angular2';
|
||||||
import {applicationDomBindings} from 'angular2/src/core/application_common';
|
import {applicationDomBindings} from 'angular2/src/core/application_common';
|
||||||
|
@ -113,6 +115,8 @@ export class UpgradeAdapter {
|
||||||
private upgradedComponents: Type[] = [];
|
private upgradedComponents: Type[] = [];
|
||||||
/* @internal */
|
/* @internal */
|
||||||
private downgradedComponents: {[name: string]: UpgradeNg1ComponentAdapterBuilder} = {};
|
private downgradedComponents: {[name: string]: UpgradeNg1ComponentAdapterBuilder} = {};
|
||||||
|
/* @internal */
|
||||||
|
private providers: Array<Type | Provider | any[]> = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows Angular v2 Component to be used from AngularJS v1.
|
* Allows Angular v2 Component to be used from AngularJS v1.
|
||||||
|
@ -298,7 +302,8 @@ export class UpgradeAdapter {
|
||||||
applicationDomBindings(),
|
applicationDomBindings(),
|
||||||
compilerProviders(),
|
compilerProviders(),
|
||||||
provide(NG1_INJECTOR, {useFactory: () => ng1Injector}),
|
provide(NG1_INJECTOR, {useFactory: () => ng1Injector}),
|
||||||
provide(NG1_COMPILE, {useFactory: () => ng1Injector.get(NG1_COMPILE)})
|
provide(NG1_COMPILE, {useFactory: () => ng1Injector.get(NG1_COMPILE)}),
|
||||||
|
this.providers
|
||||||
]);
|
]);
|
||||||
var injector: Injector = applicationRef.injector;
|
var injector: Injector = applicationRef.injector;
|
||||||
var ngZone: NgZone = injector.get(NgZone);
|
var ngZone: NgZone = injector.get(NgZone);
|
||||||
|
@ -349,16 +354,125 @@ export class UpgradeAdapter {
|
||||||
Promise.all([this.compileNg2Components(compiler, protoViewRefMap), ng1compilePromise])
|
Promise.all([this.compileNg2Components(compiler, protoViewRefMap), ng1compilePromise])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
ngZone.run(() => {
|
ngZone.run(() => {
|
||||||
rootScopePrototype.$apply = original$applyFn; // restore original $apply
|
if (rootScopePrototype) {
|
||||||
while (delayApplyExps.length) {
|
rootScopePrototype.$apply = original$applyFn; // restore original $apply
|
||||||
rootScope.$apply(delayApplyExps.shift());
|
while (delayApplyExps.length) {
|
||||||
|
rootScope.$apply(delayApplyExps.shift());
|
||||||
|
}
|
||||||
|
(<any>upgrade)._bootstrapDone(applicationRef, ng1Injector);
|
||||||
|
rootScopePrototype = null;
|
||||||
}
|
}
|
||||||
(<any>upgrade)._bootstrapDone(applicationRef, ng1Injector);
|
|
||||||
});
|
});
|
||||||
}, onError);
|
}, onError);
|
||||||
return upgrade;
|
return upgrade;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a provider to the top level environment of a hybrid AngularJS v1 / Angular v2 application.
|
||||||
|
*
|
||||||
|
* In hybrid AngularJS v1 / Angular v2 application, there is no one root Angular v2 component,
|
||||||
|
* for this reason we provide an application global way of registering providers which is
|
||||||
|
* consistent with single global injection in AngularJS v1.
|
||||||
|
*
|
||||||
|
* ## Example
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* class Greeter {
|
||||||
|
* greet(name) {
|
||||||
|
* alert('Hello ' + name + '!');
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Component({
|
||||||
|
* selector: 'app',
|
||||||
|
* template: ''
|
||||||
|
* })
|
||||||
|
* class App {
|
||||||
|
* constructor(greeter: Greeter) {
|
||||||
|
* this.greeter('World');
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* var adapter = new UpgradeAdapter();
|
||||||
|
* adapter.addProvider(Greeter);
|
||||||
|
*
|
||||||
|
* var module = angular.module('myExample', []);
|
||||||
|
* module.directive('app', adapter.downgradeNg2Component(App));
|
||||||
|
*
|
||||||
|
* document.body.innerHTML = '<app></app>'
|
||||||
|
* adapter.bootstrap(document.body, ['myExample']);
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
public addProvider(provider: Type | Provider | any[]): void { this.providers.push(provider); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows AngularJS v1 service to be accessible from Angular v2.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ## Example
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* class Login { ... }
|
||||||
|
* class Server { ... }
|
||||||
|
*
|
||||||
|
* @Injectable()
|
||||||
|
* class Example {
|
||||||
|
* constructor(@Inject('server') server, login: Login) {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* var module = angular.module('myExample', []);
|
||||||
|
* module.service('server', Server);
|
||||||
|
* module.service('login', Login);
|
||||||
|
*
|
||||||
|
* var adapter = new UpgradeAdapter();
|
||||||
|
* adapter.upgradeNg1Provider('server');
|
||||||
|
* adapter.upgradeNg1Provider('login', {asToken: Login});
|
||||||
|
* adapter.addProvider(Example);
|
||||||
|
*
|
||||||
|
* adapter.bootstrap(document.body, ['myExample']).ready((ref) => {
|
||||||
|
* var example: Example = ref.ng2Injector.get(Example);
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
public upgradeNg1Provider(name: string, options?: {asToken: any}) {
|
||||||
|
var token = options && options.asToken || name;
|
||||||
|
this.providers.push(provide(token, {
|
||||||
|
useFactory: (ng1Injector: angular.auto.IInjectorService) => ng1Injector.get(name),
|
||||||
|
deps: [NG1_INJECTOR]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows Angular v2 service to be accessible from AngularJS v1.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ## Example
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* class Example {
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* var adapter = new UpgradeAdapter();
|
||||||
|
* adapter.addProvider(Example);
|
||||||
|
*
|
||||||
|
* var module = angular.module('myExample', []);
|
||||||
|
* module.factory('example', adapter.downgradeNg2Provider(Example));
|
||||||
|
*
|
||||||
|
* adapter.bootstrap(document.body, ['myExample']).ready((ref) => {
|
||||||
|
* var example: Example = ref.ng1Injector.get('example');
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
public downgradeNg2Provider(token: any): Function {
|
||||||
|
var factory = function(injector: Injector) { return injector.get(token); };
|
||||||
|
factory.$inject = [NG2_INJECTOR];
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
/* @internal */
|
/* @internal */
|
||||||
private compileNg2Components(compiler: Compiler,
|
private compileNg2Components(compiler: Compiler,
|
||||||
protoViewRefMap: ProtoViewRefMap): Promise<ProtoViewRefMap> {
|
protoViewRefMap: ProtoViewRefMap): Promise<ProtoViewRefMap> {
|
||||||
|
@ -417,14 +531,18 @@ export class UpgradeAdapterRef {
|
||||||
/* @internal */
|
/* @internal */
|
||||||
private _readyFn: (upgradeAdapterRef?: UpgradeAdapterRef) => void = null;
|
private _readyFn: (upgradeAdapterRef?: UpgradeAdapterRef) => void = null;
|
||||||
|
|
||||||
public applicationRef: ApplicationRef = null;
|
public ng1RootScope: angular.IRootScopeService = null;
|
||||||
public ng1Injector: angular.auto.IInjectorService = null;
|
public ng1Injector: angular.auto.IInjectorService = null;
|
||||||
|
public ng2ApplicationRef: ApplicationRef = null;
|
||||||
|
public ng2Injector: Injector = null;
|
||||||
|
|
||||||
/* @internal */
|
/* @internal */
|
||||||
private _bootstrapDone(applicationRef: ApplicationRef,
|
private _bootstrapDone(applicationRef: ApplicationRef,
|
||||||
ng1Injector: angular.auto.IInjectorService) {
|
ng1Injector: angular.auto.IInjectorService) {
|
||||||
this.applicationRef = applicationRef;
|
this.ng2ApplicationRef = applicationRef;
|
||||||
|
this.ng2Injector = applicationRef.injector;
|
||||||
this.ng1Injector = ng1Injector;
|
this.ng1Injector = ng1Injector;
|
||||||
|
this.ng1RootScope = ng1Injector.get(NG1_ROOT_SCOPE);
|
||||||
this._readyFn && this._readyFn(this);
|
this._readyFn && this._readyFn(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,6 +560,6 @@ export class UpgradeAdapterRef {
|
||||||
*/
|
*/
|
||||||
public dispose() {
|
public dispose() {
|
||||||
this.ng1Injector.get(NG1_ROOT_SCOPE).$destroy();
|
this.ng1Injector.get(NG1_ROOT_SCOPE).$destroy();
|
||||||
this.applicationRef.dispose();
|
this.ng2ApplicationRef.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
xit,
|
xit,
|
||||||
} from 'angular2/testing_internal';
|
} from 'angular2/testing_internal';
|
||||||
|
|
||||||
import {Component, Class, Inject, EventEmitter} from 'angular2/angular2';
|
import {Component, Class, Inject, EventEmitter, ApplicationRef, provide} from 'angular2/angular2';
|
||||||
import {UpgradeAdapter} from 'upgrade/upgrade';
|
import {UpgradeAdapter} from 'upgrade/upgrade';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
|
@ -468,6 +468,40 @@ export function main() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('injection', () => {
|
||||||
|
function SomeToken() {}
|
||||||
|
|
||||||
|
it('should export ng2 instance to ng1', inject([AsyncTestCompleter], (async) => {
|
||||||
|
var adapter = new UpgradeAdapter();
|
||||||
|
var module = angular.module('myExample', []);
|
||||||
|
adapter.addProvider(provide(SomeToken, {useValue: 'correct_value'}));
|
||||||
|
module.factory('someToken', adapter.downgradeNg2Provider(SomeToken));
|
||||||
|
adapter.bootstrap(html('<div>'), ['myExample'])
|
||||||
|
.ready((ref) => {
|
||||||
|
expect(ref.ng1Injector.get('someToken')).toBe('correct_value');
|
||||||
|
ref.dispose();
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should export ng1 instance to ng2', inject([AsyncTestCompleter], (async) => {
|
||||||
|
var adapter = new UpgradeAdapter();
|
||||||
|
var module = angular.module('myExample', []);
|
||||||
|
module.value('testValue', 'secreteToken');
|
||||||
|
adapter.upgradeNg1Provider('testValue');
|
||||||
|
adapter.upgradeNg1Provider('testValue', {asToken: 'testToken'});
|
||||||
|
adapter.upgradeNg1Provider('testValue', {asToken: String});
|
||||||
|
adapter.bootstrap(html('<div>'), ['myExample'])
|
||||||
|
.ready((ref) => {
|
||||||
|
expect(ref.ng2Injector.get('testValue')).toBe('secreteToken');
|
||||||
|
expect(ref.ng2Injector.get(String)).toBe('secreteToken');
|
||||||
|
expect(ref.ng2Injector.get('testToken')).toBe('secreteToken');
|
||||||
|
ref.dispose();
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
describe('examples', () => {
|
describe('examples', () => {
|
||||||
it('should verify UpgradeAdapter example', inject([AsyncTestCompleter], (async) => {
|
it('should verify UpgradeAdapter example', inject([AsyncTestCompleter], (async) => {
|
||||||
var adapter = new UpgradeAdapter();
|
var adapter = new UpgradeAdapter();
|
||||||
|
|
Loading…
Reference in New Issue