Skip to content
This repository was archived by the owner on Jan 6, 2025. It is now read-only.

Commit eb22fe5

Browse files
ThomasBurlesonkara
authored andcommitted
fix(ngStyle, ngClass): StyleDirective security fixes & merge activated styles (#198)
BREAKING CHANGE: * `[style.<alias>]` selectors are deprecated in favor of `[ngStyle.<alias>]` selectors * `[class.<alias>]` selectors are deprecated in favor of `[ngClass.<alias>]` selectors * default styles are merged with activated styles ```html <div fxLayout [class.xs]="['xs-1', 'xs-2']" [style]="{'font-size': '10px', 'margin-left' : '13px'}" [style.xs]="{'font-size': '16px'}" [style.md]="{'font-size': '12px'}"> </div> ``` ```html <div fxLayout [ngClass.xs]="['xs-1', 'xs-2']" [ngStyle]="{'font-size': '10px', 'margin-left' : '13px'}" [ngStyle.xs]="{'font-size': '16px'}" [ngStyle.md]="{'font-size': '12px'}"> </div> ``` Fixes #197.
1 parent 10e1230 commit eb22fe5

File tree

11 files changed

+542
-210
lines changed

11 files changed

+542
-210
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@
131131
"ts-node": "^0.7.3",
132132
"tslint": "^4.2.0",
133133
"tslint-loader": "^3.3.0",
134-
"typescript": "2.0.10",
134+
"typescript": "^2.0.10",
135135
"url-loader": "^0.5.7",
136136
"webpack": "2.2.0-rc.3",
137137
"webpack-bundle-analyzer": "^2.2.0",

src/demo-app/app/github-issues/DemosGithubIssues.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import {Component} from '@angular/core';
55
template: `
66
<demo-issue-5345></demo-issue-5345>
77
<demo-issue-9897></demo-issue-9897>
8-
<demo-issue-181></demo-issue-181>
98
<demo-issue-135> </demo-issue-135>
9+
<demo-issue-181></demo-issue-181>
10+
<demo-issue-197></demo-issue-197>
1011
`
1112
})
1213
export class DemosGithubIssues {
@@ -19,16 +20,18 @@ import {FlexLayoutModule} from "../../../lib"; // `gulp build:components` to
1920

2021
import {DemoIssue5345} from "./issue.5345.demo";
2122
import {DemoIssue9897} from "./issue.9897.demo";
22-
import {DemoIssue181} from './issue.181.demo';
2323
import {DemoIssue135} from "./issue.135.demo";
24+
import {DemoIssue181} from './issue.181.demo';
25+
import {DemoIssue197} from './issue.197.demo';
2426

2527
@NgModule({
2628
declarations: [
2729
DemosGithubIssues, // used by the Router with the root app component
2830
DemoIssue5345,
2931
DemoIssue9897,
32+
DemoIssue135,
3033
DemoIssue181,
31-
DemoIssue135
34+
DemoIssue197
3235
],
3336
imports: [
3437
CommonModule,
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import {Component, OnDestroy} from '@angular/core';
2+
import {Subscription} from "rxjs/Subscription";
3+
import 'rxjs/add/operator/filter';
4+
5+
import {MediaChange} from "../../../lib/media-query/media-change";
6+
import {ObservableMedia} from "../../../lib/media-query/observable-media-service";
7+
8+
// [ngStyle="{'font-size.px': 10, color: 'rgb(0,0,0)', 'text-align':'left'}"
9+
// style="font-size:10px; color:black; text-align:left;"
10+
@Component({
11+
selector: 'demo-issue-197',
12+
styleUrls: [
13+
'../demo-app/material2.css'
14+
],
15+
template: `
16+
17+
<md-card class="card-demo" >
18+
<md-card-title><a href="https://github.com/angular/flex-layout/issues/197" target="_blank">Issue #197</a></md-card-title>
19+
<md-card-subtitle>Responsive Style directive should merge with default inline style:</md-card-subtitle>
20+
<md-card-content>
21+
<div class="containerX">
22+
<div class="coloredContainerX box fixed">
23+
<div class="box1"
24+
fxFlexFill
25+
style="font-size:12px; color:black; text-align:left;"
26+
[style.sm]="{'font-size': '16px', color: '#a63db8', 'text-align': 'center'}"
27+
ngStyle.md="font-size: 24px; color : #0000ff; text-align: right;">
28+
&lt;div fxFlexFill <br/>
29+
&nbsp;&nbsp;&nbsp;&nbsp;style="font-size:10px; color:black; text-align:'left';"<br/>
30+
&nbsp;&nbsp;&nbsp;&nbsp;[style.sm]="&#123;'font-size':'16px', color:#a63db8, text-align:'center' &#125;"<br/>
31+
&nbsp;&nbsp;&nbsp;&nbsp;ngStyle.md="font-size:24px; color:#00f;" text-align:'right'&gt;<br/>
32+
&lt;/div&gt;
33+
</div>
34+
</div>
35+
</div>
36+
</md-card-content>
37+
<md-card-footer style="width:95%;padding-left:20px;margin-top:-5px;">
38+
<div class="hint" >Active mediaQuery: <span style="padding-left: 20px; color: rgba(0, 0, 0, 0.54)">{{ activeMediaQuery }}</span></div>
39+
</md-card-footer>
40+
</md-card>
41+
`
42+
})
43+
export class DemoIssue197 implements OnDestroy {
44+
public activeMediaQuery = "";
45+
46+
constructor(media$: ObservableMedia) {
47+
this._watcher = media$.subscribe((change: MediaChange) => {
48+
let value = change ? `'${change.mqAlias}' = (${change.mediaQuery})` : "";
49+
this.activeMediaQuery = value;
50+
});
51+
}
52+
53+
ngOnDestroy() {
54+
this._watcher.unsubscribe();
55+
}
56+
57+
private _watcher: Subscription;
58+
}

src/lib/flexbox/api/class.spec.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -57,44 +57,49 @@ describe('class directive', () => {
5757
const selector = `class-${mq}`;
5858
it(`should apply '${selector}' with '${mq}' media query`, () => {
5959
fixture = createTestComponent(`
60-
<div class.${mq}="${selector}">
60+
<div ngClass.${mq}="${selector}">
6161
</div>
6262
`);
63-
activateMediaQuery(mq, true);
63+
activateMediaQuery(mq);
6464
expectNativeEl(fixture).toHaveCssClass(selector);
6565
});
6666
});
6767

6868
it('should keep existing class selector', () => {
6969
fixture = createTestComponent(`
70-
<div class="existing-class" class.xs="xs-class">
71-
</div>
72-
`);
70+
<div class="existing-class" ngClass.xs="xs-class">
71+
</div>
72+
`);
73+
7374
expectNativeEl(fixture).toHaveCssClass('existing-class');
74-
activateMediaQuery('xs', true);
75+
activateMediaQuery('xs');
76+
expectNativeEl(fixture).toHaveCssClass('existing-class');
77+
78+
activateMediaQuery('lg');
7579
expectNativeEl(fixture).toHaveCssClass('existing-class');
80+
expectNativeEl(fixture).not.toHaveCssClass('xs-class');
7681
});
7782

7883
it('should allow more than one responsive breakpoint on one element', () => {
7984
fixture = createTestComponent(`
80-
<div class.xs="xs-class"
81-
class.md="md-class">
85+
<div ngClass.xs="xs-class"
86+
ngClass.md="md-class">
8287
</div>
8388
`);
84-
activateMediaQuery('xs', true);
89+
activateMediaQuery('xs');
8590
expectNativeEl(fixture).toHaveCssClass('xs-class');
8691
expectNativeEl(fixture).not.toHaveCssClass('md-class');
87-
activateMediaQuery('md', true);
92+
activateMediaQuery('md');
8893
expectNativeEl(fixture).not.toHaveCssClass('xs-class');
8994
expectNativeEl(fixture).toHaveCssClass('md-class');
9095
});
9196

9297
it('should work with ngClass object notation', () => {
9398
fixture = createTestComponent(`
94-
<div [class.xs]="{'xs-1': hasXs1, 'xs-2': hasXs2}">
95-
</div>
96-
`);
97-
activateMediaQuery('xs', true);
99+
<div [ngClass.xs]="{'xs-1': hasXs1, 'xs-2': hasXs2}">
100+
</div>
101+
`);
102+
activateMediaQuery('xs');
98103
expectNativeEl(fixture, {hasXs1: true, hasXs2: false}).toHaveCssClass('xs-1');
99104
expectNativeEl(fixture, {hasXs1: true, hasXs2: false}).not.toHaveCssClass('xs-2');
100105

@@ -104,10 +109,10 @@ describe('class directive', () => {
104109

105110
it('should work with ngClass array notation', () => {
106111
fixture = createTestComponent(`
107-
<div [class.xs]="['xs-1', 'xs-2']">
108-
</div>
109-
`);
110-
activateMediaQuery('xs', true);
112+
<div [ngClass.xs]="['xs-1', 'xs-2']">
113+
</div>
114+
`);
115+
activateMediaQuery('xs');
111116
expectNativeEl(fixture).toHaveCssClass('xs-1');
112117
expectNativeEl(fixture).toHaveCssClass('xs-2');
113118
});

src/lib/flexbox/api/class.ts

Lines changed: 38 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -32,66 +32,43 @@ export type NgClassType = string | string[] | Set<string> | {[klass: string]: an
3232
*/
3333
@Directive({
3434
selector: `
35-
[class.xs],
36-
[class.gt-xs],
37-
[class.sm],
38-
[class.gt-sm],
39-
[class.md],
40-
[class.gt-md],
41-
[class.lg],
42-
[class.gt-lg],
43-
[class.xl]
35+
[ngClass.xs], [class.xs],
36+
[ngClass.gt-xs], [class.gt-xs],
37+
[ngClass.sm], [class.sm],
38+
[ngClass.gt-sm], [class.gt-sm],
39+
[ngClass.md], [class.md],
40+
[ngClass.gt-md], [class.gt-md],
41+
[ngClass.lg], [class.lg],
42+
[ngClass.gt-lg], [class.gt-lg]
4443
`
4544
})
4645
export class ClassDirective extends NgClass implements OnInit, OnChanges, OnDestroy {
4746

48-
@Input('class.xs')
49-
set classXs(val: NgClassType) {
50-
this._base.cacheInput('classXs', val);
51-
}
52-
53-
@Input('class.gt-xs')
54-
set classGtXs(val: NgClassType) {
55-
this._base.cacheInput('classGtXs', val);
56-
};
57-
58-
@Input('class.sm')
59-
set classSm(val: NgClassType) {
60-
this._base.cacheInput('classSm', val);
61-
};
62-
63-
@Input('class.gt-sm')
64-
set classGtSm(val: NgClassType) {
65-
this._base.cacheInput('classGtSm', val);
66-
};
67-
68-
@Input('class.md')
69-
set classMd(val: NgClassType) {
70-
this._base.cacheInput('classMd', val);
71-
};
72-
73-
@Input('class.gt-md')
74-
set classGtMd(val: NgClassType) {
75-
this._base.cacheInput('classGtMd', val);
76-
};
77-
78-
@Input('class.lg')
79-
set classLg(val: NgClassType) {
80-
this._base.cacheInput('classLg', val);
81-
};
82-
83-
@Input('class.gt-lg')
84-
set classGtLg(val: NgClassType) {
85-
this._base.cacheInput('classGtLg', val);
86-
};
87-
88-
@Input('class.xl')
89-
set classXl(val: NgClassType) {
90-
this._base.cacheInput('classXl', val);
91-
};
92-
93-
constructor(private monitor: MediaMonitor,
94-
private _bpRegistry: BreakPointRegistry,
47+
/* tslint:disable */
48+
@Input('ngClass.xs') set ngClassXs(val: NgClassType) { this._base.cacheInput('classXs', val, true); }
49+
@Input('ngClass.gt-xs') set ngClassGtXs(val: NgClassType) { this._base.cacheInput('classGtXs', val, true); };
50+
@Input('ngClass.sm') set ngClassSm(val: NgClassType) { this._base.cacheInput('classSm', val, true); };
51+
@Input('ngClass.gt-sm') set ngClassGtSm(val: NgClassType) { this._base.cacheInput('classGtSm', val, true);} ;
52+
@Input('ngClass.md') set ngClassMd(val: NgClassType) { this._base.cacheInput('classMd', val, true); };
53+
@Input('ngClass.gt-md') set ngClassGtMd(val: NgClassType) { this._base.cacheInput('classGtMd', val, true);};
54+
@Input('ngClass.lg') set ngClassLg(val: NgClassType) { this._base.cacheInput('classLg', val, true);};
55+
@Input('ngClass.gt-lg') set ngClassGtLg(val: NgClassType) { this._base.cacheInput('classGtLg', val, true); };
56+
@Input('ngClass.xl') set ngClassXl(val: NgClassType) { this._base.cacheInput('classXl', val, true); };
57+
58+
/** Deprecated selectors */
59+
@Input('class.xs') set classXs(val: NgClassType) { this._base.cacheInput('classXs', val, true); }
60+
@Input('class.gt-xs') set classGtXs(val: NgClassType) { this._base.cacheInput('classGtXs', val, true); };
61+
@Input('class.sm') set classSm(val: NgClassType) { this._base.cacheInput('classSm', val, true); };
62+
@Input('class.gt-sm') set classGtSm(val: NgClassType) { this._base.cacheInput('classGtSm', val, true); };
63+
@Input('class.md') set classMd(val: NgClassType) { this._base.cacheInput('classMd', val, true);};
64+
@Input('class.gt-md') set classGtMd(val: NgClassType) { this._base.cacheInput('classGtMd', val, true);};
65+
@Input('class.lg') set classLg(val: NgClassType) { this._base.cacheInput('classLg', val, true); };
66+
@Input('class.gt-lg') set classGtLg(val: NgClassType) { this._base.cacheInput('classGtLg', val, true); };
67+
@Input('class.xl') set classXl(val: NgClassType) { this._base.cacheInput('classXl', val, true); };
68+
69+
/* tslint:enable */
70+
constructor(protected monitor: MediaMonitor,
71+
protected _bpRegistry: BreakPointRegistry,
9572
_iterableDiffers: IterableDiffers, _keyValueDiffers: KeyValueDiffers,
9673
_ngEl: ElementRef, _renderer: Renderer) {
9774
super(_iterableDiffers, _keyValueDiffers, _ngEl, _renderer);
@@ -102,7 +79,9 @@ export class ClassDirective extends NgClass implements OnInit, OnChanges, OnDest
10279
* For @Input changes on the current mq activation property, see onMediaQueryChanges()
10380
*/
10481
ngOnChanges(changes: SimpleChanges) {
105-
const changed = this._bpRegistry.items.some(it => `class${it.suffix}` in changes);
82+
const changed = this._bpRegistry.items.some(it => {
83+
return (`ngClass${it.suffix}` in changes) || (`class${it.suffix}` in changes);
84+
});
10685
if (changed || this._base.mqActivation) {
10786
this._updateStyle();
10887
}
@@ -123,7 +102,7 @@ export class ClassDirective extends NgClass implements OnInit, OnChanges, OnDest
123102
this._base.ngOnDestroy();
124103
}
125104

126-
private _updateStyle(value?: NgClassType) {
105+
protected _updateStyle(value?: NgClassType) {
127106
let clazz = value || this._base.queryInput("class") || '';
128107
if (this._base.mqActivation) {
129108
clazz = this._base.mqActivation.activatedInput;
@@ -136,6 +115,6 @@ export class ClassDirective extends NgClass implements OnInit, OnChanges, OnDest
136115
* Special adapter to cross-cut responsive behaviors
137116
* into the ClassDirective
138117
*/
139-
private _base: BaseFxDirectiveAdapter;
118+
protected _base: BaseFxDirectiveAdapter;
140119
}
141120

0 commit comments

Comments
 (0)