X Tutup
Skip to content

Commit cd90e6e

Browse files
committed
feat(ngUpgrade): support for content project from ng1->ng2
1 parent 867c08a commit cd90e6e

File tree

9 files changed

+93
-28
lines changed

9 files changed

+93
-28
lines changed

modules/angular2/src/core/render/dom/dom_renderer.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ export abstract class DomRenderer extends Renderer implements NodeFactory<Node>
5050

5151
abstract destroyView(viewRef: RenderViewRef);
5252

53+
abstract createRootContentInsertionPoint();
54+
5355
getNativeElementSync(location: RenderElementRef): any {
5456
return resolveInternalDomView(location.renderView).boundElements[location.boundElementIndex];
5557
}
@@ -282,6 +284,9 @@ export class DomRenderer_ extends DomRenderer {
282284
DOM.setAttribute(node, attrNameAndValues[attrIdx], attrNameAndValues[attrIdx + 1]);
283285
}
284286
}
287+
createRootContentInsertionPoint(): Node {
288+
return DOM.createComment('root-content-insertion-point');
289+
}
285290
createShadowRoot(host: Node, templateId: number): Node {
286291
var sr = DOM.createShadowRoot(host);
287292
var styles = this._nativeShadowStyles.get(templateId);

modules/angular2/src/core/render/view.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export class DefaultRenderView<N> extends RenderViewRef {
2525

2626
constructor(public fragments: DefaultRenderFragmentRef<N>[], public boundTextNodes: N[],
2727
public boundElements: N[], public nativeShadowRoots: N[],
28-
public globalEventAdders: Function[]) {
28+
public globalEventAdders: Function[], public rootContentInsertionPoints: N[]) {
2929
super();
3030
}
3131

modules/angular2/src/core/render/view_factory.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,16 @@ export function createRenderView(fragmentCmds: RenderTemplateCmd[], inplaceEleme
2323
fragments.push(new DefaultRenderFragmentRef(context.fragments[i]));
2424
}
2525
view = new DefaultRenderView<any>(fragments, context.boundTextNodes, context.boundElements,
26-
context.nativeShadowRoots, context.globalEventAdders);
26+
context.nativeShadowRoots, context.globalEventAdders,
27+
context.rootContentInsertionPoints);
2728
return view;
2829
}
2930

