/** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import {EventEmitter} from '@angular/core'; import {C, D, E, L, T, V, b, b1, c, defineComponent, defineDirective, e, p, rC, rc, t, v} from '../../src/render3/index'; import {NO_CHANGE} from '../../src/render3/instructions'; import {renderToHtml} from './render_util'; describe('elementProperty', () => { it('should support bindings to properties', () => { function Template(ctx: any, cm: boolean) { if (cm) { E(0, 'span'); e(); } p(0, 'id', b(ctx)); } expect(renderToHtml(Template, 'testId')).toEqual(''); expect(renderToHtml(Template, 'otherId')).toEqual(''); }); it('should support creation time bindings to properties', () => { function expensive(ctx: string): any { if (ctx === 'cheapId') { return ctx; } else { throw 'Too expensive!'; } } function Template(ctx: string, cm: boolean) { if (cm) { E(0, 'span'); e(); } p(0, 'id', cm ? expensive(ctx) : NO_CHANGE); } expect(renderToHtml(Template, 'cheapId')).toEqual(''); expect(renderToHtml(Template, 'expensiveId')).toEqual(''); }); it('should support interpolation for properties', () => { function Template(ctx: any, cm: boolean) { if (cm) { E(0, 'span'); e(); } p(0, 'id', b1('_', ctx, '_')); } expect(renderToHtml(Template, 'testId')).toEqual(''); expect(renderToHtml(Template, 'otherId')).toEqual(''); }); describe('input properties', () => { let button: MyButton; let otherDir: OtherDir; class MyButton { disabled: boolean; static ngDirectiveDef = defineDirective( {type: MyButton, factory: () => button = new MyButton(), inputs: {disabled: 'disabled'}}); } class OtherDir { id: boolean; clickStream = new EventEmitter(); static ngDirectiveDef = defineDirective({ type: OtherDir, factory: () => otherDir = new OtherDir(), inputs: {id: 'id'}, outputs: {clickStream: 'click'} }); } it('should check input properties before setting (directives)', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { E(0, 'button'); { D(0, MyButton.ngDirectiveDef.n(), MyButton.ngDirectiveDef); D(1, OtherDir.ngDirectiveDef.n(), OtherDir.ngDirectiveDef); T(1, 'Click me'); } e(); } p(0, 'disabled', b(ctx.isDisabled)); p(0, 'id', b(ctx.id)); } const ctx: any = {isDisabled: true, id: 0}; expect(renderToHtml(Template, ctx)).toEqual(``); expect(button !.disabled).toEqual(true); expect(otherDir !.id).toEqual(0); ctx.isDisabled = false; ctx.id = 1; expect(renderToHtml(Template, ctx)).toEqual(``); expect(button !.disabled).toEqual(false); expect(otherDir !.id).toEqual(1); }); it('should support mixed element properties and input properties', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { E(0, 'button'); { D(0, MyButton.ngDirectiveDef.n(), MyButton.ngDirectiveDef); T(1, 'Click me'); } e(); } p(0, 'disabled', b(ctx.isDisabled)); p(0, 'id', b(ctx.id)); } const ctx: any = {isDisabled: true, id: 0}; expect(renderToHtml(Template, ctx)).toEqual(``); expect(button !.disabled).toEqual(true); ctx.isDisabled = false; ctx.id = 1; expect(renderToHtml(Template, ctx)).toEqual(``); expect(button !.disabled).toEqual(false); }); it('should check that property is not an input property before setting (component)', () => { let comp: Comp; class Comp { id: number; static ngComponentDef = defineComponent({ tag: 'comp', type: Comp, template: function(ctx: any, cm: boolean) {}, factory: () => comp = new Comp(), inputs: {id: 'id'} }); } /** */ function Template(ctx: any, cm: boolean) { if (cm) { E(0, Comp.ngComponentDef); { D(0, Comp.ngComponentDef.n(), Comp.ngComponentDef); } e(); } p(0, 'id', b(ctx.id)); Comp.ngComponentDef.r(0, 0); } expect(renderToHtml(Template, {id: 1})).toEqual(``); expect(comp !.id).toEqual(1); expect(renderToHtml(Template, {id: 2})).toEqual(``); expect(comp !.id).toEqual(2); }); it('should support two input properties with the same name', () => { let otherDisabledDir: OtherDisabledDir; class OtherDisabledDir { disabled: boolean; static ngDirectiveDef = defineDirective({ type: OtherDisabledDir, factory: () => otherDisabledDir = new OtherDisabledDir(), inputs: {disabled: 'disabled'} }); } /** */ function Template(ctx: any, cm: boolean) { if (cm) { E(0, 'button'); { D(0, MyButton.ngDirectiveDef.n(), MyButton.ngDirectiveDef); D(1, OtherDisabledDir.ngDirectiveDef.n(), OtherDisabledDir.ngDirectiveDef); T(1, 'Click me'); } e(); } p(0, 'disabled', b(ctx.isDisabled)); } const ctx: any = {isDisabled: true}; expect(renderToHtml(Template, ctx)).toEqual(``); expect(button !.disabled).toEqual(true); expect(otherDisabledDir !.disabled).toEqual(true); ctx.isDisabled = false; expect(renderToHtml(Template, ctx)).toEqual(``); expect(button !.disabled).toEqual(false); expect(otherDisabledDir !.disabled).toEqual(false); }); it('should set input property if there is an output first', () => { /** */ function Template(ctx: any, cm: boolean) { if (cm) { E(0, 'button'); { D(0, OtherDir.ngDirectiveDef.n(), OtherDir.ngDirectiveDef); L('click', ctx.onClick.bind(ctx)); T(1, 'Click me'); } e(); } p(0, 'id', b(ctx.id)); } let counter = 0; const ctx: any = {id: 1, onClick: () => counter++}; expect(renderToHtml(Template, ctx)).toEqual(``); expect(otherDir !.id).toEqual(1); otherDir !.clickStream.next(); expect(counter).toEqual(1); ctx.id = 2; renderToHtml(Template, ctx); expect(otherDir !.id).toEqual(2); }); }); describe('attributes and input properties', () => { let myDir: MyDir; class MyDir { role: string; direction: string; changeStream = new EventEmitter(); static ngDirectiveDef = defineDirective({ type: MyDir, factory: () => myDir = new MyDir(), inputs: {role: 'role', direction: 'dir'}, outputs: {changeStream: 'change'} }); } let dirB: MyDirB; class MyDirB { roleB: string; static ngDirectiveDef = defineDirective( {type: MyDirB, factory: () => dirB = new MyDirB(), inputs: {roleB: 'role'}}); } it('should set input property based on attribute if existing', () => { /**
*/ function Template(ctx: any, cm: boolean) { if (cm) { E(0, 'div', ['role', 'button']); { D(0, MyDir.ngDirectiveDef.n(), MyDir.ngDirectiveDef); } e(); } } expect(renderToHtml(Template, {})).toEqual(`
`); expect(myDir !.role).toEqual('button'); }); it('should set input property and attribute if both defined', () => { /**
*/ function Template(ctx: any, cm: boolean) { if (cm) { E(0, 'div', ['role', 'button']); { D(0, MyDir.ngDirectiveDef.n(), MyDir.ngDirectiveDef); } e(); } p(0, 'role', b(ctx.role)); } expect(renderToHtml(Template, {role: 'listbox'})).toEqual(`
`); expect(myDir !.role).toEqual('listbox'); renderToHtml(Template, {role: 'button'}); expect(myDir !.role).toEqual('button'); }); it('should set two directive input properties based on same attribute', () => { /**
*/ function Template(ctx: any, cm: boolean) { if (cm) { E(0, 'div', ['role', 'button']); { D(0, MyDir.ngDirectiveDef.n(), MyDir.ngDirectiveDef); D(1, MyDirB.ngDirectiveDef.n(), MyDirB.ngDirectiveDef); } e(); } } expect(renderToHtml(Template, {})).toEqual(`
`); expect(myDir !.role).toEqual('button'); expect(dirB !.roleB).toEqual('button'); }); it('should process two attributes on same directive', () => { /**
*/ function Template(ctx: any, cm: boolean) { if (cm) { E(0, 'div', ['role', 'button', 'dir', 'rtl']); { D(0, MyDir.ngDirectiveDef.n(), MyDir.ngDirectiveDef); } e(); } } expect(renderToHtml(Template, {})).toEqual(`
`); expect(myDir !.role).toEqual('button'); expect(myDir !.direction).toEqual('rtl'); }); it('should process attributes and outputs properly together', () => { /**
*/ function Template(ctx: any, cm: boolean) { if (cm) { E(0, 'div', ['role', 'button']); { D(0, MyDir.ngDirectiveDef.n(), MyDir.ngDirectiveDef); L('change', ctx.onChange.bind(ctx)); } e(); } } let counter = 0; expect(renderToHtml(Template, { onChange: () => counter++ })).toEqual(`
`); expect(myDir !.role).toEqual('button'); myDir !.changeStream.next(); expect(counter).toEqual(1); }); it('should process attributes properly for directives with later indices', () => { /** *
*
*/ function Template(ctx: any, cm: boolean) { if (cm) { E(0, 'div', ['role', 'button', 'dir', 'rtl']); { D(0, MyDir.ngDirectiveDef.n(), MyDir.ngDirectiveDef); } e(); E(1, 'div', ['role', 'listbox']); { D(1, MyDirB.ngDirectiveDef.n(), MyDirB.ngDirectiveDef); } e(); } } expect(renderToHtml(Template, {})) .toEqual(`
`); expect(myDir !.role).toEqual('button'); expect(myDir !.direction).toEqual('rtl'); expect(dirB !.roleB).toEqual('listbox'); }); it('should process attributes properly inside a for loop', () => { class Comp { static ngComponentDef = defineComponent({ tag: 'comp', type: Comp, template: function(ctx: any, cm: boolean) { if (cm) { E(0, 'div', ['role', 'button']); { D(0, MyDir.ngDirectiveDef.n(), MyDir.ngDirectiveDef); } e(); T(1); } t(1, b(D(0).role)); }, factory: () => new Comp() }); } /** * % for (let i = 0; i < 3; i++) { * * % } */ function Template(ctx: any, cm: boolean) { if (cm) { C(0); c(); } rC(0); { for (let i = 0; i < 2; i++) { if (V(0)) { E(0, Comp.ngComponentDef); { D(0, Comp.ngComponentDef.n(), Comp.ngComponentDef); } e(); } Comp.ngComponentDef.r(0, 0); v(); } } rc(); } expect(renderToHtml(Template, {})) .toEqual( `
button
button
`); }); }); });