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

Commit 1f62f0c

Browse files
fix(lib, media-query): use getDom() for platform-server/universal fixes
Now gets document object from platform-browser by DI instead of global. > Thx to @ardatan for PR #346. Fixes #187. Closes #346.
1 parent 21c34f7 commit 1f62f0c

25 files changed

+635
-74
lines changed

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ jobs:
2020
include:
2121
- env: "MODE=lint"
2222
- env: "MODE=aot"
23+
- env: "MODE=prerender"
2324
- env: "MODE=closure-compiler"
2425
- env: "MODE=saucelabs_required"
2526
- env: "MODE=browserstack_required"

package.json

+7-6
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,18 @@
99
"url": "git+https://github.com/angular/flex-layout.git"
1010
},
1111
"scripts": {
12+
"api": "gulp api-docs",
1213
"build": "gulp :publish:build-releases",
1314
"demo-app": "gulp serve:devapp",
14-
"test": "gulp test",
15-
"tslint": "gulp lint",
16-
"stylelint": "gulp lint",
17-
"e2e": "gulp e2e",
15+
"docs": "gulp docs",
1816
"deploy": "gulp deploy:devapp",
1917
"stage": "gulp stage-deploy:devapp",
18+
"stylelint": "gulp lint",
19+
"test": "gulp test",
20+
"tslint": "gulp lint",
2021
"webdriver-manager": "webdriver-manager",
21-
"docs": "gulp docs",
22-
"api": "gulp api-docs"
22+
"universal": "gulp universal:build",
23+
"universal:test": "gulp ci:prerender"
2324
},
2425
"version": "2.0.0-beta.8",
2526
"license": "MIT",

scripts/ci/travis-testing.sh

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ elif is_aot; then
3939
$(npm bin)/gulp ci:aot
4040
elif is_unit; then
4141
$(npm bin)/gulp ci:test
42+
elif is_prerender; then
43+
$(npm bin)/gulp ci:prerender
4244
elif is_closure_compiler; then
4345
./scripts/closure-compiler/build-devapp-bundle.sh
4446
fi

src/demo-app/app/github-issues/splitter/split-handle.directive.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {Directive, ElementRef, Output} from '@angular/core';
2+
23
import {Observable} from 'rxjs/Observable';
34

45
import 'rxjs/add/observable/fromEvent';
@@ -19,13 +20,14 @@ export class SplitHandleDirective {
1920

2021
constructor(ref: ElementRef) {
2122
const getMouseEventPosition = (event: MouseEvent) => ({x: event.movementX, y: event.movementY});
23+
const document = ref.nativeElement.ownerDocument;
2224

23-
const mouseup$ = Observable.fromEvent(document, 'mouseup');
24-
const mousemove$ = Observable.fromEvent(document, 'mousemove')
25-
.map(getMouseEventPosition);
26-
const mousedown$ = Observable.fromEvent(ref.nativeElement, 'mousedown')
27-
.map(getMouseEventPosition);
25+
/* tslint:disable */
26+
const mousedown$ = Observable.fromEvent(ref.nativeElement, 'mousedown').map(getMouseEventPosition);
27+
const mousemove$ = Observable.fromEvent(document, 'mousemove').map(getMouseEventPosition);
28+
const mouseup$ = Observable.fromEvent(document, 'mouseup').map(getMouseEventPosition);
2829

30+
/* tslint:enable */
2931
this.drag = mousedown$.switchMap(_ => mousemove$.takeUntil(mouseup$));
3032
}
3133

src/lib/flexbox/api/base.ts

+18-10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
ElementRef, OnDestroy, SimpleChanges, OnChanges,
1010
SimpleChange, Renderer
1111
} from '@angular/core';
12+
import {ɵgetDOM as getDom} from '@angular/platform-browser';
1213

