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

Commit 1dbf11c

Browse files
committed
fix: add correct ssr styles
* Add virtual stylesheet to store server styles, which applies default styles when breakpoint overrides are not present * Intercept all style calls and reroute them to the virtual stylesheet while not in the browser * Add a new type of MediaQueryList similar to the MockMediaQueryList for the server that allows for manual activation/deactivation of breakpoints * Update deps to Angular v5.2.x and TypeScript v2.6.x
1 parent 2d17a48 commit 1dbf11c

31 files changed

+435
-108
lines changed

src/lib/api/core/base-adapter.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
import {ElementRef, Renderer2} from '@angular/core';
99
import {BaseFxDirectiveAdapter} from './base-adapter';
1010
import {expect} from '../../utils/testing/custom-matchers';
11-
import {MediaMonitor} from '@angular/flex-layout/media-query';
11+
import {MediaMonitor} from '../../media-query/media-monitor';
12+
import {ServerStylesheet} from '../../utils/server-stylesheet';
1213

1314
export class MockElementRef extends ElementRef {
1415
constructor() {
@@ -21,7 +22,7 @@ export class MockElementRef extends ElementRef {
2122
describe('BaseFxDirectiveAdapter class', () => {
2223
let component;
2324
beforeEach(() => {
24-
component = new BaseFxDirectiveAdapter('', {} as MediaMonitor, new MockElementRef(), {} as Renderer2, {}); // tslint:disable-line:max-line-length
25+
component = new BaseFxDirectiveAdapter('', {} as MediaMonitor, new MockElementRef(), {} as Renderer2, {}, {} as ServerStylesheet); // tslint:disable-line:max-line-length
2526
});
2627
describe('cacheInput', () => {
2728
it('should call _cacheInputArray when source is an array', () => {

src/lib/api/core/base-adapter.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {BaseFxDirective} from './base';
1111
import {ResponsiveActivation} from './responsive-activation';
1212
import {MediaQuerySubscriber} from '../../media-query/media-change';
1313
import {MediaMonitor} from '../../media-query/media-monitor';
14+
import {ServerStylesheet} from '../../utils/server-stylesheet';
1415

1516

1617
/**
@@ -49,8 +50,9 @@ export class BaseFxDirectiveAdapter extends BaseFxDirective {
4950
protected _mediaMonitor: MediaMonitor,
5051
protected _elementRef: ElementRef,
5152
protected _renderer: Renderer2,
52-
@Inject(PLATFORM_ID) protected _platformId: Object) {
53-
super(_mediaMonitor, _elementRef, _renderer, _platformId);
53+
@Inject(PLATFORM_ID) protected _platformId: Object,
54+
protected _serverStylesheet: ServerStylesheet) {
55+
super(_mediaMonitor, _elementRef, _renderer, _platformId, _serverStylesheet);
5456
}
5557

5658
/**

src/lib/api/core/base.ts

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import {
2929
import {ResponsiveActivation, KeyOptions} from '../core/responsive-activation';
3030
import {MediaMonitor} from '../../media-query/media-monitor';
3131
import {MediaQuerySubscriber} from '../../media-query/media-change';
32+
import {ServerStylesheet} from '../../utils/server-stylesheet';
33+
import {isPlatformBrowser} from '@angular/common';
3234

3335
/** Abstract base class for the Layout API styling directives. */
3436
export abstract class BaseFxDirective implements OnDestroy, OnChanges {
@@ -71,7 +73,8 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges {
7173
constructor(protected _mediaMonitor: MediaMonitor,
7274
protected _elementRef: ElementRef,
7375
protected _renderer: Renderer2,
74-
@Inject(PLATFORM_ID) protected _platformId: Object) {
76+
@Inject(PLATFORM_ID) protected _platformId: Object,
77+
protected _serverStylesheet: ServerStylesheet) {
7578
}
7679

7780
// *********************************************
@@ -137,11 +140,16 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges {
137140

138141
/**
139142
* Quick accessor to the current HTMLElement's `display` style
140-
* Note: this allows use to preserve the original style
143+
* Note: this allows us to preserve the original style
141144
* and optional restore it when the mediaQueries deactivate
142145
*/
143146
protected _getDisplayStyle(source: HTMLElement = this.nativeElement): string {
144-
return lookupStyle(this._platformId, source || this.nativeElement, 'display');
147+
const query = 'display';
148+
if (isPlatformBrowser(this._platformId)) {
149+
return lookupStyle(this._platformId, source || this.nativeElement, query);
150+
} else {
151+
return this._serverStylesheet.getStyleForElement(source, query);
152+
}
145153
}
146154

147155
/**
@@ -160,13 +168,26 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges {
160168
*/
161169
protected _getFlowDirection(target: any, addIfMissing = false): string {
162170
let value = 'row';
171+
let hasInlineValue = '';
172+
const query = 'flex-direction';
163173

164174
if (target) {
165-
value = lookupStyle(this._platformId, target, 'flex-direction') || 'row';
166-
let hasInlineValue = lookupInlineStyle(target, 'flex-direction');
175+
if (isPlatformBrowser(this._platformId)) {
176+
value = lookupStyle(this._platformId, target, query) || 'row';
177+
hasInlineValue = lookupInlineStyle(target, query);
178+
} else {
179+
// TODO(CaerusKaru): platform-server has no implementation for getComputedStyle
180+
value = this._serverStylesheet.getStyleForElement(target, query) || 'row';
181+
}
167182

168183
if (!hasInlineValue && addIfMissing) {
169-
applyStyleToElements(this._renderer, buildLayoutCSS(value), [target]);
184+
const style = buildLayoutCSS(value);
185+
const elements = [target];
186+
if (isPlatformBrowser(this._platformId)) {
187+
applyStyleToElements(this._renderer, style, elements);
188+
} else {
189+
this._serverStylesheet.addStyleToElements(style, elements);
190+
}
170191
}
171192
}
172193

@@ -180,14 +201,22 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges {
180201
value?: string | number,
181202
nativeElement: any = this.nativeElement) {
182203
let element = nativeElement || this.nativeElement;
183-
applyStyleToElement(this._renderer, element, style, value);
204+
if (isPlatformBrowser(this._platformId)) {
205+
applyStyleToElement(this._renderer, element, style, value);
206+
} else {
207+
this._serverStylesheet.addStyleToElement(element, style, value);
208+
}
184209
}
185210

186211
/**
187212
* Applies styles given via string pair or object map to the directive's element.
188213
*/
189214
protected _applyStyleToElements(style: StyleDefinition, elements: HTMLElement[ ]) {
190-
applyStyleToElements(this._renderer, style, elements || []);
215+
if (isPlatformBrowser(this._platformId)) {
216+
applyStyleToElements(this._renderer, style, elements || []);
217+
} else {
218+
this._serverStylesheet.addStyleToElements(style, elements || []);
219+
}
191220
}
192221

193222
/**

src/lib/api/core/responsive-activation.spec.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import {MediaQuerySubscriber, MediaChange} from '../../media-query/media-change'
2222
describe('responsive-activation', () => {
2323
let monitor: MediaMonitor;
2424
let matchMedia: MockMatchMedia;
25-
let breakPoints: BreakPointRegistry;
2625

2726
/**
2827
* MediaQuery Change responder used to determine the activated input
@@ -52,9 +51,8 @@ describe('responsive-activation', () => {
5251

5352
// Single async inject to save references; which are used in all tests below
5453
beforeEach(inject(
55-
[BreakPointRegistry, MatchMedia, MediaMonitor],
56-
(_breakPoints, _matchMedia, _mediaMonitor) => {
57-
breakPoints = _breakPoints; // Only used to look up mediaQuery by aliases
54+
[MatchMedia, MediaMonitor],
55+
(_matchMedia, _mediaMonitor) => {
5856
matchMedia = _matchMedia; // Only used to manual/simulate activate a mediaQuery
5957
monitor = _mediaMonitor;
6058
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {BreakPointRegistry} from '../../media-query/breakpoints/break-point-regi
2222

2323
import {ClassDirective} from './class';
2424
import {MediaQueriesModule} from '../../media-query/_module';
25+
import {ServerStylesheet} from '../../utils/server-stylesheet';
2526

2627
describe('class directive', () => {
2728
let fixture: ComponentFixture<any>;
@@ -47,7 +48,8 @@ describe('class directive', () => {
4748
declarations: [TestClassComponent, ClassDirective],
4849
providers: [
4950
BreakPointRegistry, DEFAULT_BREAKPOINTS_PROVIDER,
50-
{provide: MatchMedia, useClass: MockMatchMedia}
51+
{provide: MatchMedia, useClass: MockMatchMedia},
52+
ServerStylesheet
5153
]
5254
});
5355
});

src/lib/api/ext/class.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {BaseFxDirectiveAdapter} from '../core/base-adapter';
2929
import {MediaChange} from '../../media-query/media-change';
3030
import {MediaMonitor} from '../../media-query/media-monitor';
3131
import {RendererAdapter} from '../core/renderer-adapter';
32+
import {ServerStylesheet} from '../../utils/server-stylesheet';
3233

3334
/** NgClass allowed inputs **/
3435
export type NgClassType = string | string[] | Set<string> | {[klass: string]: any};
@@ -95,8 +96,9 @@ export class ClassDirective extends BaseFxDirective
9596
protected _ngEl: ElementRef,
9697
protected _renderer: Renderer2,
9798
@Optional() @Self() private _ngClassInstance: NgClass,
98-
@Inject(PLATFORM_ID) protected _platformId: Object) {
99-
super(monitor, _ngEl, _renderer, _platformId);
99+
@Inject(PLATFORM_ID) protected _platformId: Object,
100+
protected _serverStylesheet: ServerStylesheet) {
101+
super(monitor, _ngEl, _renderer, _platformId, _serverStylesheet);
100102
this._configureAdapters();
101103
}
102104

@@ -139,7 +141,12 @@ export class ClassDirective extends BaseFxDirective
139141
*/
140142
protected _configureAdapters() {
141143
this._base = new BaseFxDirectiveAdapter(
142-
'ngClass', this.monitor, this._ngEl, this._renderer, this._platformId
144+
'ngClass',
145+
this.monitor,
146+
this._ngEl,
147+
this._renderer,
148+
this._platformId,
149+
this._serverStylesheet,
143150
);
144151
if (!this._ngClassInstance) {
145152
// Create an instance NgClass Directive instance only if `ngClass=""` has NOT been defined on

src/lib/api/ext/hide.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
} from '../../utils/testing/helpers';
2424
import {ShowHideDirective} from './show-hide';
2525
import {MediaQueriesModule} from '../../media-query/_module';
26+
import {ServerStylesheet} from '../../utils/server-stylesheet';
2627

2728
describe('hide directive', () => {
2829
let fixture: ComponentFixture<any>;
@@ -60,7 +61,8 @@ describe('hide directive', () => {
6061
declarations: [TestHideComponent, ShowHideDirective],
6162
providers: [
6263
BreakPointRegistry, DEFAULT_BREAKPOINTS_PROVIDER,
63-
{provide: MatchMedia, useClass: MockMatchMedia}
64+
{provide: MatchMedia, useClass: MockMatchMedia},
65+
ServerStylesheet
6466
]
6567
});
6668
});

src/lib/api/ext/img-src.spec.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,13 @@ const DEFAULT_SRC = 'https://dummyimage.com/300x300/c72538/ffffff.png';
5151
describe('img-src directive', () => {
5252
let fixture: ComponentFixture<any>;
5353
let matchMedia: MockMatchMedia;
54-
let breakpoints: BreakPointRegistry;
5554

5655
let componentWithTemplate = (template: string) => {
5756
fixture = makeCreateTestComponent(() => TestSrcComponent)(template);
5857

59-
inject([MatchMedia, BreakPointRegistry],
60-
(_matchMedia: MockMatchMedia, _breakpoints: BreakPointRegistry) => {
58+
inject([MatchMedia],
59+
(_matchMedia: MockMatchMedia) => {
6160
matchMedia = _matchMedia;
62-
breakpoints = _breakpoints;
6361
})();
6462
};
6563

src/lib/api/ext/img-src.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818

1919
import {BaseFxDirective} from '../core/base';
2020
import {MediaMonitor} from '../../media-query/media-monitor';
21+
import {ServerStylesheet} from '../../utils/server-stylesheet';
2122

2223
/**
2324
* This directive provides a responsive API for the HTML <img> 'src' attribute
@@ -60,8 +61,9 @@ export class ImgSrcDirective extends BaseFxDirective implements OnInit, OnChange
6061
constructor(elRef: ElementRef,
6162
renderer: Renderer2,
6263
monitor: MediaMonitor,
63-
@Inject(PLATFORM_ID) platformId: Object) {
64-
super(monitor, elRef, renderer, platformId);
64+
@Inject(PLATFORM_ID) platformId: Object,
65+
serverStylesheet: ServerStylesheet) {
66+
super(monitor, elRef, renderer, platformId, serverStylesheet);
6567
this._cacheInput('src', elRef.nativeElement.getAttribute('src') || '');
6668
}
6769

src/lib/api/ext/show-hide.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {BaseFxDirective} from '../core/base';
2626
import {MediaChange} from '../../media-query/media-change';
2727
import {MediaMonitor} from '../../media-query/media-monitor';
2828
import {LayoutDirective} from '../flexbox/layout';
29+
import {ServerStylesheet} from '../../utils/server-stylesheet';
2930

3031
const FALSY = ['false', false, 0];
3132

@@ -107,9 +108,10 @@ export class ShowHideDirective extends BaseFxDirective implements OnInit, OnChan
107108
@Optional() @Self() protected _layout: LayoutDirective,
108109
protected elRef: ElementRef,
109110
protected renderer: Renderer2,
110-
@Inject(PLATFORM_ID) protected platformId: Object) {
111+
@Inject(PLATFORM_ID) protected platformId: Object,
112+
protected serverStylesheet: ServerStylesheet) {
111113

112-
super(monitor, elRef, renderer, platformId);
114+
super(monitor, elRef, renderer, platformId, serverStylesheet);
113115

114116
if (_layout) {
115117
/**

src/lib/api/ext/show.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {FlexLayoutModule} from '../../module';
1818

1919
import {customMatchers} from '../../utils/testing/custom-matchers';
2020
import {makeCreateTestComponent, expectNativeEl} from '../../utils/testing/helpers';
21+
import {ServerStylesheet} from '../../utils/server-stylesheet';
2122

2223
describe('show directive', () => {
2324
let fixture: ComponentFixture<any>;
@@ -39,7 +40,8 @@ describe('show directive', () => {
3940
declarations: [TestShowComponent],
4041
providers: [
4142
BreakPointRegistry, DEFAULT_BREAKPOINTS_PROVIDER,
42-
{provide: MatchMedia, useClass: MockMatchMedia}
43+
{provide: MatchMedia, useClass: MockMatchMedia},
44+
ServerStylesheet
4345
]
4446
});
4547
});

src/lib/api/ext/style.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {customMatchers} from '../../utils/testing/custom-matchers';
2222
import {
2323
makeCreateTestComponent, expectNativeEl
2424
} from '../../utils/testing/helpers';
25+
import {ServerStylesheet} from '../../utils/server-stylesheet';
2526

2627
describe('style directive', () => {
2728
let fixture: ComponentFixture<any>;
@@ -43,7 +44,8 @@ describe('style directive', () => {
4344
declarations: [TestStyleComponent, LayoutDirective, StyleDirective],
4445
providers: [
4546
BreakPointRegistry, DEFAULT_BREAKPOINTS_PROVIDER,
46-
{provide: MatchMedia, useClass: MockMatchMedia}
47+
{provide: MatchMedia, useClass: MockMatchMedia},
48+
ServerStylesheet
4749
]
4850
});
4951
});

src/lib/api/ext/style.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
ngStyleUtils as _
3939
} from '../../utils/style-transforms';
4040
import {RendererAdapter} from '../core/renderer-adapter';
41+
import {ServerStylesheet} from '../../utils/server-stylesheet';
4142

4243

4344
/**
@@ -93,9 +94,10 @@ export class StyleDirective extends BaseFxDirective
9394
protected _renderer: Renderer2,
9495
protected _differs: KeyValueDiffers,
9596
@Optional() @Self() private _ngStyleInstance: NgStyle,
96-
@Inject(PLATFORM_ID) protected _platformId: Object) {
97+
@Inject(PLATFORM_ID) protected _platformId: Object,
98+
protected _serverStylesheet: ServerStylesheet) {
9799

98-
super(monitor, _ngEl, _renderer, _platformId);
100+
super(monitor, _ngEl, _renderer, _platformId, _serverStylesheet);
99101
this._configureAdapters();
100102
}
101103

@@ -138,9 +140,14 @@ export class StyleDirective extends BaseFxDirective
138140
*/
139141
protected _configureAdapters() {
140142
this._base = new BaseFxDirectiveAdapter(
141-
'ngStyle', this.monitor, this._ngEl, this._renderer, this._platformId
143+
'ngStyle',
144+
this.monitor,
145+
this._ngEl,
146+
this._renderer,
147+
this._platformId,
148+
this._serverStylesheet,
142149
);
143-
if ( !this._ngStyleInstance ) {
150+
if (!this._ngStyleInstance) {
144151
// Create an instance NgClass Directive instance only if `ngClass=""` has NOT been
145152
// defined on the same host element; since the responsive variations may be defined...
146153
let adapter = new RendererAdapter(this._renderer);

src/lib/api/flexbox/flex-align.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
import {BaseFxDirective} from '../core/base';
2222
import {MediaChange} from '../../media-query/media-change';
2323
import {MediaMonitor} from '../../media-query/media-monitor';
24+
import {ServerStylesheet} from '../../utils/server-stylesheet';
2425

2526
/**
2627
* 'flex-align' flexbox styling directive
@@ -59,8 +60,9 @@ export class FlexAlignDirective extends BaseFxDirective implements OnInit, OnCha
5960
constructor(monitor: MediaMonitor,
6061
elRef: ElementRef,
6162
renderer: Renderer2,
62-
@Inject(PLATFORM_ID) platformId: Object) {
63-
super(monitor, elRef, renderer, platformId);
63+
@Inject(PLATFORM_ID) platformId: Object,
64+
serverStylesheet: ServerStylesheet) {
65+
super(monitor, elRef, renderer, platformId, serverStylesheet);
6466
}
6567

6668

src/lib/api/flexbox/flex-fill.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {Directive, ElementRef, Inject, PLATFORM_ID, Renderer2} from '@angular/co
99

1010
import {MediaMonitor} from '../../media-query/media-monitor';
1111
import {BaseFxDirective} from '../core/base';
12+
import {ServerStylesheet} from '../../utils/server-stylesheet';
1213

1314
const FLEX_FILL_CSS = {
1415
'margin': 0,
@@ -32,8 +33,9 @@ export class FlexFillDirective extends BaseFxDirective {
3233
constructor(monitor: MediaMonitor,
3334
public elRef: ElementRef,
3435
public renderer: Renderer2,
35-
@Inject(PLATFORM_ID) platformId: Object) {
36-
super(monitor, elRef, renderer, platformId);
36+
@Inject(PLATFORM_ID) platformId: Object,
37+
serverStylesheet: ServerStylesheet) {
38+
super(monitor, elRef, renderer, platformId, serverStylesheet);
3739
this._applyStyleToElement(FLEX_FILL_CSS);
3840
}
3941
}

0 commit comments

Comments
 (0)