3031
export interface NodeFactory<N> {
3132
resolveComponentTemplate(templateId: number): RenderTemplateCmd[];
3233
createTemplateAnchor(attrNameAndValues: string[]): N;
3334
createElement(name: string, attrNameAndValues: string[]): N;
35+
createRootContentInsertionPoint(): N;
3436
mergeElement(existing: N, attrNameAndValues: string[]);
3537
createShadowRoot(host: N, templateId: number): N;
3638
createText(value: string): N;
@@ -41,14 +43,19 @@ export interface NodeFactory<N> {
4143

4244
class BuildContext<N> {
4345
constructor(private _eventDispatcher: Function, public factory: NodeFactory<N>,
44-
private _inplaceElement: N) {}
46+
private _inplaceElement: N) {
47+
this.isHost = isPresent((_inplaceElement));
48+
}
4549
private _builders: RenderViewBuilder<N>[] = [];
4650

4751
globalEventAdders: Function[] = [];
4852
boundElements: N[] = [];
4953
boundTextNodes: N[] = [];
5054
nativeShadowRoots: N[] = [];
5155
fragments: N[][] = [];
56+
rootContentInsertionPoints: N[] = [];
57+
componentCount: number = 0;
58+
isHost: boolean;
5259

5360
build(fragmentCmds: RenderTemplateCmd[]) {
5461
this.enqueueFragmentBuilder(null, fragmentCmds);
@@ -65,6 +72,7 @@ class BuildContext<N> {
6572
}
6673

6774
enqueueComponentBuilder(component: Component<N>) {
75+
this.componentCount++;
6876
this._builders.push(new RenderViewBuilder<N>(
6977
component, null, this.factory.resolveComponentTemplate(component.cmd.templateId)));
7078
}
@@ -131,10 +139,20 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
131139
}
132140
visitNgContent(cmd: RenderNgContentCmd, context: BuildContext<N>): any {
133141
if (isPresent(this.parentComponent)) {
134-
var projectedNodes = this.parentComponent.project(cmd.index);
135-
for (var i = 0; i < projectedNodes.length; i++) {
136-
var node = projectedNodes[i];
137-
this._addChild(node, cmd.ngContentIndex, context);
142+
if (this.parentComponent.isRoot) {
143+
var insertionPoint = context.factory.createRootContentInsertionPoint();
144+
if (this.parent instanceof Component) {
145+
context.factory.appendChild((<Component<N>>this.parent).shadowRoot, insertionPoint);
146+
} else {
147+
context.factory.appendChild(<N>this.parent, insertionPoint);
148+
}
149+
context.rootContentInsertionPoints.push(insertionPoint);
150+
} else {
151+
var projectedNodes = this.parentComponent.project(cmd.index);
152+
for (var i = 0; i < projectedNodes.length; i++) {
153+
var node = projectedNodes[i];
154+
this._addChild(node, cmd.ngContentIndex, context);
155+
}
138156
}
139157
}
140158
return null;
@@ -154,7 +172,8 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
154172
root = context.factory.createShadowRoot(el, cmd.templateId);
155173
context.nativeShadowRoots.push(root);
156174
}
157-
var component = new Component(el, root, cmd);
175+
var isRoot = context.componentCount === 0 && context.isHost;
176+
var component = new Component(el, root, cmd, isRoot);
158177
context.enqueueComponentBuilder(component);
159178
this.parentStack.push(component);
160179
return null;
@@ -213,7 +232,8 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
213232
class Component<N> {
214233
private contentNodesByNgContentIndex: N[][] = [];
215234

216-
constructor(public hostElement: N, public shadowRoot: N, public cmd: RenderBeginComponentCmd) {}
235+
constructor(public hostElement: N, public shadowRoot: N, public cmd: RenderBeginComponentCmd,
236+
public isRoot: boolean) {}
217237
addContentNode(ngContentIndex: number, node: N, context: BuildContext<N>) {
218238
if (isBlank(ngContentIndex)) {
219239
if (this.cmd.nativeShadow) {

modules/angular2/test/core/render/view_factory_spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,16 @@ export function main() {
517517
expect(stringifyFragment(view.fragments[0].nodes))
518518
.toEqual('<a-comp><b-comp>(hello)</b-comp></a-comp>');
519519
});
520+
521+
522+
it('should store content injection points for root component in a view', () => {
523+
componentTemplates.set(0, [ngContent(0, null)]);
524+
var view =
525+
createRenderView([beginComponent('a-comp', [], [], false, null, 0), endComponent()],
526+
DOM.createElement('root'), nodeFactory);
527+
expect(stringifyFragment(view.rootContentInsertionPoints))
528+
.toEqual('<root-content-insertion-point></root-content-insertion-point>');
529+
});
520530
});
521531
});
522532
}
@@ -571,6 +581,9 @@ class DomNodeFactory implements NodeFactory<Node> {
571581
return root;
572582
}
573583
createText(value: string): Node { return DOM.createTextNode(isPresent(value) ? value : ''); }
584+
createRootContentInsertionPoint(): Node {
585+
return DOM.createElement('root-content-insertion-point');
586+
}
574587
appendChild(parent: Node, child: Node) { DOM.appendChild(parent, child); }
575588
on(element: Node, eventName: string, callback: Function) {
576589
this._localEventListeners.push(new LocalEventListener(element, eventName, callback));

modules/angular2/test/core/render/view_spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export function main() {
1818
it('should register global event listeners', () => {
1919
var addCount = 0;
2020
var adder = () => { addCount++ };
21-
var view = new DefaultRenderView<Node>([], [], [], [], [adder]);
21+
var view = new DefaultRenderView<Node>([], [], [], [], [adder], []);
2222
view.hydrate();
2323
expect(addCount).toBe(1);
2424
});
@@ -28,7 +28,7 @@ export function main() {
2828
it('should deregister global event listeners', () => {
2929
var removeCount = 0;
3030
var adder = () => () => { removeCount++ };
31-
var view = new DefaultRenderView<Node>([], [], [], [], [adder]);
31+
var view = new DefaultRenderView<Node>([], [], [], [], [adder], []);
3232
view.hydrate();
3333
view.dehydrate();
3434
expect(removeCount).toBe(1);

modules/upgrade/src/angular.d.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,26 @@ declare namespace angular {
2525
require?: string;
2626
restrict?: string;
2727
scope?: {[key: string]: string};
28-
link?: Function;
28+
link?: {pre?: Function, post?: Function};
2929
}
3030
interface IAttributes {
3131
$observe(attr: string, fn: (v: string) => void);
3232
}
3333
interface ITranscludeFunction {}
3434
interface IAugmentedJQuery {
3535
bind(name: string, fn: () => void);
36+
data(name: string, value?: any);
37+
contents(): IAugmentedJQuery;
38+
length: number;
39+
[index: number]: Node;
3640
}
3741
interface IParseService {
3842
(expression: string): ICompiledExpression;
3943
}
4044
interface ICompiledExpression {
4145
assign(context: any, value: any): any;
4246
}
43-
function element(e: Element);
47+
function element(e: Element): IAugmentedJQuery;
4448
function bootstrap(e: Element, modules: IModule[], config: IAngularBootstrapConfig);
4549

4650
namespace auto {

modules/upgrade/src/ng2_facade.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ import {
55
HostViewRef,
66
Injector,
77
ProtoViewRef,
8-
SimpleChange
8+
SimpleChange,
9+
ViewRef
910
} from 'angular2/angular2';
1011
import {NG1_SCOPE} from './constants';
1112
import {ComponentInfo} from './metadata';
13+
import {ViewRef_} from "../../angular2/src/core/linker/view_ref";
14+
import Element = protractor.Element;
1215

1316
const INITIAL_VALUE = {
1417
__UNINITIALIZED__: true
@@ -21,23 +24,29 @@ export class Ng2ComponentFacade {
2124
hostViewRef: HostViewRef = null;
2225
changeDetector: ChangeDetectorRef = null;
2326
componentScope: angular.IScope;
27+
childNodes: Node[];
28+
contentInserctionPoint: Node = null;
2429

2530
constructor(private id: string, private info: ComponentInfo,
2631
private element: angular.IAugmentedJQuery, private attrs: angular.IAttributes,
2732
private scope: angular.IScope, private parentInjector: Injector,
2833
private parse: angular.IParseService, private viewManager: AppViewManager,
2934
private protoView: ProtoViewRef) {
35+
(<any>this.element[0]).id = id;
3036
this.componentScope = scope.$new();
37+
this.childNodes = <Node[]><any>element.contents();
3138
}
3239

3340
bootstrapNg2() {
3441
var childInjector =
3542
this.parentInjector.resolveAndCreateChild([bind(NG1_SCOPE).toValue(this.componentScope)]);
3643
this.hostViewRef =
3744
this.viewManager.createRootHostView(this.protoView, '#' + this.id, childInjector);
45+
var renderer: any = (<any>this.hostViewRef).render;
3846
var hostElement = this.viewManager.getHostElement(this.hostViewRef);
3947
this.changeDetector = this.hostViewRef.changeDetectorRef;
4048
this.component = this.viewManager.getComponent(hostElement);
49+
this.contentInserctionPoint = renderer.rootContentInsertionPoints[0];
4150
}
4251

4352
setupInputs() {
@@ -94,6 +103,16 @@ export class Ng2ComponentFacade {
94103
this.componentScope.$watch(() => this.changeDetector.detectChanges());
95104
}
96105

106+
projectContent() {
107+
var childNodes = this.childNodes;
108+
if (this.contentInserctionPoint) {
109+
var parent = this.contentInserctionPoint.parentNode;
110+
for (var i = 0, ii = childNodes.length; i < ii; i++) {
111+
parent.insertBefore(childNodes[i], this.contentInserctionPoint);
112+
}
113+
}
114+
}
115+
97116
setupOutputs() {
98117
var attrs = this.attrs;
99118
var outputs = this.info.outputs;

modules/upgrade/src/upgrade_module.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -169,16 +169,19 @@ function ng1ComponentDirective(info: ComponentInfo, idPrefix: string): Function
169169
return {
170170
restrict: 'E',
171171
require: REQUIRE_INJECTOR,
172-
link: (scope: angular.IScope, element: angular.IAugmentedJQuery, attrs: angular.IAttributes,
173-
parentInjector: any, transclude: angular.ITranscludeFunction): void => {
174-
var facade =
175-
new Ng2ComponentFacade(element[0].id = idPrefix + (idCount++), info, element, attrs,
176-
scope, <Injector>parentInjector, parse, viewManager, protoView);
177-
178-
facade.setupInputs();
179-
facade.bootstrapNg2();
180-
facade.setupOutputs();
181-
facade.registerCleanup();
172+
link: {
173+
post: (scope: angular.IScope, element: angular.IAugmentedJQuery, attrs: angular.IAttributes,
174+
parentInjector: any, transclude: angular.ITranscludeFunction): void => {
175+
var domElement = <any>element[0];
176+
var facade =
177+
new Ng2ComponentFacade(idPrefix + (idCount++), info, element, attrs, scope,
178+
<Injector>parentInjector, parse, viewManager, protoView);
179+
facade.setupInputs();
180+
facade.bootstrapNg2();
181+
facade.projectContent();
182+
facade.setupOutputs();
183+
facade.registerCleanup();
184+
}
182185
}
183186
};
184187
}

modules/upgrade/test/integration_spec.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,18 @@ export function main() {
1818
describe('upgrade: ng1 to ng2', () => {
1919
it('should have angular 1 loaded', () => expect(angular.version.major).toBe(1));
2020

21-
it('should instantiate ng2 in ng1 template', inject([AsyncTestCompleter], (async) => {
21+
it('should instantiate ng2 in ng1 template and project content',
22+
inject([AsyncTestCompleter], (async) => {
2223
var Ng2 = Component({selector: 'ng2'})
23-
.View({template: `{{ 'NG2' }}`})
24+
.View({template: `{{ 'NG2' }}(<ng-content></ng-content>)`})
2425
.Class({constructor: function() {}});
2526

26-
var element = html("<div>{{ 'ng1-' }}<ng2>~~</ng2>{{ '-ng1' }}</div>");
27+
var element = html("<div>{{ 'ng1[' }}<ng2>~{{ 'ng-content' }}~</ng2>{{ ']' }}</div>");
2728

2829
var upgradeModule: UpgradeModule = createUpgradeModule();
2930
upgradeModule.importNg2Component(Ng2);
3031
upgradeModule.bootstrap(element).ready(() => {
31-
expect(document.body.textContent).toEqual("ng1-NG2-ng1");
32+
expect(document.body.textContent).toEqual("ng1[NG2(~ng-content~)]");
3233
async.done();
3334
});
3435
}));

0 commit comments

Comments
 (0)
X Tutup