1314
import {applyCssPrefixes} from '../../utils/auto-prefixer';
1415
import {buildLayoutCSS} from '../../utils/layout-validator';
@@ -117,20 +118,27 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges {
117118
*/
118119
protected _getDisplayStyle(source?: HTMLElement): string {
119120
let element: HTMLElement = source || this._elementRef.nativeElement;
120-
let value = (element.style as any)['display'] || getComputedStyle(element)['display'];
121-
return value ? value.trim() : 'block';
121+
let value = '';
122+
try {
123+
let immediateValue = getDom().getStyle(element, 'display');
124+
value = immediateValue || getDom().getComputedStyle(element).display;
125+
} catch (e) {
126+
// TODO: platform-server throws an exception for getComputedStyle
127+
}
128+
129+
return value ? value.trim() : ((element.nodeType === 1) ? 'block' : 'inline-block');
122130
}
123131

124132
protected _getFlowDirection(target: any, addIfMissing = false): string {
125133
let value = '';
126134
if (target) {
127-
let directionKeys = Object.keys(applyCssPrefixes({'flex-direction': ''}));
128-
let findDirection = (styles) => directionKeys.reduce((direction, key) => {
129-
return direction || styles[key];
130-
}, null);
135+
let immediateValue = getDom().getStyle(target, 'flex-direction');
136+
try {
137+
value = immediateValue || getDom().getComputedStyle(target)['flex-direction'];
138+
} catch (e) {
139+
// TODO: platform-server throws an exception for getComputedStyle
140+
}
131141

132-
let immediateValue = findDirection(target.style);
133-
value = immediateValue || findDirection(getComputedStyle(target as Element));
134142
if (!immediateValue && addIfMissing) {
135143
value = value || 'row';
136144
this._applyStyleToElements(buildLayoutCSS(value), [target]);
@@ -220,11 +228,11 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges {
220228
* Special accessor to query for all child 'element' nodes regardless of type, class, etc.
221229
*/
222230
protected get childrenNodes() {
223-
const obj = this._elementRef.nativeElement.childNodes;
231+
const obj = this._elementRef.nativeElement.children;
224232
const buffer = [];
225233

226234
// iterate backwards ensuring that length is an UInt32
227-
for ( let i = obj.length; i--; ) {
235+
for (let i = obj.length; i--; ) {
228236
buffer[i] = obj[i];
229237
}
230238
return buffer;

src/lib/flexbox/api/layout-gap.ts

+23-19
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ import {LayoutDirective} from './layout';
2424
import {MediaChange} from '../../media-query/media-change';
2525
import {MediaMonitor} from '../../media-query/media-monitor';
2626
import {LAYOUT_VALUES} from '../../utils/layout-validator';
27+
2728
/**
2829
* 'layout-padding' styling directive
2930
* Defines padding of child elements in a layout container
3031
*/
31-
@Directive({selector: `
32+
@Directive({
33+
selector: `
3234
[fxLayoutGap],
3335
[fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl],
3436
[fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl],
@@ -42,22 +44,22 @@ export class LayoutGapDirective extends BaseFxDirective implements AfterContentI
4244
protected _observer: MutationObserver;
4345

4446
/* tslint:disable */
45-
@Input('fxLayoutGap') set gap(val) { this._cacheInput('gap', val); }
46-
@Input('fxLayoutGap.xs') set gapXs(val) { this._cacheInput('gapXs', val); }
47-
@Input('fxLayoutGap.sm') set gapSm(val) { this._cacheInput('gapSm', val); };
48-
@Input('fxLayoutGap.md') set gapMd(val) { this._cacheInput('gapMd', val); };
49-
@Input('fxLayoutGap.lg') set gapLg(val) { this._cacheInput('gapLg', val); };
50-
@Input('fxLayoutGap.xl') set gapXl(val) { this._cacheInput('gapXl', val); };
51-
52-
@Input('fxLayoutGap.gt-xs') set gapGtXs(val) { this._cacheInput('gapGtXs', val); };
53-
@Input('fxLayoutGap.gt-sm') set gapGtSm(val) { this._cacheInput('gapGtSm', val); };
54-
@Input('fxLayoutGap.gt-md') set gapGtMd(val) { this._cacheInput('gapGtMd', val); };
55-
@Input('fxLayoutGap.gt-lg') set gapGtLg(val) { this._cacheInput('gapGtLg', val); };
56-
57-
@Input('fxLayoutGap.lt-sm') set gapLtSm(val) { this._cacheInput('gapLtSm', val); };
58-
@Input('fxLayoutGap.lt-md') set gapLtMd(val) { this._cacheInput('gapLtMd', val); };
59-
@Input('fxLayoutGap.lt-lg') set gapLtLg(val) { this._cacheInput('gapLtLg', val); };
60-
@Input('fxLayoutGap.lt-xl') set gapLtXl(val) { this._cacheInput('gapLtXl', val); };
47+
@Input('fxLayoutGap') set gap(val) { this._cacheInput('gap', val); }
48+
@Input('fxLayoutGap.xs') set gapXs(val) { this._cacheInput('gapXs', val); }
49+
@Input('fxLayoutGap.sm') set gapSm(val) { this._cacheInput('gapSm', val); };
50+
@Input('fxLayoutGap.md') set gapMd(val) { this._cacheInput('gapMd', val); };
51+
@Input('fxLayoutGap.lg') set gapLg(val) { this._cacheInput('gapLg', val); };
52+
@Input('fxLayoutGap.xl') set gapXl(val) { this._cacheInput('gapXl', val); };
53+
54+
@Input('fxLayoutGap.gt-xs') set gapGtXs(val) { this._cacheInput('gapGtXs', val); };
55+
@Input('fxLayoutGap.gt-sm') set gapGtSm(val) { this._cacheInput('gapGtSm', val); };
56+
@Input('fxLayoutGap.gt-md') set gapGtMd(val) { this._cacheInput('gapGtMd', val); };
57+
@Input('fxLayoutGap.gt-lg') set gapGtLg(val) { this._cacheInput('gapGtLg', val); };
58+
59+
@Input('fxLayoutGap.lt-sm') set gapLtSm(val) { this._cacheInput('gapLtSm', val); };
60+
@Input('fxLayoutGap.lt-md') set gapLtMd(val) { this._cacheInput('gapLtMd', val); };
61+
@Input('fxLayoutGap.lt-lg') set gapLtLg(val) { this._cacheInput('gapLtLg', val); };
62+
@Input('fxLayoutGap.lt-xl') set gapLtXl(val) { this._cacheInput('gapLtXl', val); };
6163

6264
/* tslint:enable */
6365
constructor(monitor: MediaMonitor,
@@ -124,8 +126,10 @@ export class LayoutGapDirective extends BaseFxDirective implements AfterContentI
124126
}
125127
};
126128

127-
this._observer = new MutationObserver(onMutationCallback);
128-
this._observer.observe(this._elementRef.nativeElement, {childList: true});
129+
if (typeof MutationObserver !== 'undefined') {
130+
this._observer = new MutationObserver(onMutationCallback);
131+
this._observer.observe(this._elementRef.nativeElement, {childList: true});
132+
}
129133
}
130134

131135
/**

src/lib/media-query/match-media.ts

+20-14
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import {Injectable, NgZone} from '@angular/core';
9-
8+
import {Inject, Injectable, NgZone} from '@angular/core';
9+
import {ɵgetDOM as getDom, DOCUMENT} from '@angular/platform-browser';
1010
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
1111
import {Observable} from 'rxjs/Observable';
1212
import {filter} from 'rxjs/operator/filter';
@@ -27,7 +27,9 @@ export interface MediaQueryListListener {
2727
export interface MediaQueryList {
2828
readonly matches: boolean;
2929
readonly media: string;
30+
3031
addListener(listener: MediaQueryListListener): void;
32+
3133
removeListener(listener: MediaQueryListListener): void;
3234
}
3335

@@ -45,7 +47,7 @@ export class MatchMedia {
4547
protected _source: BehaviorSubject<MediaChange>;
4648
protected _observable$: Observable<MediaChange>;
4749

48-
constructor(protected _zone: NgZone) {
50+
constructor(protected _zone: NgZone, @Inject(DOCUMENT) protected _document: any) {
4951
this._registry = new Map<string, MediaQueryList>();
5052
this._source = new BehaviorSubject<MediaChange>(new MediaChange(true));
5153
this._observable$ = this._source.asObservable();
@@ -86,7 +88,7 @@ export class MatchMedia {
8688
let list = normalizeQuery(mediaQuery);
8789

8890
if (list.length > 0) {
89-
prepareQueryCSS(list);
91+
prepareQueryCSS(list, this._document);
9092

9193
list.forEach(query => {
9294
let mql = this._registry.get(query);
@@ -114,9 +116,12 @@ export class MatchMedia {
114116
* Call window.matchMedia() to build a MediaQueryList; which
115117
* supports 0..n listeners for activation/deactivation
116118
*/
117-
protected _buildMQL(query: string): MediaQueryList {
118-
let canListen = !!(<any>window).matchMedia('all').addListener;
119-
return canListen ? (<any>window).matchMedia(query) : <MediaQueryList>{
119+
protected _buildMQL(query: string): MediaQueryList {
120+
let canListen = (typeof matchMedia != 'undefined');
121+
if (canListen) {
122+
canListen = !!matchMedia('all').addListener;
123+
}
124+
return canListen ? matchMedia(query) : <MediaQueryList>{
120125
matches: query === 'all' || query === '',
121126
media: query,
122127
addListener: () => {
@@ -140,27 +145,28 @@ const ALL_STYLES = {};
140145
* @param query string The mediaQuery used to create a faux CSS selector
141146
*
142147
*/
143-
function prepareQueryCSS(mediaQueries: string[]) {
148+
function prepareQueryCSS(mediaQueries: string[], _document: any) {
144149
let list = mediaQueries.filter(it => !ALL_STYLES[it]);
145150
if (list.length > 0) {
146151
let query = list.join(', ');
152+
147153
try {
148-
let style = document.createElement('style');
154+
let styleEl = getDom().createElement('style');
149155

150-
style.setAttribute('type', 'text/css');
151-
if (!style['styleSheet']) {
156+
getDom().setAttribute(styleEl, 'type', 'text/css');
157+
if (!styleEl['styleSheet']) {
152158
let cssText = `/*
153159
@angular/flex-layout - workaround for possible browser quirk with mediaQuery listeners
154160
see http://bit.ly/2sd4HMP
155161
*/
156162
@media ${query} {.fx-query-test{ }}`;
157-
style.appendChild(document.createTextNode(cssText));
163+
getDom().appendChild(styleEl, getDom().createTextNode(cssText));
158164
}
159165

160-
document.getElementsByTagName('head')[0].appendChild(style);
166+
getDom().appendChild(_document.head, styleEl);
161167

162168
// Store in private global registry
163-
list.forEach(mq => ALL_STYLES[mq] = style);
169+
list.forEach(mq => ALL_STYLES[mq] = styleEl);
164170

165171
} catch (e) {
166172
console.error(e);

src/lib/media-query/mock/mock-match-media.ts

+32-12
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import {Injectable, NgZone} from '@angular/core';
8+
import {Inject, Injectable, NgZone} from '@angular/core';
9+
import {DOCUMENT} from '@angular/platform-browser';
10+
911
import {MatchMedia} from '../match-media';
1012
import {BreakPointRegistry} from '../breakpoints/break-point-registry';
1113

@@ -28,8 +30,10 @@ export class MockMatchMedia extends MatchMedia {
2830
*/
2931
public useOverlaps = false;
3032

31-
constructor(_zone: NgZone, private _breakpoints: BreakPointRegistry) {
32-
super(_zone);
33+
constructor(_zone: NgZone,
34+
@Inject(DOCUMENT) _document: any,
35+
private _breakpoints: BreakPointRegistry) {
36+
super(_zone, _document);
3337
this._actives = [];
3438
}
3539

@@ -83,18 +87,34 @@ export class MockMatchMedia extends MatchMedia {
8387

8488
// Simulate activation of overlapping lt-<XXX> ranges
8589
switch (alias) {
86-
case 'lg' : this._activateByAlias('lt-xl'); break;
87-
case 'md' : this._activateByAlias('lt-xl, lt-lg'); break;
88-
case 'sm' : this._activateByAlias('lt-xl, lt-lg, lt-md'); break;
89-
case 'xs' : this._activateByAlias('lt-xl, lt-lg, lt-md, lt-sm'); break;
90+
case 'lg' :
91+
this._activateByAlias('lt-xl');
92+
break;
93+
case 'md' :
94+
this._activateByAlias('lt-xl, lt-lg');
95+
break;
96+
case 'sm' :
97+
this._activateByAlias('lt-xl, lt-lg, lt-md');
98+
break;
99+
case 'xs' :
100+
this._activateByAlias('lt-xl, lt-lg, lt-md, lt-sm');
101+
break;
90102
}
91103

92104
// Simulate activate of overlapping gt-<xxxx> mediaQuery ranges
93105
switch (alias) {
94-
case 'xl' : this._activateByAlias('gt-lg, gt-md, gt-sm, gt-xs'); break;
95-
case 'lg' : this._activateByAlias('gt-md, gt-sm, gt-xs'); break;
96-
case 'md' : this._activateByAlias('gt-sm, gt-xs'); break;
97-
case 'sm' : this._activateByAlias('gt-xs'); break;
106+
case 'xl' :
107+
this._activateByAlias('gt-lg, gt-md, gt-sm, gt-xs');
108+
break;
109+
case 'lg' :
110+
this._activateByAlias('gt-md, gt-sm, gt-xs');
111+
break;
112+
case 'md' :
113+
this._activateByAlias('gt-sm, gt-xs');
114+
break;
115+
case 'sm' :
116+
this._activateByAlias('gt-xs');
117+
break;
98118
}
99119
}
100120
// Activate last since the responsiveActivation is watching *this* mediaQuery
@@ -195,7 +215,7 @@ export class MockMediaQueryList implements MediaQueryList {
195215
* Notify all listeners that 'matches === TRUE'
196216
*/
197217
activate(): MockMediaQueryList {
198-
if ( !this._isActive ) {
218+
if (!this._isActive) {
199219
this._isActive = true;
200220
this._listeners.forEach((callback) => {
201221
callback(this);

0 commit comments

Comments
 (0)