X Tutup
Skip to content

Commit d896e43

Browse files
committed
feat(ngUpgrade): add support for upgrade/downgrade of injectables
Closes #4766
1 parent 486c1ed commit d896e43

File tree

2 files changed

+161
-9
lines changed

2 files changed

+161
-9
lines changed

modules/upgrade/src/upgrade_adapter.ts

Lines changed: 126 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ import {
77
ApplicationRef,
88
AppViewManager,
99
Compiler,
10+
Inject,
1011
Injector,
1112
NgZone,
1213
PlatformRef,
1314
ProtoViewRef,
15+
Provider,
1416
Type
1517
} from 'angular2/angular2';
1618
import {applicationDomBindings} from 'angular2/src/core/application_common';
@@ -113,6 +115,8 @@ export class UpgradeAdapter {
113115
private upgradedComponents: Type[] = [];
114116
/* @internal */
115117
private downgradedComponents: {[name: string]: UpgradeNg1ComponentAdapterBuilder} = {};
118+
/* @internal */
119+
private providers: Array<Type | Provider | any[]> = [];
116120

117121
/**
118122
* Allows Angular v2 Component to be used from AngularJS v1.
@@ -298,7 +302,8 @@ export class UpgradeAdapter {
298302
applicationDomBindings(),
299303
compilerProviders(),
300304
provide(NG1_INJECTOR, {useFactory: () => ng1Injector}),
301-
provide(NG1_COMPILE, {useFactory: () => ng1Injector.get(NG1_COMPILE)})
305+
provide(NG1_COMPILE, {useFactory: () => ng1Injector.get(NG1_COMPILE)}),
306+
this.providers
302307
]);
303308
var injector: Injector = applicationRef.injector;
304309
var ngZone: NgZone = injector.get(NgZone);
@@ -349,16 +354,125 @@ export class UpgradeAdapter {
349354
Promise.all([this.compileNg2Components(compiler, protoViewRefMap), ng1compilePromise])
350355
.then(() => {
351356
ngZone.run(() => {
352-
rootScopePrototype.$apply = original$applyFn; // restore original $apply
353-
while (delayApplyExps.length) {
354-
rootScope.$apply(delayApplyExps.shift());
357+
if (rootScopePrototype) {
358+
rootScopePrototype.$apply = original$applyFn; // restore original $apply
359+
while (delayApplyExps.length) {
360+
rootScope.$apply(delayApplyExps.shift());
361+
}
362+
(<any>upgrade)._bootstrapDone(applicationRef, ng1Injector);
363+
rootScopePrototype = null;
355364
}
356-
(<any>upgrade)._bootstrapDone(applicationRef, ng1Injector);
357365
});
358366
}, onError);
359367
return upgrade;
360368
}
361369

370+
/**
371+
* Adds a provider to the top level environment of a hybrid AngularJS v1 / Angular v2 application.
372+
*
373+
* In hybrid AngularJS v1 / Angular v2 application, there is no one root Angular v2 component,
374+
* for this reason we provide an application global way of registering providers which is
375+
* consistent with single global injection in AngularJS v1.
376+
*
377+
* ## Example
378+
*
379+
* ```
380+
* class Greeter {
381+
* greet(name) {
382+
* alert('Hello ' + name + '!');
383+
* }
384+
* }
385+
*
386+
* @Component({
387+
* selector: 'app',
388+
* template: ''
389+
* })
390+
* class App {
391+
* constructor(greeter: Greeter) {
392+
* this.greeter('World');
393+
* }
394+
* }
395+
*
396+
* var adapter = new UpgradeAdapter();
397+
* adapter.addProvider(Greeter);
398+
*
399+
* var module = angular.module('myExample', []);
400+
* module.directive('app', adapter.downgradeNg2Component(App));
401+
*
402+
* document.body.innerHTML = '<app></app>'
403+
* adapter.bootstrap(document.body, ['myExample']);
404+
*```
405+
*/
406+
public addProvider(provider: Type | Provider | any[]): void { this.providers.push(provider); }
407+
408+
/**
409+
* Allows AngularJS v1 service to be accessible from Angular v2.
410+
*
411+
*
412+
* ## Example
413+
*
414+
* ```
415+
* class Login { ... }
416+
* class Server { ... }
417+
*
418+
* @Injectable()
419+
* class Example {
420+
* constructor(@Inject('server') server, login: Login) {
421+
* ...
422+
* }
423+
* }
424+
*
425+
* var module = angular.module('myExample', []);
426+
* module.service('server', Server);
427+
* module.service('login', Login);
428+
*
429+
* var adapter = new UpgradeAdapter();
430+
* adapter.upgradeNg1Provider('server');
431+
* adapter.upgradeNg1Provider('login', {asToken: Login});
432+
* adapter.addProvider(Example);
433+
*
434+
* adapter.bootstrap(document.body, ['myExample']).ready((ref) => {
435+
* var example: Example = ref.ng2Injector.get(Example);
436+
* });
437+
*
438+
* ```
439+
*/
440+
public upgradeNg1Provider(name: string, options?: {asToken: any}) {
441+
var token = options && options.asToken || name;
442+
this.providers.push(provide(token, {
443+
useFactory: (ng1Injector: angular.auto.IInjectorService) => ng1Injector.get(name),
444+
deps: [NG1_INJECTOR]
445+
}));
446+
}
447+
448+
/**
449+
* Allows Angular v2 service to be accessible from AngularJS v1.
450+
*
451+
*
452+
* ## Example
453+
*
454+
* ```
455+
* class Example {
456+
* }
457+
*
458+
* var adapter = new UpgradeAdapter();
459+
* adapter.addProvider(Example);
460+
*
461+
* var module = angular.module('myExample', []);
462+
* module.factory('example', adapter.downgradeNg2Provider(Example));
463+
*
464+
* adapter.bootstrap(document.body, ['myExample']).ready((ref) => {
465+
* var example: Example = ref.ng1Injector.get('example');
466+
* });
467+
*
468+
* ```
469+
*/
470+
public downgradeNg2Provider(token: any): Function {
471+
var factory = function(injector: Injector) { return injector.get(token); };
472+
factory.$inject = [NG2_INJECTOR];
473+
return factory;
474+
}
475+
362476
/* @internal */
363477
private compileNg2Components(compiler: Compiler,
364478
protoViewRefMap: ProtoViewRefMap): Promise<ProtoViewRefMap> {
@@ -417,14 +531,18 @@ export class UpgradeAdapterRef {
417531
/* @internal */
418532
private _readyFn: (upgradeAdapterRef?: UpgradeAdapterRef) => void = null;
419533

420-
public applicationRef: ApplicationRef = null;
534+
public ng1RootScope: angular.IRootScopeService = null;
421535
public ng1Injector: angular.auto.IInjectorService = null;
536+
public ng2ApplicationRef: ApplicationRef = null;
537+
public ng2Injector: Injector = null;
422538

423539
/* @internal */
424540
private _bootstrapDone(applicationRef: ApplicationRef,
425541
ng1Injector: angular.auto.IInjectorService) {
426-
this.applicationRef = applicationRef;
542+
this.ng2ApplicationRef = applicationRef;
543+
this.ng2Injector = applicationRef.injector;
427544
this.ng1Injector = ng1Injector;
545+
this.ng1RootScope = ng1Injector.get(NG1_ROOT_SCOPE);
428546
this._readyFn && this._readyFn(this);
429547
}
430548

@@ -442,6 +560,6 @@ export class UpgradeAdapterRef {
442560
*/
443561
public dispose() {
444562
this.ng1Injector.get(NG1_ROOT_SCOPE).$destroy();
445-
this.applicationRef.dispose();
563+
this.ng2ApplicationRef.dispose();
446564
}
447565
}

modules/upgrade/test/integration_spec.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
xit,
1212
} from 'angular2/testing_internal';
1313

14-
import {Component, Class, Inject, EventEmitter} from 'angular2/angular2';
14+
import {Component, Class, Inject, EventEmitter, ApplicationRef, provide} from 'angular2/angular2';
1515
import {UpgradeAdapter} from 'upgrade/upgrade';
1616

1717
export function main() {
@@ -468,6 +468,40 @@ export function main() {
468468

469469
});
470470

471+
describe('injection', () => {
472+
function SomeToken() {}
473+
474+
it('should export ng2 instance to ng1', inject([AsyncTestCompleter], (async) => {
475+
var adapter = new UpgradeAdapter();
476+
var module = angular.module('myExample', []);
477+
adapter.addProvider(provide(SomeToken, {useValue: 'correct_value'}));
478+
module.factory('someToken', adapter.downgradeNg2Provider(SomeToken));
479+
adapter.bootstrap(html('<div>'), ['myExample'])
480+
.ready((ref) => {
481+
expect(ref.ng1Injector.get('someToken')).toBe('correct_value');
482+
ref.dispose();
483+
async.done();
484+
});
485+
}));
486+
487+
it('should export ng1 instance to ng2', inject([AsyncTestCompleter], (async) => {
488+
var adapter = new UpgradeAdapter();
489+
var module = angular.module('myExample', []);
490+
module.value('testValue', 'secreteToken');
491+
adapter.upgradeNg1Provider('testValue');
492+
adapter.upgradeNg1Provider('testValue', {asToken: 'testToken'});
493+
adapter.upgradeNg1Provider('testValue', {asToken: String});
494+
adapter.bootstrap(html('<div>'), ['myExample'])
495+
.ready((ref) => {
496+
expect(ref.ng2Injector.get('testValue')).toBe('secreteToken');
497+
expect(ref.ng2Injector.get(String)).toBe('secreteToken');
498+
expect(ref.ng2Injector.get('testToken')).toBe('secreteToken');
499+
ref.dispose();
500+
async.done();
501+
});
502+
}));
503+
});
504+
471505
describe('examples', () => {
472506
it('should verify UpgradeAdapter example', inject([AsyncTestCompleter], (async) => {
473507
var adapter = new UpgradeAdapter();

0 commit comments

Comments
 (0)
X Tutup