From ae35afa9ff9f5db973dd7c60956ac3a2d9577ce2 Mon Sep 17 00:00:00 2001 From: Thomas Burleson Date: Thu, 23 Feb 2017 14:49:50 -0600 Subject: [PATCH] fix(ngStyle, ngClass): StyleDirective should merge styles instead of overwriting all existing * Deprecated use of `style` selectors to favor `ngStyle` selectors since the functionality extends NgStyle directive. * Deprecated use of `class` selectors to favor `ngClass` selectors since the functionality extends NgClass directive. * ngStyle restores default (non-activated) inline styles when breakpoint deactivates * ngStyle merges default inline styles with activated styles * `ngStyle` selectors support Object notations or raw strings (ala html inline styles) * e.g. Object notation = `[ngStyle="{'font-size.px': 10, color: 'rgb(0,0,0)', 'text-align':'left'}"` * e.g. Raw-string notation = `style="font-size:10px; color:black; text-align:left;"` BREAKING CHANGE: * `[style.]` selectors are deprecated in favor of `[ngStyle.]` selectors * `[class.]` selectors are deprecated in favor of `[ngClass.]` selectors * default styles are merged with activated styles ```html
``` ```html
``` Fixes #197. --- .../app/github-issues/DemosGithubIssues.ts | 9 +- .../app/github-issues/issue.197.demo.ts | 58 +++++++ src/lib/flexbox/api/class.spec.ts | 20 +-- src/lib/flexbox/api/class.ts | 89 ++++------ src/lib/flexbox/api/style.spec.ts | 152 +++++++++------- src/lib/flexbox/api/style.ts | 162 +++++++++++------- 6 files changed, 298 insertions(+), 192 deletions(-) create mode 100644 src/demo-app/app/github-issues/issue.197.demo.ts diff --git a/src/demo-app/app/github-issues/DemosGithubIssues.ts b/src/demo-app/app/github-issues/DemosGithubIssues.ts index 5db11f220..1f1d7363b 100644 --- a/src/demo-app/app/github-issues/DemosGithubIssues.ts +++ b/src/demo-app/app/github-issues/DemosGithubIssues.ts @@ -5,8 +5,9 @@ import {Component} from '@angular/core'; template: ` - + + ` }) export class DemosGithubIssues { @@ -19,16 +20,18 @@ import {FlexLayoutModule} from "../../../lib"; // `gulp build:components` to import {DemoIssue5345} from "./issue.5345.demo"; import {DemoIssue9897} from "./issue.9897.demo"; -import {DemoIssue181} from './issue.181.demo'; import {DemoIssue135} from "./issue.135.demo"; +import {DemoIssue181} from './issue.181.demo'; +import {DemoIssue197} from './issue.197.demo'; @NgModule({ declarations: [ DemosGithubIssues, // used by the Router with the root app component DemoIssue5345, DemoIssue9897, + DemoIssue135, DemoIssue181, - DemoIssue135 + DemoIssue197 ], imports: [ CommonModule, diff --git a/src/demo-app/app/github-issues/issue.197.demo.ts b/src/demo-app/app/github-issues/issue.197.demo.ts new file mode 100644 index 000000000..3cf3fcdf3 --- /dev/null +++ b/src/demo-app/app/github-issues/issue.197.demo.ts @@ -0,0 +1,58 @@ +import {Component, OnDestroy} from '@angular/core'; +import {Subscription} from "rxjs/Subscription"; +import 'rxjs/add/operator/filter'; + +import {MediaChange} from "../../../lib/media-query/media-change"; +import {ObservableMedia} from "../../../lib/media-query/observable-media-service"; + +// [ngStyle="{'font-size.px': 10, color: 'rgb(0,0,0)', 'text-align':'left'}" +// style="font-size:10px; color:black; text-align:left;" +@Component({ + selector: 'demo-issue-197', + styleUrls: [ + '../demo-app/material2.css' + ], + template: ` + + + Issue #197 + Responsive Style directive should merge with default inline style: + +
+
+
+ <div fxFlexFill
+     style="font-size:10px; color:black; text-align:left;"
+     [style.md]="{'font-size': '16px', color: 'red'}"
+     ngStyle.lg="font-size: 24px; color : #00f;" >
+ </div> +
+
+
+
+ +
Active mediaQuery: {{ activeMediaQuery }}
+
+
+ ` +}) +export class DemoIssue197 implements OnDestroy { + public activeMediaQuery = ""; + + constructor(media$: ObservableMedia) { + this._watcher = media$.subscribe((change: MediaChange) => { + let value = change ? `'${change.mqAlias}' = (${change.mediaQuery})` : ""; + this.activeMediaQuery = value; + }); + } + + ngOnDestroy() { + this._watcher.unsubscribe(); + } + + private _watcher: Subscription; +} diff --git a/src/lib/flexbox/api/class.spec.ts b/src/lib/flexbox/api/class.spec.ts index 39ee519ae..647c4b49b 100644 --- a/src/lib/flexbox/api/class.spec.ts +++ b/src/lib/flexbox/api/class.spec.ts @@ -57,7 +57,7 @@ describe('class directive', () => { const selector = `class-${mq}`; it(`should apply '${selector}' with '${mq}' media query`, () => { fixture = createTestComponent(` -
+
`); activateMediaQuery(mq, true); @@ -67,7 +67,7 @@ describe('class directive', () => { it('should keep existing class selector', () => { fixture = createTestComponent(` -
+
`); expectNativeEl(fixture).toHaveCssClass('existing-class'); @@ -77,8 +77,8 @@ describe('class directive', () => { it('should allow more than one responsive breakpoint on one element', () => { fixture = createTestComponent(` -
+
`); activateMediaQuery('xs', true); @@ -91,9 +91,9 @@ describe('class directive', () => { it('should work with ngClass object notation', () => { fixture = createTestComponent(` -
-
- `); +
+
+ `); activateMediaQuery('xs', true); expectNativeEl(fixture, {hasXs1: true, hasXs2: false}).toHaveCssClass('xs-1'); expectNativeEl(fixture, {hasXs1: true, hasXs2: false}).not.toHaveCssClass('xs-2'); @@ -104,9 +104,9 @@ describe('class directive', () => { it('should work with ngClass array notation', () => { fixture = createTestComponent(` -
-
- `); +
+
+ `); activateMediaQuery('xs', true); expectNativeEl(fixture).toHaveCssClass('xs-1'); expectNativeEl(fixture).toHaveCssClass('xs-2'); diff --git a/src/lib/flexbox/api/class.ts b/src/lib/flexbox/api/class.ts index fcc5d69fc..0c72ee938 100644 --- a/src/lib/flexbox/api/class.ts +++ b/src/lib/flexbox/api/class.ts @@ -32,64 +32,41 @@ export type NgClassType = string | string[] | Set | {[klass: string]: an */ @Directive({ selector: ` - [class.xs], - [class.gt-xs], - [class.sm], - [class.gt-sm], - [class.md], - [class.gt-md], - [class.lg], - [class.gt-lg], - [class.xl] + [ngClass.xs], [class.xs], + [ngClass.gt-xs], [class.gt-xs], + [ngClass.sm], [class.sm], + [ngClass.gt-sm], [class.gt-sm], + [ngClass.md], [class.md], + [ngClass.gt-md], [class.gt-md], + [ngClass.lg], [class.lg], + [ngClass.gt-lg], [class.gt-lg] ` }) export class ClassDirective extends NgClass implements OnInit, OnChanges, OnDestroy { - @Input('class.xs') - set classXs(val: NgClassType) { - this._base.cacheInput('classXs', val); - } - - @Input('class.gt-xs') - set classGtXs(val: NgClassType) { - this._base.cacheInput('classGtXs', val); - }; - - @Input('class.sm') - set classSm(val: NgClassType) { - this._base.cacheInput('classSm', val); - }; - - @Input('class.gt-sm') - set classGtSm(val: NgClassType) { - this._base.cacheInput('classGtSm', val); - }; - - @Input('class.md') - set classMd(val: NgClassType) { - this._base.cacheInput('classMd', val); - }; - - @Input('class.gt-md') - set classGtMd(val: NgClassType) { - this._base.cacheInput('classGtMd', val); - }; - - @Input('class.lg') - set classLg(val: NgClassType) { - this._base.cacheInput('classLg', val); - }; - - @Input('class.gt-lg') - set classGtLg(val: NgClassType) { - this._base.cacheInput('classGtLg', val); - }; - - @Input('class.xl') - set classXl(val: NgClassType) { - this._base.cacheInput('classXl', val); - }; - + /* tslint:disable */ + @Input('ngClass.xs') set ngClassXs(val: NgClassType) { this._base.cacheInput('classXs', val, true); } + @Input('ngClass.gt-xs') set ngClassGtXs(val: NgClassType) { this._base.cacheInput('classGtXs', val, true); }; + @Input('ngClass.sm') set ngClassSm(val: NgClassType) { this._base.cacheInput('classSm', val, true); }; + @Input('ngClass.gt-sm') set ngClassGtSm(val: NgClassType) { this._base.cacheInput('classGtSm', val, true);} ; + @Input('ngClass.md') set ngClassMd(val: NgClassType) { this._base.cacheInput('classMd', val, true); }; + @Input('ngClass.gt-md') set ngClassGtMd(val: NgClassType) { this._base.cacheInput('classGtMd', val, true);}; + @Input('ngClass.lg') set ngClassLg(val: NgClassType) { this._base.cacheInput('classLg', val, true);}; + @Input('ngClass.gt-lg') set ngClassGtLg(val: NgClassType) { this._base.cacheInput('classGtLg', val, true); }; + @Input('ngClass.xl') set ngClassXl(val: NgClassType) { this._base.cacheInput('classXl', val, true); }; + + /** Deprecated selectors */ + @Input('class.xs') set classXs(val: NgClassType) { this._base.cacheInput('classXs', val, true); } + @Input('class.gt-xs') set classGtXs(val: NgClassType) { this._base.cacheInput('classGtXs', val, true); }; + @Input('class.sm') set classSm(val: NgClassType) { this._base.cacheInput('classSm', val, true); }; + @Input('class.gt-sm') set classGtSm(val: NgClassType) { this._base.cacheInput('classGtSm', val, true); }; + @Input('class.md') set classMd(val: NgClassType) { this._base.cacheInput('classMd', val, true);}; + @Input('class.gt-md') set classGtMd(val: NgClassType) { this._base.cacheInput('classGtMd', val, true);}; + @Input('class.lg') set classLg(val: NgClassType) { this._base.cacheInput('classLg', val, true); }; + @Input('class.gt-lg') set classGtLg(val: NgClassType) { this._base.cacheInput('classGtLg', val, true); }; + @Input('class.xl') set classXl(val: NgClassType) { this._base.cacheInput('classXl', val, true); }; + + /* tslint:enable */ constructor(private monitor: MediaMonitor, private _bpRegistry: BreakPointRegistry, _iterableDiffers: IterableDiffers, _keyValueDiffers: KeyValueDiffers, @@ -102,7 +79,9 @@ export class ClassDirective extends NgClass implements OnInit, OnChanges, OnDest * For @Input changes on the current mq activation property, see onMediaQueryChanges() */ ngOnChanges(changes: SimpleChanges) { - const changed = this._bpRegistry.items.some(it => `class${it.suffix}` in changes); + const changed = this._bpRegistry.items.some(it => { + return (`ngClass${it.suffix}` in changes) || (`class${it.suffix}` in changes); + }); if (changed || this._base.mqActivation) { this._updateStyle(); } diff --git a/src/lib/flexbox/api/style.spec.ts b/src/lib/flexbox/api/style.spec.ts index ea0818bb9..2665d7755 100644 --- a/src/lib/flexbox/api/style.spec.ts +++ b/src/lib/flexbox/api/style.spec.ts @@ -5,24 +5,23 @@ * 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 { - Component, OnInit, Inject -} from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { MockMatchMedia } from '../../media-query/mock/mock-match-media'; -import { MatchMedia } from '../../media-query/match-media'; -import { ObservableMedia } from '../../media-query/observable-media-service'; -import { BreakPointsProvider } from '../../media-query/breakpoints/break-points'; -import { BreakPointRegistry } from '../../media-query/breakpoints/break-point-registry'; - -import { customMatchers } from '../../utils/testing/custom-matchers'; +import {Component} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {ComponentFixture, TestBed} from '@angular/core/testing'; + +import {MockMatchMedia} from '../../media-query/mock/mock-match-media'; +import {MatchMedia} from '../../media-query/match-media'; +import {BreakPointsProvider} from '../../media-query/breakpoints/break-points'; +import {BreakPointRegistry} from '../../media-query/breakpoints/break-point-registry'; + +import {LayoutDirective} from './layout'; +import {StyleDirective} from './style'; +import {MediaQueriesModule} from '../../media-query/_module'; + +import {customMatchers} from '../../utils/testing/custom-matchers'; import { makeCreateTestComponent, expectNativeEl } from '../../utils/testing/helpers'; -import { StyleDirective } from './style'; -import { MediaQueriesModule } from '../../media-query/_module'; describe('style directive', () => { let fixture: ComponentFixture; @@ -38,10 +37,10 @@ describe('style directive', () => { // Configure testbed to prepare services TestBed.configureTestingModule({ imports: [CommonModule, MediaQueriesModule], - declarations: [TestStyleComponent, StyleDirective], + declarations: [TestStyleComponent, LayoutDirective, StyleDirective], providers: [ BreakPointRegistry, BreakPointsProvider, - { provide: MatchMedia, useClass: MockMatchMedia } + {provide: MatchMedia, useClass: MockMatchMedia} ] }); }); @@ -53,62 +52,93 @@ describe('style directive', () => { }); [ - { mq: 'xs', styleStr: "{'font-size': '15px'}", styleObj: { 'font-size': '15px' } }, - { mq: 'sm', styleStr: "{'font-size': '16px'}", styleObj: { 'font-size': '16px' } }, - { mq: 'md', styleStr: "{'font-size': '17px'}", styleObj: { 'font-size': '17px' } }, - { mq: 'lg', styleStr: "{'font-size': '18px'}", styleObj: { 'font-size': '18px' } } + {mq: 'xs', styleStr: "{'font-size': '15px'}", styleObj: {'font-size': '15px'}}, + {mq: 'sm', styleStr: "{'font-size': '16px'}", styleObj: {'font-size': '16px'}}, + {mq: 'md', styleStr: "{'font-size': '17px'}", styleObj: {'font-size': '17px'}}, + {mq: 'lg', styleStr: "{'font-size': '18px'}", styleObj: {'font-size': '18px'}} ] - .forEach(testData => { - it(`should apply '${testData.styleStr}' with '${testData.mq}' media query`, () => { - fixture = createTestComponent(` -
-
- `); - activateMediaQuery(testData.mq, true); - expectNativeEl(fixture).toHaveCssStyle(testData.styleObj); - }); + .forEach(testData => { + it(`should apply '${testData.styleStr}' with '${testData.mq}' media query`, () => { + fixture = createTestComponent(` +
+
+ `); + activateMediaQuery(testData.mq); + expectNativeEl(fixture).toHaveCssStyle(testData.styleObj); }); + }); + + it('should merge with default inline styles', () => { + fixture = createTestComponent(` +
+
+ `); + expectNativeEl(fixture).toHaveCssStyle({color: 'blue'}); + activateMediaQuery('xs'); + expectNativeEl(fixture).toHaveCssStyle({color: 'blue', 'font-size': '15px'}); + }); - it('should override existing styles', () => { +// rgba(252, 41, 41, 0.65) + it('should support raw-string notations', () => { fixture = createTestComponent(` -
-
- `); - expectNativeEl(fixture).toHaveCssStyle({ color: 'blue' }); - activateMediaQuery('xs', true); - expectNativeEl(fixture).toHaveCssStyle({ 'font-size': '15px' }); +
+
+ `); + expectNativeEl(fixture).toHaveCssStyle({color: 'blue'}); + activateMediaQuery('xs'); + expectNativeEl(fixture).toHaveCssStyle({ + 'color': 'blue', + 'font-size': '15px', + 'background-color': 'rgb(252, 41, 41)' + }); }); it('should allow more than one responsive breakpoint on one element', () => { fixture = createTestComponent(` -
-
- `); - activateMediaQuery('xs', true); - expectNativeEl(fixture).toHaveCssStyle({ 'font-size': '16px' }); - expectNativeEl(fixture).not.toHaveCssStyle({ 'font-size': '12px' }); - activateMediaQuery('md', true); - expectNativeEl(fixture).not.toHaveCssStyle({ 'font-size': '16px' }); - expectNativeEl(fixture).toHaveCssStyle({ 'font-size': '12px' }); +
+
+ `); + + fixture.detectChanges(); + + activateMediaQuery('xs'); + expectNativeEl(fixture).toHaveCssStyle({'display': 'flex'}); + expectNativeEl(fixture).toHaveCssStyle({'font-size': '16px'}); + expectNativeEl(fixture).not.toHaveCssStyle({'font-size': '12px'}); + + activateMediaQuery('md'); + expectNativeEl(fixture).not.toHaveCssStyle({'font-size': '16px'}); + expectNativeEl(fixture).toHaveCssStyle({'font-size': '12px'}); + + activateMediaQuery('lg'); + expectNativeEl(fixture).not.toHaveCssStyle({'font-size': '12px'}); + expectNativeEl(fixture).not.toHaveCssStyle({'font-size': '16px'}); + expectNativeEl(fixture).toHaveCssStyle({'font-size': '10px'}); // original is gone + expectNativeEl(fixture).toHaveCssStyle({'margin-left': '13px'}); // portion remains + }); it('should work with special ngStyle px notation', () => { fixture = createTestComponent(` -
-
- `); - activateMediaQuery('xs', true); - expectNativeEl(fixture).toHaveCssStyle({ 'font-size': '15px' }); +
+
+ `); + activateMediaQuery('xs'); + expectNativeEl(fixture).toHaveCssStyle({'font-size': '15px'}); }); it('should work with bound values', () => { fixture = createTestComponent(` -
-
- `); - activateMediaQuery('xs', true); - expectNativeEl(fixture, { fontSize: 19 }).toHaveCssStyle({ 'font-size': '19px' }); +
+
+ `); + activateMediaQuery('xs'); + expectNativeEl(fixture, {fontSize: 19}).toHaveCssStyle({'font-size': '19px'}); }); }); @@ -120,14 +150,8 @@ describe('style directive', () => { selector: 'test-style-api', template: `PlaceHolder Template HTML` }) -export class TestStyleComponent implements OnInit { +export class TestStyleComponent { fontSize: number; - - constructor( @Inject(ObservableMedia) private media) { - } - - ngOnInit() { - } } diff --git a/src/lib/flexbox/api/style.ts b/src/lib/flexbox/api/style.ts index b63d23dcf..e2c21360d 100644 --- a/src/lib/flexbox/api/style.ts +++ b/src/lib/flexbox/api/style.ts @@ -11,10 +11,10 @@ import { Input, OnDestroy, OnInit, - Renderer, OnChanges, - SimpleChanges, - KeyValueDiffers + Renderer, + KeyValueDiffers, + SimpleChanges } from '@angular/core'; import {NgStyle} from '@angular/common'; @@ -22,9 +22,11 @@ import {BaseFxDirectiveAdapter} from './base-adapter'; import {BreakPointRegistry} from './../../media-query/breakpoints/break-point-registry'; import {MediaChange} from '../../media-query/media-change'; import {MediaMonitor} from '../../media-query/media-monitor'; +import {extendObject} from '../../utils/object-extend'; /** NgStyle allowed inputs **/ -export type NgStyleType = string | string[] | Set | {[klass: string]: any}; +export type ngStyleMap = {[klass: string]: string}; +export type NgStyleType = string | string[] | Set | ngStyleMap; /** * Directive to add responsive support for ngStyle. @@ -32,79 +34,70 @@ export type NgStyleType = string | string[] | Set | {[klass: string]: an */ @Directive({ selector: ` - [style.xs], - [style.gt-xs], - [style.sm], - [style.gt-sm], - [style.md], - [style.gt-md], - [style.lg], - [style.gt-lg], - [style.xl] + [ngStyle], + [ngStyle.xs], [style.xs], + [ngStyle.gt-xs], [style.gt-xs], + [ngStyle.sm], [style.sm], + [ngStyle.gt-sm], [style.gt-sm], + [ngStyle.md], [style.md], + [ngStyle.gt-md], [style.gt-md], + [ngStyle.lg], [style.lg], + [ngStyle.gt-lg], [style.gt-lg], + [ngStyle.xl], [style.xl] ` }) export class StyleDirective extends NgStyle implements OnInit, OnChanges, OnDestroy { - @Input('style.xs') - set styleXs(val: NgStyleType) { - this._base.cacheInput('styleXs', val, true); + @Input('ngStyle') + set styleBase(val: NgStyleType) { + this._base.cacheInput('style', val, true); + this.ngStyle = this._base.inputMap['style']; } - @Input('style.gt-xs') - set styleGtXs(val: NgStyleType) { - this._base.cacheInput('styleGtXs', val, true); - }; - - @Input('style.sm') - set styleSm(val: NgStyleType) { - this._base.cacheInput('styleSm', val, true); - }; - - @Input('style.gt-sm') - set styleGtSm(val: NgStyleType) { - this._base.cacheInput('styleGtSm', val, true); - }; - - @Input('style.md') - set styleMd(val: NgStyleType) { - this._base.cacheInput('styleMd', val, true); - }; - - @Input('style.gt-md') - set styleGtMd(val: NgStyleType) { - this._base.cacheInput('styleGtMd', val, true); - }; - - @Input('style.lg') - set styleLg(val: NgStyleType) { - this._base.cacheInput('styleLg', val, true); - }; - - @Input('style.gt-lg') - set styleGtLg(val: NgStyleType) { - this._base.cacheInput('styleGtLg', val, true); - }; - - @Input('style.xl') - set styleXl(val: NgStyleType) { - this._base.cacheInput('styleXl', val, true); - }; - + /* tslint:disable */ + @Input('ngStyle.xs') set ngStyleXs(val: NgStyleType) { this._base.cacheInput('styleXs', val, true); } + @Input('ngStyle.gt-xs') set ngStyleGtXs(val: NgStyleType) { this._base.cacheInput('styleGtXs', val, true); }; + @Input('ngStyle.sm') set ngStyleSm(val: NgStyleType) { this._base.cacheInput('styleSm', val, true); }; + @Input('ngStyle.gt-sm') set ngStyleGtSm(val: NgStyleType) { this._base.cacheInput('styleGtSm', val, true);} ; + @Input('ngStyle.md') set ngStyleMd(val: NgStyleType) { this._base.cacheInput('styleMd', val, true); }; + @Input('ngStyle.gt-md') set ngStyleGtMd(val: NgStyleType) { this._base.cacheInput('styleGtMd', val, true);}; + @Input('ngStyle.lg') set ngStyleLg(val: NgStyleType) { this._base.cacheInput('styleLg', val, true);}; + @Input('ngStyle.gt-lg') set ngStyleGtLg(val: NgStyleType) { this._base.cacheInput('styleGtLg', val, true); }; + @Input('ngStyle.xl') set ngStyleXl(val: NgStyleType) { this._base.cacheInput('styleXl', val, true); }; + + /** Deprecated selectors */ + @Input('style.xs') set styleXs(val: NgStyleType) { this._base.cacheInput('styleXs', val, true); } + @Input('style.gt-xs') set styleGtXs(val: NgStyleType) { this._base.cacheInput('styleGtXs', val, true); }; + @Input('style.sm') set styleSm(val: NgStyleType) { this._base.cacheInput('styleSm', val, true); }; + @Input('style.gt-sm') set styleGtSm(val: NgStyleType) { this._base.cacheInput('styleGtSm', val, true); }; + @Input('style.md') set styleMd(val: NgStyleType) { this._base.cacheInput('styleMd', val, true);}; + @Input('style.gt-md') set styleGtMd(val: NgStyleType) { this._base.cacheInput('styleGtMd', val, true);}; + @Input('style.lg') set styleLg(val: NgStyleType) { this._base.cacheInput('styleLg', val, true); }; + @Input('style.gt-lg') set styleGtLg(val: NgStyleType) { this._base.cacheInput('styleGtLg', val, true); }; + @Input('style.xl') set styleXl(val: NgStyleType) { this._base.cacheInput('styleXl', val, true); }; + + /* tslint:enable */ /** - * + * Constructor for the ngStyle subclass; which adds selectors and + * a MediaQuery Activation Adapter */ constructor(private monitor: MediaMonitor, private _bpRegistry: BreakPointRegistry, _differs: KeyValueDiffers, _ngEl: ElementRef, _renderer: Renderer) { super(_differs, _ngEl, _renderer); - this._base = new BaseFxDirectiveAdapter(monitor, _ngEl, _renderer); + this._buildAdapter(monitor, _ngEl, _renderer); + + // Get current inline style if any + this._base.cacheInput('style', _ngEl.nativeElement.getAttribute("style"), true); } /** * For @Input changes on the current mq activation property, see onMediaQueryChanges() */ ngOnChanges(changes: SimpleChanges) { - const changed = this._bpRegistry.items.some(it => `style${it.suffix}` in changes); + const changed = this._bpRegistry.items.some(it => { + return (`ngStyle${it.suffix}` in changes) || (`style${it.suffix}` in changes); + }); if (changed || this._base.mqActivation) { this._updateStyle(); } @@ -118,22 +111,71 @@ export class StyleDirective extends NgStyle implements OnInit, OnChanges, OnDest this._base.listenForMediaQueryChanges('style', '', (changes: MediaChange) => { this._updateStyle(changes.value); }); - this._updateStyle(); } ngOnDestroy() { this._base.ngOnDestroy(); } + // ************************************************************************ + // Private Internal Methods + // ************************************************************************ + + /** + * Use the currently activated input property and assign to + * `ngStyle` which does the style injections... + */ private _updateStyle(value?: NgStyleType) { let style = value || this._base.queryInput("style") || ''; if (this._base.mqActivation) { style = this._base.mqActivation.activatedInput; } + // Delegate subsequent activity to the NgStyle logic this.ngStyle = style; } + + /** + * Build MediaQuery Activation Adapter + * This adapter manages listening to mediaQuery change events and identifying + * which property value should be used for the style update + */ + private _buildAdapter(monitor: MediaMonitor, _ngEl: ElementRef, _renderer: Renderer) { + this._base = new BaseFxDirectiveAdapter(monitor, _ngEl, _renderer); + + // Build intercept to convert raw strings to ngStyleMap + let cacheInput = this._base.cacheInput.bind(this._base); + this._base.cacheInput = (key?: string, source?: any, cacheRaw = false, merge = true) => { + let styles = this._buildStyleMap(source); + if (merge) { + styles = extendObject({}, this._base.inputMap['style'], styles); + } + cacheInput(key, styles, cacheRaw); + }; + } + + /** + * Convert raw strings to ngStyleMap; which is required by ngStyle + */ + private _buildStyleMap(styles: NgStyleType) { + if (typeof styles === 'string') { + let value = styles; + return value.replace(",", ";").split(";") + .map((it: string) => { + let [key, val] = it.split(":"); + return val ? [`${(key as string).replace("'","").trim()}`, val.trim()] : null; + }) + .filter(it => it !== null) + .reduce((map, it) => { + let [key, val] = it; + map[key] = val; + return map; + }, {}); + } + return styles; + } + /** * Special adapter to cross-cut responsive behaviors * into the StyleDirective