diff --git a/modules/directives/src/ng_if.js b/modules/directives/src/ng_if.js
new file mode 100644
index 0000000000..8a24bd4060
--- /dev/null
+++ b/modules/directives/src/ng_if.js
@@ -0,0 +1,30 @@
+import {Template} from 'core/annotations/annotations';
+import {OnChange} from 'core/compiler/interfaces';
+import {ViewPort} from 'core/compiler/viewport';
+import {isBlank} from 'facade/lang';
+
+@Template({
+ selector: '[ng-if]',
+ bind: {
+ 'ng-if': 'condition'
+ }
+})
+export class NgIf {
+ viewPort: ViewPort;
+ prevCondition: boolean;
+
+ constructor(viewPort: ViewPort) {
+ this.viewPort = viewPort;
+ this.prevCondition = null;
+ }
+
+ set condition(newCondition) {
+ if (newCondition && (isBlank(this.prevCondition) || !this.prevCondition)) {
+ this.prevCondition = true;
+ this.viewPort.create();
+ } else if (!newCondition && (isBlank(this.prevCondition) || this.prevCondition)) {
+ this.prevCondition = false;
+ this.viewPort.clear();
+ }
+ }
+}
diff --git a/modules/directives/test/ng_if_spec.js b/modules/directives/test/ng_if_spec.js
new file mode 100644
index 0000000000..e6324f0a3c
--- /dev/null
+++ b/modules/directives/test/ng_if_spec.js
@@ -0,0 +1,217 @@
+import {describe, xit, it, expect, beforeEach, ddescribe, iit, IS_DARTIUM} from 'test_lib/test_lib';
+
+import {DOM} from 'facade/dom';
+
+import {Injector} from 'di/di';
+import {Lexer, Parser, ChangeDetector} from 'change_detection/change_detection';
+
+import {Compiler, CompilerCache} from 'core/compiler/compiler';
+import {DirectiveMetadataReader} from 'core/compiler/directive_metadata_reader';
+
+import {Component} from 'core/annotations/annotations';
+import {TemplateConfig} from 'core/annotations/template_config';
+
+import {NgIf} from 'directives/ng_if';
+
+export function main() {
+ describe('ng-if', () => {
+ var view, cd, compiler, component;
+ beforeEach(() => {
+ compiler = new Compiler(null, new DirectiveMetadataReader(), new Parser(new Lexer()), new CompilerCache());
+ });
+
+ function createElement(html) {
+ return DOM.createTemplate(html).content.firstChild;
+ }
+
+ function createView(pv) {
+ component = new TestComponent();
+ view = pv.instantiate(null);
+ view.hydrate(new Injector([]), null, component);
+ cd = view.changeDetector;
+ }
+
+ function compileWithTemplate(template) {
+ return compiler.compile(TestComponent, createElement(template));
+ }
+
+ it('should work in a template attribute', (done) => {
+ compileWithTemplate('
hello
').then((pv) => {
+ createView(pv);
+ cd.detectChanges();
+
+ expect(view.nodes[0].querySelectorAll('copy-me').length).toEqual(1);
+ expect(DOM.getText(view.nodes[0])).toEqual('hello');
+ done();
+ });
+ });
+
+ it('should work in a template element', (done) => {
+ compileWithTemplate('hello2
').then((pv) => {
+ createView(pv);
+ cd.detectChanges();
+
+ expect(view.nodes[0].querySelectorAll('copy-me').length).toEqual(1);
+ expect(DOM.getText(view.nodes[0])).toEqual('hello2');
+ done();
+ });
+ });
+
+ it('should toggle node when condition changes', (done) => {
+ compileWithTemplate('hello
').then((pv) => {
+ createView(pv);
+
+ component.booleanCondition = false;
+ cd.detectChanges();
+ expect(view.nodes[0].querySelectorAll('copy-me').length).toEqual(0);
+ expect(DOM.getText(view.nodes[0])).toEqual('');
+
+
+ component.booleanCondition = true;
+ cd.detectChanges();
+ expect(view.nodes[0].querySelectorAll('copy-me').length).toEqual(1);
+ expect(DOM.getText(view.nodes[0])).toEqual('hello');
+
+ component.booleanCondition = false;
+ cd.detectChanges();
+ expect(view.nodes[0].querySelectorAll('copy-me').length).toEqual(0);
+ expect(DOM.getText(view.nodes[0])).toEqual('');
+
+ done();
+ });
+ });
+
+ it('should update several nodes with ng-if', (done) => {
+ var templateString =
+ '' +
+ 'helloNumber' +
+ 'helloString' +
+ 'helloFunction' +
+ '
';
+ compileWithTemplate(templateString).then((pv) => {
+ createView(pv);
+
+ cd.detectChanges();
+ expect(view.nodes[0].querySelectorAll('copy-me').length).toEqual(3);
+ expect(DOM.getText(view.nodes[0])).toEqual('helloNumberhelloStringhelloFunction');
+
+ component.numberCondition = 0;
+ cd.detectChanges();
+ expect(view.nodes[0].querySelectorAll('copy-me').length).toEqual(1);
+ expect(DOM.getText(view.nodes[0])).toEqual('helloString');
+
+ component.numberCondition = 1;
+ component.stringCondition = "bar";
+ cd.detectChanges();
+ expect(view.nodes[0].querySelectorAll('copy-me').length).toEqual(1);
+ expect(DOM.getText(view.nodes[0])).toEqual('helloNumber');
+ done();
+ });
+ });
+
+
+ if (!IS_DARTIUM) {
+ it('should leave the element if the condition is a non-empty string (JS)', (done) => {
+ compileWithTemplate('hello
').then((pv) => {
+ createView(pv);
+ cd.detectChanges();
+
+ expect(view.nodes[0].querySelectorAll('copy-me').length).toEqual(1);
+ expect(DOM.getText(view.nodes[0])).toEqual('hello');
+ done();
+ });
+ });
+
+ it('should leave the element if the condition is an object (JS)', (done) => {
+ compileWithTemplate('hello
').then((pv) => {
+ createView(pv);
+ cd.detectChanges();
+
+ expect(view.nodes[0].querySelectorAll('copy-me').length).toEqual(1);
+ expect(DOM.getText(view.nodes[0])).toEqual('hello');
+ done();
+ });
+ });
+
+ it('should remove the element if the condition is null (JS)', (done) => {
+ compileWithTemplate('hello
').then((pv) => {
+ createView(pv);
+ cd.detectChanges();
+
+ expect(view.nodes[0].querySelectorAll('copy-me').length).toEqual(0);
+ expect(DOM.getText(view.nodes[0])).toEqual('');
+ done();
+ });
+ });
+
+ it('should not add the element twice if the condition goes from true to true (JS)', (done) => {
+ compileWithTemplate('hello
').then((pv) => {
+ createView(pv);
+
+ cd.detectChanges();
+ expect(view.nodes[0].querySelectorAll('copy-me').length).toEqual(1);
+ expect(DOM.getText(view.nodes[0])).toEqual('hello');
+
+ component.numberCondition = 2;
+ cd.detectChanges();
+ expect(view.nodes[0].querySelectorAll('copy-me').length).toEqual(1);
+ expect(DOM.getText(view.nodes[0])).toEqual('hello');
+
+ done();
+ });
+ });
+
+ it('should not recreate the element if the condition goes from true to true (JS)', (done) => {
+ compileWithTemplate('hello
').then((pv) => {
+ createView(pv);
+
+ cd.detectChanges();
+ DOM.addClass(view.nodes[0].childNodes[1], "foo");
+
+ component.numberCondition = 2;
+ cd.detectChanges();
+ expect(DOM.hasClass(view.nodes[0].childNodes[1], "foo")).toBe(true);
+
+ done();
+ });
+ });
+ } else {
+ it('should not create the element if the condition is not a boolean (DART)', (done) => {
+ compileWithTemplate('hello
').then((pv) => {
+ createView(pv);
+ expect(function(){cd.detectChanges();}).toThrowError();
+ expect(view.nodes[0].querySelectorAll('copy-me').length).toEqual(0);
+ expect(DOM.getText(view.nodes[0])).toEqual('');
+ done();
+ });
+ });
+ }
+
+ });
+}
+
+@Component({
+ selector: 'test-cmp',
+ template: new TemplateConfig({
+ inline: '', // each test swaps with a custom template.
+ directives: [NgIf]
+ })
+})
+class TestComponent {
+ booleanCondition: boolean;
+ numberCondition: number;
+ stringCondition: string;
+ functionCondition: Function;
+ objectCondition: any;
+ nullCondition: any;
+ constructor() {
+ this.booleanCondition = true;
+ this.numberCondition = 1;
+ this.stringCondition = "foo";
+ this.functionCondition = function(s, n){
+ return s == "foo" && n == 1;
+ };
+ this.objectCondition = {};
+ this.nullCondition = null;
+ }
+}