diff --git a/modules/angular2/src/common/directives.ts b/modules/angular2/src/common/directives.ts
index ceb662d168..e81c64a200 100644
--- a/modules/angular2/src/common/directives.ts
+++ b/modules/angular2/src/common/directives.ts
@@ -6,6 +6,7 @@
export {NgClass} from './directives/ng_class';
export {NgFor} from './directives/ng_for';
export {NgIf} from './directives/ng_if';
+export {NgTemplateOutlet} from './directives/ng_template_outlet';
export {NgStyle} from './directives/ng_style';
export {NgSwitch, NgSwitchWhen, NgSwitchDefault} from './directives/ng_switch';
export {NgPlural, NgPluralCase, NgLocalization} from './directives/ng_plural';
diff --git a/modules/angular2/src/common/directives/core_directives.ts b/modules/angular2/src/common/directives/core_directives.ts
index 64772e101a..fed030c5c2 100644
--- a/modules/angular2/src/common/directives/core_directives.ts
+++ b/modules/angular2/src/common/directives/core_directives.ts
@@ -2,6 +2,7 @@ import {CONST_EXPR, Type} from 'angular2/src/facade/lang';
import {NgClass} from './ng_class';
import {NgFor} from './ng_for';
import {NgIf} from './ng_if';
+import {NgTemplateOutlet} from './ng_template_outlet';
import {NgStyle} from './ng_style';
import {NgSwitch, NgSwitchWhen, NgSwitchDefault} from './ng_switch';
import {NgPlural, NgPluralCase} from './ng_plural';
@@ -50,6 +51,7 @@ export const CORE_DIRECTIVES: Type[] = CONST_EXPR([
NgClass,
NgFor,
NgIf,
+ NgTemplateOutlet,
NgStyle,
NgSwitch,
NgSwitchWhen,
diff --git a/modules/angular2/src/common/directives/ng_template_outlet.ts b/modules/angular2/src/common/directives/ng_template_outlet.ts
new file mode 100644
index 0000000000..38784bbad4
--- /dev/null
+++ b/modules/angular2/src/common/directives/ng_template_outlet.ts
@@ -0,0 +1,26 @@
+import {Directive, Input, ViewContainerRef, ViewRef, TemplateRef} from 'angular2/core';
+import {isPresent} from 'angular2/src/facade/lang';
+
+/**
+ * Creates and inserts an embedded view based on a prepared `TemplateRef`.
+ *
+ * ### Syntax
+ * - ``
+ */
+@Directive({selector: '[ngTemplateOutlet]'})
+export class NgTemplateOutlet {
+ private _insertedViewRef: ViewRef;
+
+ constructor(private _viewContainerRef: ViewContainerRef) {}
+
+ @Input()
+ set ngTemplateOutlet(templateRef: TemplateRef) {
+ if (isPresent(this._insertedViewRef)) {
+ this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._insertedViewRef));
+ }
+
+ if (isPresent(templateRef)) {
+ this._insertedViewRef = this._viewContainerRef.createEmbeddedView(templateRef);
+ }
+ }
+}
diff --git a/modules/angular2/test/common/directives/ng_template_outlet_spec.ts b/modules/angular2/test/common/directives/ng_template_outlet_spec.ts
new file mode 100644
index 0000000000..158188f206
--- /dev/null
+++ b/modules/angular2/test/common/directives/ng_template_outlet_spec.ts
@@ -0,0 +1,113 @@
+import {
+ AsyncTestCompleter,
+ TestComponentBuilder,
+ beforeEach,
+ ddescribe,
+ describe,
+ el,
+ expect,
+ iit,
+ inject,
+ it,
+ xit,
+} from 'angular2/testing_internal';
+
+import {Component, Directive, TemplateRef, ContentChildren, QueryList} from 'angular2/core';
+
+import {NgTemplateOutlet} from 'angular2/src/common/directives/ng_template_outlet';
+
+export function main() {
+ describe('insert', () => {
+ it('should do nothing if templateRef is null',
+ inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
+ var template = ``;
+ tcb.overrideTemplate(TestComponent, template)
+ .createAsync(TestComponent)
+ .then((fixture) => {
+
+ fixture.detectChanges();
+ expect(fixture.nativeElement).toHaveText('');
+
+ async.done();
+ });
+ }));
+
+ it('should insert content specified by TemplateRef',
+ inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
+ var template =
+ `foo`;
+ tcb.overrideTemplate(TestComponent, template)
+ .createAsync(TestComponent)
+ .then((fixture) => {
+
+ fixture.detectChanges();
+ expect(fixture.nativeElement).toHaveText('');
+
+ var refs = fixture.debugElement.children[0].getLocal('refs');
+
+ fixture.componentInstance.currentTplRef = refs.tplRefs.first;
+ fixture.detectChanges();
+ expect(fixture.nativeElement).toHaveText('foo');
+
+ async.done();
+ });
+ }));
+
+ it('should clear content if TemplateRef becomes null',
+ inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
+ var template =
+ `foo`;
+ tcb.overrideTemplate(TestComponent, template)
+ .createAsync(TestComponent)
+ .then((fixture) => {
+
+ fixture.detectChanges();
+ var refs = fixture.debugElement.children[0].getLocal('refs');
+
+ fixture.componentInstance.currentTplRef = refs.tplRefs.first;
+ fixture.detectChanges();
+ expect(fixture.nativeElement).toHaveText('foo');
+
+ fixture.componentInstance.currentTplRef = null;
+ fixture.detectChanges();
+ expect(fixture.nativeElement).toHaveText('');
+
+ async.done();
+ });
+ }));
+
+ it('should swap content if TemplateRef changes',
+ inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
+ var template = `foobar`;
+ tcb.overrideTemplate(TestComponent, template)
+ .createAsync(TestComponent)
+ .then((fixture) => {
+
+ fixture.detectChanges();
+ var refs = fixture.debugElement.children[0].getLocal('refs');
+
+ fixture.componentInstance.currentTplRef = refs.tplRefs.first;
+ fixture.detectChanges();
+ expect(fixture.nativeElement).toHaveText('foo');
+
+ fixture.componentInstance.currentTplRef = refs.tplRefs.last;
+ fixture.detectChanges();
+ expect(fixture.nativeElement).toHaveText('bar');
+
+ async.done();
+ });
+ }));
+
+ });
+}
+
+
+@Directive({selector: 'tpl-refs', exportAs: 'tplRefs'})
+class CaptureTplRefs {
+ @ContentChildren(TemplateRef) tplRefs: QueryList;
+}
+
+@Component({selector: 'test-cmp', directives: [NgTemplateOutlet, CaptureTplRefs], template: ''})
+class TestComponent {
+ currentTplRef: TemplateRef;
+}
diff --git a/modules/angular2/test/public_api_spec.ts b/modules/angular2/test/public_api_spec.ts
index e365717eaa..0674737ce4 100644
--- a/modules/angular2/test/public_api_spec.ts
+++ b/modules/angular2/test/public_api_spec.ts
@@ -66,6 +66,7 @@ var NG_COMMON = [
'NgFormControl',
'NgFormModel',
'NgIf',
+ 'NgTemplateOutlet',
'NgModel',
'NgSelectOption',
'NgStyle',
diff --git a/tools/public_api_guard/public_api_spec.ts b/tools/public_api_guard/public_api_spec.ts
index d2a87aa9f5..7ef838ec1d 100644
--- a/tools/public_api_guard/public_api_spec.ts
+++ b/tools/public_api_guard/public_api_spec.ts
@@ -736,6 +736,9 @@ const COMMON = [
'NgIf',
'NgIf.constructor(_viewContainer:ViewContainerRef, _templateRef:TemplateRef)',
'NgIf.ngIf=(newCondition:any)',
+ 'NgTemplateOutlet',
+ 'NgTemplateOutlet.constructor(_viewContainerRef:ViewContainerRef)',
+ 'NgTemplateOutlet.ngTemplateOutlet=(templateRef:TemplateRef)',
'NgLocalization',
'NgLocalization.getPluralCategory(value:any):string',
'NgModel',