From b50edfd1f3bca1faaaee6d433febbee514510bec Mon Sep 17 00:00:00 2001 From: Pawel Kozlowski Date: Sun, 21 Jun 2015 14:18:28 +0200 Subject: [PATCH] feat(NgStyle): add new NgStyle directive Closes #2665 --- modules/angular2/src/directives/ng_style.ts | 38 ++++++ .../angular2/test/directives/ng_style_spec.ts | 126 ++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 modules/angular2/src/directives/ng_style.ts create mode 100644 modules/angular2/test/directives/ng_style_spec.ts diff --git a/modules/angular2/src/directives/ng_style.ts b/modules/angular2/src/directives/ng_style.ts new file mode 100644 index 0000000000..359deab0a9 --- /dev/null +++ b/modules/angular2/src/directives/ng_style.ts @@ -0,0 +1,38 @@ +import {Directive, onCheck} from 'angular2/annotations'; +import {ElementRef} from 'angular2/core'; +import {Pipe} from 'angular2/src/change_detection/pipes/pipe'; +import {PipeRegistry} from 'angular2/src/change_detection/pipes/pipe_registry'; +import {KeyValueChanges} from 'angular2/src/change_detection/pipes/keyvalue_changes'; +import {isPresent, print} from 'angular2/src/facade/lang'; +import {Renderer} from 'angular2/src/render/api'; + +@Directive({selector: '[ng-style]', lifecycle: [onCheck], properties: ['rawStyle: ng-style']}) +export class NgStyle { + _pipe: Pipe; + _rawStyle; + + constructor(private _pipeRegistry: PipeRegistry, private _ngEl: ElementRef, + private _renderer: Renderer) {} + + set rawStyle(v) { + this._rawStyle = v; + this._pipe = this._pipeRegistry.get('keyValDiff', this._rawStyle); + } + + onCheck() { + var diff = this._pipe.transform(this._rawStyle); + if (isPresent(diff) && isPresent(diff.wrapped)) { + this._applyChanges(diff.wrapped); + } + } + + private _applyChanges(diff: KeyValueChanges): void { + diff.forEachAddedItem((record) => { this._setStyle(record.key, record.currentValue); }); + diff.forEachChangedItem((record) => { this._setStyle(record.key, record.currentValue); }); + diff.forEachRemovedItem((record) => { this._setStyle(record.key, null); }); + } + + private _setStyle(name: string, val: string): void { + this._renderer.setElementStyle(this._ngEl, name, val); + } +} diff --git a/modules/angular2/test/directives/ng_style_spec.ts b/modules/angular2/test/directives/ng_style_spec.ts new file mode 100644 index 0000000000..0425143090 --- /dev/null +++ b/modules/angular2/test/directives/ng_style_spec.ts @@ -0,0 +1,126 @@ +import { + AsyncTestCompleter, + beforeEach, + beforeEachBindings, + ddescribe, + xdescribe, + describe, + el, + expect, + iit, + inject, + it, + xit, +} from 'angular2/test_lib'; + +import {StringMapWrapper} from 'angular2/src/facade/collection'; + +import {Component, View} from 'angular2/angular2'; + +import {TestBed} from 'angular2/src/test_lib/test_bed'; +import {DOM} from 'angular2/src/dom/dom_adapter'; +import {NgStyle} from 'angular2/src/directives/ng_style'; + +export function main() { + describe('binding to CSS styles', () => { + + it('should add styles specified in an object literal', + inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => { + var template = `
`; + + tb.createView(TestComponent, {html: template}) + .then((view) => { + view.detectChanges(); + expect(DOM.getStyle(view.rootNodes[0], 'text-align')).toEqual('right'); + + async.done(); + }); + })); + + it('should add and change styles specified in an object expression', + inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => { + var template = `
`; + + tb.createView(TestComponent, {html: template}) + .then((view) => { + var expr: Map; + + view.context.expr = {'text-align': 'right'}; + view.detectChanges(); + expect(DOM.getStyle(view.rootNodes[0], 'text-align')).toEqual('right'); + + expr = view.context.expr; + expr['text-align'] = 'left'; + view.detectChanges(); + expect(DOM.getStyle(view.rootNodes[0], 'text-align')).toEqual('left'); + + async.done(); + }); + })); + + it('should remove styles when deleting a key in an object expression', + inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => { + var template = `
`; + + tb.createView(TestComponent, {html: template}) + .then((view) => { + view.context.expr = {'text-align': 'right'}; + view.detectChanges(); + expect(DOM.getStyle(view.rootNodes[0], 'text-align')).toEqual('right'); + + StringMapWrapper.delete(view.context.expr, 'text-align'); + view.detectChanges(); + expect(DOM.getStyle(view.rootNodes[0], 'text-align')).toEqual(''); + + async.done(); + }); + })); + + it('should co-operate with the style attribute', + inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => { + var template = `
`; + + tb.createView(TestComponent, {html: template}) + .then((view) => { + view.context.expr = {'text-align': 'right'}; + view.detectChanges(); + expect(DOM.getStyle(view.rootNodes[0], 'text-align')).toEqual('right'); + expect(DOM.getStyle(view.rootNodes[0], 'font-size')).toEqual('12px'); + + StringMapWrapper.delete(view.context.expr, 'text-align'); + view.detectChanges(); + expect(DOM.getStyle(view.rootNodes[0], 'text-align')).toEqual(''); + expect(DOM.getStyle(view.rootNodes[0], 'font-size')).toEqual('12px'); + + async.done(); + }); + })); + + it('should co-operate with the style.[styleName]="expr" special-case in the compiler', + inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => { + var template = `
`; + + tb.createView(TestComponent, {html: template}) + .then((view) => { + view.context.expr = {'text-align': 'right'}; + view.detectChanges(); + expect(DOM.getStyle(view.rootNodes[0], 'text-align')).toEqual('right'); + expect(DOM.getStyle(view.rootNodes[0], 'font-size')).toEqual('12px'); + + StringMapWrapper.delete(view.context.expr, 'text-align'); + expect(DOM.getStyle(view.rootNodes[0], 'font-size')).toEqual('12px'); + + view.detectChanges(); + expect(DOM.getStyle(view.rootNodes[0], 'text-align')).toEqual(''); + + async.done(); + }); + })); + }) +} + +@Component({selector: 'test-cmp'}) +@View({directives: [NgStyle]}) +class TestComponent { + expr; +}