X Tutup
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions modules/angular2/src/core/render/dom/dom_renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export abstract class DomRenderer extends Renderer implements NodeFactory<Node>

abstract destroyView(viewRef: RenderViewRef);

abstract createRootContentInsertionPoint();

getNativeElementSync(location: RenderElementRef): any {
return resolveInternalDomView(location.renderView).boundElements[location.boundElementIndex];
}
Expand Down Expand Up @@ -282,6 +284,9 @@ export class DomRenderer_ extends DomRenderer {
DOM.setAttribute(node, attrNameAndValues[attrIdx], attrNameAndValues[attrIdx + 1]);
}
}
createRootContentInsertionPoint(): Node {
return DOM.createComment('root-content-insertion-point');
}
createShadowRoot(host: Node, templateId: number): Node {
var sr = DOM.createShadowRoot(host);
var styles = this._nativeShadowStyles.get(templateId);
Expand Down
2 changes: 1 addition & 1 deletion modules/angular2/src/core/render/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class DefaultRenderView<N> extends RenderViewRef {

constructor(public fragments: DefaultRenderFragmentRef<N>[], public boundTextNodes: N[],
public boundElements: N[], public nativeShadowRoots: N[],
public globalEventAdders: Function[]) {
public globalEventAdders: Function[], public rootContentInsertionPoints: N[]) {
super();
}

Expand Down
36 changes: 28 additions & 8 deletions modules/angular2/src/core/render/view_factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@ export function createRenderView(fragmentCmds: RenderTemplateCmd[], inplaceEleme
fragments.push(new DefaultRenderFragmentRef(context.fragments[i]));
}
view = new DefaultRenderView<any>(fragments, context.boundTextNodes, context.boundElements,
context.nativeShadowRoots, context.globalEventAdders);
context.nativeShadowRoots, context.globalEventAdders,
context.rootContentInsertionPoints);
return view;
}

