X Tutup
Skip to content

Commit 053b7a5

Browse files
committed
feat(ngUpgrade): faster ng2->ng1 adapter by only compiling ng1 once
The adapter only compiles ng1 template. This means that we need to reimplement / emulate all of the ng1’s API on the HOST element. interface IDirective { compile?: IDirectiveCompileFn; // NOT SUPPORTED controller?: any; // IMPLEMENTED controllerAs?: string; // IMPLEMENTED bindToController?: boolean|Object; // IMPLEMENTED link?: IDirectiveLinkFn | IDirectivePrePost; // IMPLEMENTED (pre-link only) name?: string; // N/A priority?: number; // NOT SUPPORTED replace?: boolean; // NOT SUPPORTED require?: any; // IMPLEMENTED restrict?: string; // WORKING scope?: any; // IMPLEMENTED template?: any; // IMPLEMENTED templateUrl?: any; // IMPLEMENTED terminal?: boolean; // NOT SUPPORTED transclude?: any; // IMPLEMENTED }
1 parent 059e8fa commit 053b7a5

File tree

7 files changed

+475
-68
lines changed

7 files changed

+475
-68
lines changed

modules/upgrade/src/angular.d.ts

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ declare namespace angular {
77
run(a: any);
88
}
99
interface ICompileService {
10-
(element: Element, transclude?: Function): ILinkFn;
10+
(element: Element | NodeList | string, transclude?: Function): ILinkFn;
1111
}
1212
interface ILinkFn {
1313
(scope: IScope, cloneAttachFn?: Function, options?: ILinkFnOptions): void
@@ -17,7 +17,8 @@ declare namespace angular {
1717
futureParentElement?: Node
1818
}
1919
interface IRootScopeService {
20-
$new(): IScope;
20+
$new(isolate?: boolean): IScope;
21+
$id: string;
2122
$watch(expr: any, fn?: (a1?: any, a2?: any) => void);
2223
$apply(): any;
2324
$apply(exp: string): any;
@@ -29,19 +30,53 @@ declare namespace angular {
2930
interface IScope extends IRootScopeService {}
3031
interface IAngularBootstrapConfig {}
3132
interface IDirective {
32-
require?: string;
33+
compile?: IDirectiveCompileFn;
34+
controller?: any;
35+
controllerAs?: string;
36+
bindToController?: boolean | Object;
37+
link?: IDirectiveLinkFn | IDirectivePrePost;
38+
name?: string;
39+
priority?: number;
40+
replace?: boolean;
41+
require?: any;
3342
restrict?: string;
34-
scope?: {[key: string]: string};
35-
link?: {pre?: Function, post?: Function};
43+
scope?: any;
44+
template?: any;
45+
templateUrl?: any;
46+
terminal?: boolean;
47+
transclude?: any;
48+
}
49+
interface IDirectiveCompileFn {
50+
(templateElement: IAugmentedJQuery, templateAttributes: IAttributes,
51+
transclude: ITranscludeFunction): IDirectivePrePost;
52+
}
53+
interface IDirectivePrePost {
54+
pre?: IDirectiveLinkFn;
55+
post?: IDirectiveLinkFn;
56+
}
57+
interface IDirectiveLinkFn {
58+
(scope: IScope, instanceElement: IAugmentedJQuery, instanceAttributes: IAttributes,
59+
controller: any, transclude: ITranscludeFunction): void;
3660
}
3761
interface IAttributes {
3862
$observe(attr: string, fn: (v: string) => void);
3963
}
40-
interface ITranscludeFunction {}
64+
interface ITranscludeFunction {
65+
// If the scope is provided, then the cloneAttachFn must be as well.
66+
(scope: IScope, cloneAttachFn: ICloneAttachFunction): IAugmentedJQuery;
67+
// If one argument is provided, then it's assumed to be the cloneAttachFn.
68+
(cloneAttachFn?: ICloneAttachFunction): IAugmentedJQuery;
69+
}
70+
interface ICloneAttachFunction {
71+
// Let's hint but not force cloneAttachFn's signature
72+
(clonedElement?: IAugmentedJQuery, scope?: IScope): any;
73+
}
4174
interface IAugmentedJQuery {
4275
bind(name: string, fn: () => void);
4376
data(name: string, value?: any);
77+
inheritedData(name: string, value?: any);
4478
contents(): IAugmentedJQuery;
79+
parent(): IAugmentedJQuery;
4580
length: number;
4681
[index: number]: Node;
4782
}
@@ -53,6 +88,19 @@ declare namespace angular {
5388
}
5489
function element(e: Element): IAugmentedJQuery;
5590
function bootstrap(e: Element, modules: string[], config: IAngularBootstrapConfig);
91+
interface IHttpBackendService {
92+
(method: string, url: string, post?: any, callback?: Function, headers?: any, timeout?: number,
93+
withCredentials?: boolean): void;
94+
}
95+
interface ICacheObject {
96+
put<T>(key: string, value?: T): T;
97+
get(key: string): any;
98+
}
99+
interface ITemplateCacheService extends ICacheObject {}
100+
interface IControllerService {
101+
(controllerConstructor: Function, locals?: any, later?: any, ident?: any): any;
102+
(controllerName: string, locals?: any): any;
103+
}
56104

57105
namespace auto {
58106
interface IInjectorService {

modules/upgrade/src/constants.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ export const NG2_INJECTOR = 'ng2.Injector';
44
export const NG2_PROTO_VIEW_REF_MAP = 'ng2.ProtoViewRefMap';
55
export const NG2_ZONE = 'ng2.NgZone';
66

7-
export const NG1_REQUIRE_INJECTOR_REF = '$' + NG2_INJECTOR + 'Controller';
7+
export const NG1_CONTROLLER = '$controller';
88
export const NG1_SCOPE = '$scope';
99
export const NG1_ROOT_SCOPE = '$rootScope';
1010
export const NG1_COMPILE = '$compile';
11+
export const NG1_HTTP_BACKEND = '$httpBackend';
1112
export const NG1_INJECTOR = '$injector';
1213
export const NG1_PARSE = '$parse';
14+
export const NG1_TEMPLATE_CACHE = '$templateCache';
1315
export const REQUIRE_INJECTOR = '^' + NG2_INJECTOR;

modules/upgrade/src/downgrade_ng2_adapter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export class DowngradeNg2ComponentAdapter {
101101
this.component.onChanges(inputChanges);
102102
});
103103
}
104-
this.componentScope.$watch(() => this.changeDetector.detectChanges());
104+
this.componentScope.$watch(() => this.changeDetector && this.changeDetector.detectChanges());
105105
}
106106

107107
projectContent() {

modules/upgrade/src/upgrade_adapter.ts

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,12 @@ import {applicationCommonBindings} from 'angular2/src/core/application_ref';
1818
import {compilerProviders} from 'angular2/src/core/compiler/compiler';
1919

2020
import {getComponentInfo, ComponentInfo} from './metadata';
21-
import {onError} from './util';
21+
import {onError, controllerKey} from './util';
2222
import {
2323
NG1_COMPILE,
2424
NG1_INJECTOR,
2525
NG1_PARSE,
2626
NG1_ROOT_SCOPE,
27-
NG1_REQUIRE_INJECTOR_REF,
2827
NG1_SCOPE,
2928
NG2_APP_VIEW_MANAGER,
3029
NG2_COMPILER,
@@ -70,8 +69,10 @@ var upgradeCount: number = 0;
7069
* 7. Whenever an adapter component is instantiated the host element is owned by the the framework
7170
* doing the instantiation. The other framework then instantiates and owns the view for that
7271
* component. This implies that component bindings will always follow the semantics of the
73-
* instantiation framework, but with Angular v2 syntax.
72+
* instantiation framework. The syntax is always that of Angular v2 syntax.
7473
* 8. AngularJS v1 is always bootstrapped first and owns the bottom most view.
74+
* 9. The new application is running in Angular v2 zone, and therefore it no longer needs calls to
75+
* `$apply()`.
7576
*
7677
* ## Example
7778
*
@@ -81,7 +82,7 @@ var upgradeCount: number = 0;
8182
*
8283
* module.directive('ng1', function() {
8384
* return {
84-
* scope: { title: '@' },
85+
* scope: { title: '=' },
8586
* template: 'ng1[Hello {{title}}!](<span ng-transclude></span>)'
8687
* };
8788
* });
@@ -127,8 +128,8 @@ export class UpgradeAdapter {
127128
}
128129

129130
bootstrap(element: Element, modules?: any[],
130-
config?: angular.IAngularBootstrapConfig): UpgradeRef {
131-
var upgrade = new UpgradeRef();
131+
config?: angular.IAngularBootstrapConfig): UpgradeAdapterRef {
132+
var upgrade = new UpgradeAdapterRef();
132133
var ng1Injector: angular.auto.IInjectorService = null;
133134
var platformRef: PlatformRef = platform();
134135
var applicationRef: ApplicationRef = platformRef.application([
@@ -147,6 +148,7 @@ export class UpgradeAdapter {
147148
var rootScope: angular.IRootScopeService;
148149
var protoViewRefMap: ProtoViewRefMap = {};
149150
var ng1Module = angular.module(this.idPrefix, modules);
151+
var ng1compilePromise: Promise<any> = null;
150152
ng1Module.value(NG2_INJECTOR, injector)
151153
.value(NG2_ZONE, ngZone)
152154
.value(NG2_COMPILER, compiler)
@@ -176,22 +178,23 @@ export class UpgradeAdapter {
176178
(injector: angular.auto.IInjectorService, rootScope: angular.IRootScopeService) => {
177179
ng1Injector = injector;
178180
ngZone.overrideOnTurnDone(() => rootScope.$apply());
179-
UpgradeNg1ComponentAdapterBuilder.resolve(this.downgradedComponents, injector);
181+
ng1compilePromise =
182+
UpgradeNg1ComponentAdapterBuilder.resolve(this.downgradedComponents, injector);
180183
}
181184
]);
182185

183-
angular.element(element).data(NG1_REQUIRE_INJECTOR_REF, injector);
186+
angular.element(element).data(controllerKey(NG2_INJECTOR), injector);
184187
ngZone.run(() => { angular.bootstrap(element, [this.idPrefix], config); });
185-
this.compileNg2Components(compiler, protoViewRefMap)
186-
.then((protoViewRefMap: ProtoViewRefMap) => {
188+
Promise.all([this.compileNg2Components(compiler, protoViewRefMap), ng1compilePromise])
189+
.then(() => {
187190
ngZone.run(() => {
188191
rootScopePrototype.$apply = original$applyFn; // restore original $apply
189192
while (delayApplyExps.length) {
190193
rootScope.$apply(delayApplyExps.shift());
191194
}
192-
upgrade.readyFn && upgrade.readyFn();
195+
(<any>upgrade)._bootstrapDone(applicationRef, ng1Injector);
193196
});
194-
});
197+
}, onError);
195198
return upgrade;
196199
}
197200

@@ -214,7 +217,7 @@ export class UpgradeAdapter {
214217
}
215218

216219
interface ProtoViewRefMap {
217-
[selector: string]: ProtoViewRef
220+
[selector: string]: ProtoViewRef;
218221
}
219222

220223
function ng1ComponentDirective(info: ComponentInfo, idPrefix: string): Function {
@@ -246,8 +249,38 @@ function ng1ComponentDirective(info: ComponentInfo, idPrefix: string): Function
246249
return directiveFactory;
247250
}
248251

249-
export class UpgradeRef {
250-
readyFn: Function;
252+
/**
253+
* Use `UgradeAdapterRef` to control a hybrid AngularJS v1 / Angular v2 application.
254+
*/
255+
export class UpgradeAdapterRef {
256+
/* @internal */
257+
private _readyFn: (upgradeAdapterRef?: UpgradeAdapterRef) => void = null;
251258

252-
ready(fn: Function) { this.readyFn = fn; }
259+
public applicationRef: ApplicationRef = null;
260+
public ng1Injector: angular.auto.IInjectorService = null;
261+
262+
/* @internal */
263+
private _bootstrapDone(applicationRef: ApplicationRef,
264+
ng1Injector: angular.auto.IInjectorService) {
265+
this.applicationRef = applicationRef;
266+
this.ng1Injector = ng1Injector;
267+
this._readyFn && this._readyFn(this);
268+
}
269+
270+
/**
271+
* Register a callback function which is notified upon successful hybrid AngularJS v1 / Angular v2
272+
* application has been bootstrapped.
273+
*
274+
* The `ready` callback function is invoked inside the Angular v2 zone, therefore it does not
275+
* require a call to `$apply()`.
276+
*/
277+
public ready(fn: (upgradeAdapterRef?: UpgradeAdapterRef) => void) { this._readyFn = fn; }
278+
279+
/**
280+
* Dispose of running hybrid AngularJS v1 / Angular v2 application.
281+
*/
282+
public dispose() {
283+
this.ng1Injector.get(NG1_ROOT_SCOPE).$destroy();
284+
this.applicationRef.dispose();
285+
}
253286
}

0 commit comments

Comments
 (0)
X Tutup