export interface NodeFactory<N> {
resolveComponentTemplate(templateId: number): RenderTemplateCmd[];
createTemplateAnchor(attrNameAndValues: string[]): N;
createElement(name: string, attrNameAndValues: string[]): N;
createRootContentInsertionPoint(): N;
mergeElement(existing: N, attrNameAndValues: string[]);
createShadowRoot(host: N, templateId: number): N;
createText(value: string): N;
Expand All @@ -41,14 +43,19 @@ export interface NodeFactory<N> {

class BuildContext<N> {
constructor(private _eventDispatcher: Function, public factory: NodeFactory<N>,
private _inplaceElement: N) {}
private _inplaceElement: N) {
this.isHost = isPresent((_inplaceElement));
}
private _builders: RenderViewBuilder<N>[] = [];

globalEventAdders: Function[] = [];
boundElements: N[] = [];
boundTextNodes: N[] = [];
nativeShadowRoots: N[] = [];
fragments: N[][] = [];
rootContentInsertionPoints: N[] = [];
componentCount: number = 0;
isHost: boolean;

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

enqueueComponentBuilder(component: Component<N>) {
this.componentCount++;
this._builders.push(new RenderViewBuilder<N>(
component, null, this.factory.resolveComponentTemplate(component.cmd.templateId)));
}
Expand Down Expand Up @@ -131,10 +139,20 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
}
visitNgContent(cmd: RenderNgContentCmd, context: BuildContext<N>): any {
if (isPresent(this.parentComponent)) {
var projectedNodes = this.parentComponent.project(cmd.index);
for (var i = 0; i < projectedNodes.length; i++) {
var node = projectedNodes[i];
this._addChild(node, cmd.ngContentIndex, context);
if (this.parentComponent.isRoot) {
var insertionPoint = context.factory.createRootContentInsertionPoint();
if (this.parent instanceof Component) {
context.factory.appendChild((<Component<N>>this.parent).shadowRoot, insertionPoint);
} else {
context.factory.appendChild(<N>this.parent, insertionPoint);
}
context.rootContentInsertionPoints.push(insertionPoint);
} else {
var projectedNodes = this.parentComponent.project(cmd.index);
for (var i = 0; i < projectedNodes.length; i++) {
var node = projectedNodes[i];
this._addChild(node, cmd.ngContentIndex, context);
}
}
}
return null;
Expand All @@ -154,7 +172,8 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
root = context.factory.createShadowRoot(el, cmd.templateId);
context.nativeShadowRoots.push(root);
}
var component = new Component(el, root, cmd);
var isRoot = context.componentCount === 0 && context.isHost;
var component = new Component(el, root, cmd, isRoot);
context.enqueueComponentBuilder(component);
this.parentStack.push(component);
return null;
Expand Down Expand Up @@ -213,7 +232,8 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
class Component<N> {
private contentNodesByNgContentIndex: N[][] = [];

constructor(public hostElement: N, public shadowRoot: N, public cmd: RenderBeginComponentCmd) {}
constructor(public hostElement: N, public shadowRoot: N, public cmd: RenderBeginComponentCmd,
public isRoot: boolean) {}
addContentNode(ngContentIndex: number, node: N, context: BuildContext<N>) {
if (isBlank(ngContentIndex)) {
if (this.cmd.nativeShadow) {
Expand Down
13 changes: 13 additions & 0 deletions modules/angular2/test/core/render/view_factory_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,16 @@ export function main() {
expect(stringifyFragment(view.fragments[0].nodes))
.toEqual('<a-comp><b-comp>(hello)</b-comp></a-comp>');
});


it('should store content injection points for root component in a view', () => {
componentTemplates.set(0, [ngContent(0, null)]);
var view =
createRenderView([beginComponent('a-comp', [], [], false, null, 0), endComponent()],
DOM.createElement('root'), nodeFactory);
expect(stringifyFragment(view.rootContentInsertionPoints))
.toEqual('<root-content-insertion-point></root-content-insertion-point>');
});
});
});
}
Expand Down Expand Up @@ -571,6 +581,9 @@ class DomNodeFactory implements NodeFactory<Node> {
return root;
}
createText(value: string): Node { return DOM.createTextNode(isPresent(value) ? value : ''); }
createRootContentInsertionPoint(): Node {
return DOM.createElement('root-content-insertion-point');
}
appendChild(parent: Node, child: Node) { DOM.appendChild(parent, child); }
on(element: Node, eventName: string, callback: Function) {
this._localEventListeners.push(new LocalEventListener(element, eventName, callback));
Expand Down
4 changes: 2 additions & 2 deletions modules/angular2/test/core/render/view_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function main() {
it('should register global event listeners', () => {
var addCount = 0;
var adder = () => { addCount++ };
var view = new DefaultRenderView<Node>([], [], [], [], [adder]);
var view = new DefaultRenderView<Node>([], [], [], [], [adder], []);
view.hydrate();
expect(addCount).toBe(1);
});
Expand All @@ -28,7 +28,7 @@ export function main() {
it('should deregister global event listeners', () => {
var removeCount = 0;
var adder = () => () => { removeCount++ };
var view = new DefaultRenderView<Node>([], [], [], [], [adder]);
var view = new DefaultRenderView<Node>([], [], [], [], [adder], []);
view.hydrate();
view.dehydrate();
expect(removeCount).toBe(1);
Expand Down
8 changes: 6 additions & 2 deletions modules/upgrade/src/angular.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,26 @@ declare namespace angular {
require?: string;
restrict?: string;
scope?: {[key: string]: string};
link?: Function;
link?: {pre?: Function, post?: Function};
}
interface IAttributes {
$observe(attr: string, fn: (v: string) => void);
}
interface ITranscludeFunction {}
interface IAugmentedJQuery {
bind(name: string, fn: () => void);
data(name: string, value?: any);
contents(): IAugmentedJQuery;
length: number;
[index: number]: Node;
}
interface IParseService {
(expression: string): ICompiledExpression;
}
interface ICompiledExpression {
assign(context: any, value: any): any;
}
function element(e: Element);
function element(e: Element): IAugmentedJQuery;
function bootstrap(e: Element, modules: IModule[], config: IAngularBootstrapConfig);

namespace auto {
Expand Down
16 changes: 12 additions & 4 deletions modules/upgrade/src/ng1_facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class ExportedNg1Component {
var scope = directive.scope;
if (typeof scope == 'object') {
for (var name in scope) {
if (scope.hasOwnProperty(name)) {
if ((<any>scope).hasOwnProperty(name)) {
var localName = scope[name];
var type = localName.charAt(0);
localName = localName.substr(1) || name;
Expand Down Expand Up @@ -96,7 +96,7 @@ export class ExportedNg1Component {
static resolve(exportedComponents: {[name: string]: ExportedNg1Component},
injector: angular.auto.IInjectorService) {
for (var name in exportedComponents) {
if (exportedComponents.hasOwnProperty(name)) {
if ((<any>exportedComponents).hasOwnProperty(name)) {
var exportedComponent = exportedComponents[name];
exportedComponent.extractBindings(injector);
}
Expand All @@ -112,8 +112,16 @@ class Ng1ComponentFacade implements OnChanges, DoCheck {
private inputs: string[], private outputs: string[], private propOuts: string[],
private checkProperties: string[], private propertyMap: {[key: string]: string}) {
var chailTail = scope.$$childTail; // remember where the next scope is inserted
compile(elementRef.nativeElement)(scope);

var element: Element = elementRef.nativeElement;
var childNodes: Node[] = [];
var childNode;
while (childNode = element.firstChild) {
element.removeChild(childNode);
childNodes.push(childNode);
}
element.appendChild(element.ownerDocument.createElement('ng-transclude'));
compile(element)(scope, null,
{parentBoundTranscludeFn: (scope, cloneAttach) => cloneAttach(childNodes)});
// If we are first scope take it, otherwise take the next one in list.
this.componentScope = chailTail ? chailTail.$$nextSibling : scope.$$childHead;

Expand Down
21 changes: 20 additions & 1 deletion modules/upgrade/src/ng2_facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import {
HostViewRef,
Injector,
ProtoViewRef,
SimpleChange
SimpleChange,
ViewRef
} from 'angular2/angular2';
import {NG1_SCOPE} from './constants';
import {ComponentInfo} from './metadata';
import {ViewRef_} from "../../angular2/src/core/linker/view_ref";
import Element = protractor.Element;

const INITIAL_VALUE = {
__UNINITIALIZED__: true
Expand All @@ -21,23 +24,29 @@ export class Ng2ComponentFacade {
hostViewRef: HostViewRef = null;
changeDetector: ChangeDetectorRef = null;
componentScope: angular.IScope;
childNodes: Node[];
contentInserctionPoint: Node = null;

constructor(private id: string, private info: ComponentInfo,
private element: angular.IAugmentedJQuery, private attrs: angular.IAttributes,
private scope: angular.IScope, private parentInjector: Injector,
private parse: angular.IParseService, private viewManager: AppViewManager,
private protoView: ProtoViewRef) {
(<any>this.element[0]).id = id;
this.componentScope = scope.$new();
this.childNodes = <Node[]><any>element.contents();
}

bootstrapNg2() {
var childInjector =
this.parentInjector.resolveAndCreateChild([bind(NG1_SCOPE).toValue(this.componentScope)]);
this.hostViewRef =
this.viewManager.createRootHostView(this.protoView, '#' + this.id, childInjector);
var renderer: any = (<any>this.hostViewRef).render;
var hostElement = this.viewManager.getHostElement(this.hostViewRef);
this.changeDetector = this.hostViewRef.changeDetectorRef;
this.component = this.viewManager.getComponent(hostElement);
this.contentInserctionPoint = renderer.rootContentInsertionPoints[0];
}

setupInputs() {
Expand Down Expand Up @@ -94,6 +103,16 @@ export class Ng2ComponentFacade {
this.componentScope.$watch(() => this.changeDetector.detectChanges());
}

projectContent() {
var childNodes = this.childNodes;
if (this.contentInserctionPoint) {
var parent = this.contentInserctionPoint.parentNode;
for (var i = 0, ii = childNodes.length; i < ii; i++) {
parent.insertBefore(childNodes[i], this.contentInserctionPoint);
}
}
}

setupOutputs() {
var attrs = this.attrs;
var outputs = this.info.outputs;
Expand Down
25 changes: 14 additions & 11 deletions modules/upgrade/src/upgrade_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class UpgradeModule {
}

exportAsNg2Component(name: string): Type {
if (this.exportedNg1Components.hasOwnProperty(name)) {
if ((<any>this.exportedNg1Components).hasOwnProperty(name)) {
return this.exportedNg1Components[name].type;
} else {
return (this.exportedNg1Components[name] = new ExportedNg1Component(name)).type;
Expand Down Expand Up @@ -169,16 +169,19 @@ function ng1ComponentDirective(info: ComponentInfo, idPrefix: string): Function
return {
restrict: 'E',
require: REQUIRE_INJECTOR,
link: (scope: angular.IScope, element: angular.IAugmentedJQuery, attrs: angular.IAttributes,
parentInjector: any, transclude: angular.ITranscludeFunction): void => {
var facade =
new Ng2ComponentFacade(element[0].id = idPrefix + (idCount++), info, element, attrs,
scope, <Injector>parentInjector, parse, viewManager, protoView);

facade.setupInputs();
facade.bootstrapNg2();
facade.setupOutputs();
facade.registerCleanup();
link: {
post: (scope: angular.IScope, element: angular.IAugmentedJQuery, attrs: angular.IAttributes,
parentInjector: any, transclude: angular.ITranscludeFunction): void => {
var domElement = <any>element[0];
var facade =
new Ng2ComponentFacade(idPrefix + (idCount++), info, element, attrs, scope,
<Injector>parentInjector, parse, viewManager, protoView);
facade.setupInputs();
facade.bootstrapNg2();
facade.projectContent();
facade.setupOutputs();
facade.registerCleanup();
}
}
};
}
Expand Down
29 changes: 16 additions & 13 deletions modules/upgrade/test/integration_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,39 +18,42 @@ export function main() {
describe('upgrade: ng1 to ng2', () => {
it('should have angular 1 loaded', () => expect(angular.version.major).toBe(1));

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

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

var upgradeModule: UpgradeModule = createUpgradeModule();
upgradeModule.importNg2Component(Ng2);
upgradeModule.bootstrap(element).ready(() => {
expect(document.body.textContent).toEqual("ng1-NG2-ng1");
expect(document.body.textContent).toEqual("ng1[NG2(~ng-content~)]");
async.done();
});
}));

it('should instantiate ng1 in ng2 template', inject([AsyncTestCompleter], (async) => {
var upgradeModule: UpgradeModule = createUpgradeModule();
it('should instantiate ng1 in ng2 template and project content',
inject([AsyncTestCompleter], (async) => {
var upgrMod: UpgradeModule = createUpgradeModule();

var Ng2 = Component({selector: 'ng2-1'})
.View({
template: `{{ 'ng2(' }}<ng1></ng1>{{ ')' }}`,
directives: [upgradeModule.exportAsNg2Component('ng1')]
template: `{{ 'ng2(' }}<ng1>{{'transclude'}}</ng1>{{ ')' }}`,
directives: [upgrMod.exportAsNg2Component('ng1')]
})
.Class({constructor: function() {}});

upgradeModule.ng1Module.directive('ng1',
() => { return {template: 'ng1 {{ "WORKS" }}!'}; });
upgradeModule.importNg2Component(Ng2);
upgrMod.ng1Module.directive('ng1', () => {
return {transclude: true, template: '{{ "ng1" }}(<ng-transclude></ng-transclude>)'};
});
upgrMod.importNg2Component(Ng2);

var element = html("<div>{{'ng1('}}<ng2-1></ng2-1>{{')'}}</div>");

upgradeModule.bootstrap(element).ready(() => {
expect(document.body.textContent).toEqual("ng1(ng2(ng1 WORKS!))");
upgrMod.bootstrap(element).ready(() => {
expect(document.body.textContent).toEqual("ng1(ng2(ng1(transclude)))");
async.done();
});
}));
Expand Down
Loading
X Tutup