Skip to content

Commit

Permalink
feat(ssr): enhance support for Universal and SSR with stylesheets
Browse files Browse the repository at this point in the history
* Add StyleUtils class to manage application and retrieval of
  styles from elements in a platform-agnostic manner
* 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
  • Loading branch information
CaerusKaru committed Jan 16, 2018
1 parent 9214328 commit ee9d608
Show file tree
Hide file tree
Showing 25 changed files with 508 additions and 211 deletions.
7 changes: 4 additions & 3 deletions src/lib/api/core/base-adapter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
* 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 {ElementRef, Renderer2} from '@angular/core';
import {ElementRef} from '@angular/core';
import {BaseFxDirectiveAdapter} from './base-adapter';
import {expect} from '../../utils/testing/custom-matchers';
import {MediaMonitor} from '@angular/flex-layout/media-query';
import {MediaMonitor} from '../../media-query/media-monitor';
import {StyleUtils} from '../../utils/style-utils';

export class MockElementRef extends ElementRef {
constructor() {
Expand All @@ -21,7 +22,7 @@ export class MockElementRef extends ElementRef {
describe('BaseFxDirectiveAdapter class', () => {
let component;
beforeEach(() => {
component = new BaseFxDirectiveAdapter('', {} as MediaMonitor, new MockElementRef(), {} as Renderer2, {}); // tslint:disable-line:max-line-length
component = new BaseFxDirectiveAdapter('', {} as MediaMonitor, new MockElementRef(), {} as StyleUtils); // tslint:disable-line:max-line-length
});
describe('cacheInput', () => {
it('should call _cacheInputArray when source is an array', () => {
Expand Down
8 changes: 4 additions & 4 deletions src/lib/api/core/base-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
* 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 {ElementRef, Inject, PLATFORM_ID, Renderer2} from '@angular/core';
import {ElementRef} from '@angular/core';

import {BaseFxDirective} from './base';
import {ResponsiveActivation} from './responsive-activation';
import {MediaQuerySubscriber} from '../../media-query/media-change';
import {MediaMonitor} from '../../media-query/media-monitor';
import {StyleUtils} from '../../utils/style-utils';


/**
Expand Down Expand Up @@ -48,9 +49,8 @@ export class BaseFxDirectiveAdapter extends BaseFxDirective {
constructor(protected _baseKey: string, // non-responsive @Input property name
protected _mediaMonitor: MediaMonitor,
protected _elementRef: ElementRef,
protected _renderer: Renderer2,
@Inject(PLATFORM_ID) protected _platformId: Object) {
super(_mediaMonitor, _elementRef, _renderer, _platformId);
protected _styleUtils: StyleUtils) {
super(_mediaMonitor, _elementRef, _styleUtils);
}

/**
Expand Down
41 changes: 19 additions & 22 deletions src/lib/api/core/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,12 @@ import {
SimpleChanges,
OnChanges,
SimpleChange,
Renderer2,
Inject,
PLATFORM_ID,
} from '@angular/core';

import {buildLayoutCSS} from '../../utils/layout-validator';
import {
StyleDefinition,
lookupStyle,
lookupInlineStyle,
applyStyleToElement,
applyStyleToElements,
lookupAttributeValue,
StyleUtils,
} from '../../utils/style-utils';

import {ResponsiveActivation, KeyOptions} from '../core/responsive-activation';
Expand Down Expand Up @@ -70,8 +63,7 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges {
*/
constructor(protected _mediaMonitor: MediaMonitor,
protected _elementRef: ElementRef,
protected _renderer: Renderer2,
@Inject(PLATFORM_ID) protected _platformId: Object) {
protected _styleUtils: StyleUtils) {
}

// *********************************************
Expand Down Expand Up @@ -137,19 +129,20 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges {

/**
* Quick accessor to the current HTMLElement's `display` style
* Note: this allows use to preserve the original style
* Note: this allows us to preserve the original style
* and optional restore it when the mediaQueries deactivate
*/
protected _getDisplayStyle(source: HTMLElement = this.nativeElement): string {
return lookupStyle(this._platformId, source || this.nativeElement, 'display');
const query = 'display';
return this._styleUtils.lookupStyle(source, query);
}

/**
* Quick accessor to raw attribute value on the target DOM element
*/
protected _getAttributeValue(attribute: string,
source: HTMLElement = this.nativeElement): string {
return lookupAttributeValue(source || this.nativeElement, attribute);
return this._styleUtils.lookupAttributeValue(source, attribute);
}

/**
Expand All @@ -158,15 +151,20 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges {
* Check inline style first then check computed (stylesheet) style.
* And optionally add the flow value to element's inline style.
*/
protected _getFlowDirection(target: any, addIfMissing = false): string {
protected _getFlowDirection(target: HTMLElement, addIfMissing = false): string {
let value = 'row';
let hasInlineValue = '';
const query = 'flex-direction';

if (target) {
value = lookupStyle(this._platformId, target, 'flex-direction') || 'row';
let hasInlineValue = lookupInlineStyle(target, 'flex-direction');

value = this._styleUtils.lookupStyle(target, query) || 'row';
hasInlineValue = this._styleUtils.lookupInlineStyle(target, query);

if (!hasInlineValue && addIfMissing) {
applyStyleToElements(this._renderer, buildLayoutCSS(value), [target]);
const style = buildLayoutCSS(value);
const elements = [target];
this._styleUtils.applyStyleToElements(style, elements);
}
}

Expand All @@ -178,16 +176,15 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges {
*/
protected _applyStyleToElement(style: StyleDefinition,
value?: string | number,
nativeElement: any = this.nativeElement) {
let element = nativeElement || this.nativeElement;
applyStyleToElement(this._renderer, element, style, value);
element: HTMLElement = this.nativeElement) {
this._styleUtils.applyStyleToElement(element, style, value);
}

/**
* Applies styles given via string pair or object map to the directive's element.
*/
protected _applyStyleToElements(style: StyleDefinition, elements: HTMLElement[ ]) {
applyStyleToElements(this._renderer, style, elements || []);
protected _applyStyleToElements(style: StyleDefinition, elements: HTMLElement[]) {
this._styleUtils.applyStyleToElements(style, elements);
}

/**
Expand Down
6 changes: 5 additions & 1 deletion src/lib/api/ext/class.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {BreakPointRegistry} from '../../media-query/breakpoints/break-point-regi

import {ClassDirective} from './class';
import {MediaQueriesModule} from '../../media-query/_module';
import {ServerStylesheet} from '../../utils/server-stylesheet';
import {StyleUtils} from '../../utils/style-utils';

describe('class directive', () => {
let fixture: ComponentFixture<any>;
Expand All @@ -46,7 +48,9 @@ describe('class directive', () => {
declarations: [TestClassComponent, ClassDirective],
providers: [
BreakPointRegistry, DEFAULT_BREAKPOINTS_PROVIDER,
{provide: MatchMedia, useClass: MockMatchMedia}
{provide: MatchMedia, useClass: MockMatchMedia},
ServerStylesheet,
StyleUtils,
]
});
});
Expand Down
12 changes: 7 additions & 5 deletions src/lib/api/ext/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ import {
SimpleChanges,
Self,
OnInit,
Inject,
PLATFORM_ID,
} from '@angular/core';
import {NgClass} from '@angular/common';

Expand All @@ -29,6 +27,7 @@ import {BaseFxDirectiveAdapter} from '../core/base-adapter';
import {MediaChange} from '../../media-query/media-change';
import {MediaMonitor} from '../../media-query/media-monitor';
import {RendererAdapter} from '../core/renderer-adapter';
import {StyleUtils} from '../../utils/style-utils';

/** NgClass allowed inputs **/
export type NgClassType = string | string[] | Set<string> | {[klass: string]: any};
Expand Down Expand Up @@ -95,8 +94,8 @@ export class ClassDirective extends BaseFxDirective
protected _ngEl: ElementRef,
protected _renderer: Renderer2,
@Optional() @Self() private _ngClassInstance: NgClass,
@Inject(PLATFORM_ID) protected _platformId: Object) {
super(monitor, _ngEl, _renderer, _platformId);
protected _styleUtils: StyleUtils) {
super(monitor, _ngEl, _styleUtils);
this._configureAdapters();
}

Expand Down Expand Up @@ -139,7 +138,10 @@ export class ClassDirective extends BaseFxDirective
*/
protected _configureAdapters() {
this._base = new BaseFxDirectiveAdapter(
'ngClass', this.monitor, this._ngEl, this._renderer, this._platformId
'ngClass',
this.monitor,
this._ngEl,
this._styleUtils
);
if (!this._ngClassInstance) {
// Create an instance NgClass Directive instance only if `ngClass=""` has NOT been defined on
Expand Down
6 changes: 5 additions & 1 deletion src/lib/api/ext/hide.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {
} from '../../utils/testing/helpers';
import {ShowHideDirective} from './show-hide';
import {MediaQueriesModule} from '../../media-query/_module';
import {ServerStylesheet} from '../../utils/server-stylesheet';
import {StyleUtils} from '../../utils/style-utils';

describe('hide directive', () => {
let fixture: ComponentFixture<any>;
Expand Down Expand Up @@ -60,7 +62,9 @@ describe('hide directive', () => {
declarations: [TestHideComponent, ShowHideDirective],
providers: [
BreakPointRegistry, DEFAULT_BREAKPOINTS_PROVIDER,
{provide: MatchMedia, useClass: MockMatchMedia}
{provide: MatchMedia, useClass: MockMatchMedia},
ServerStylesheet,
StyleUtils,
]
});
});
Expand Down
15 changes: 7 additions & 8 deletions src/lib/api/ext/img-src.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ import {
OnInit,
OnChanges,
Renderer2,
Inject,
PLATFORM_ID,
} from '@angular/core';

import {BaseFxDirective} from '../core/base';
import {MediaMonitor} from '../../media-query/media-monitor';
import {StyleUtils} from '../../utils/style-utils';

/**
* This directive provides a responsive API for the HTML <img> 'src' attribute
Expand Down Expand Up @@ -57,12 +56,12 @@ export class ImgSrcDirective extends BaseFxDirective implements OnInit, OnChange
@Input('src.gt-lg') set srcGtLg(val) { this._cacheInput('srcGtLg', val); }
/* tslint:enable */

constructor(elRef: ElementRef,
renderer: Renderer2,
monitor: MediaMonitor,
@Inject(PLATFORM_ID) platformId: Object) {
super(monitor, elRef, renderer, platformId);
this._cacheInput('src', elRef.nativeElement.getAttribute('src') || '');
constructor(protected _elRef: ElementRef,
protected _renderer: Renderer2,
protected _monitor: MediaMonitor,
protected _styleUtils: StyleUtils) {
super(_monitor, _elRef, _styleUtils);
this._cacheInput('src', _elRef.nativeElement.getAttribute('src') || '');
}

/**
Expand Down
17 changes: 7 additions & 10 deletions src/lib/api/ext/show-hide.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,9 @@ import {
OnInit,
OnChanges,
OnDestroy,
Renderer2,
SimpleChanges,
Self,
Optional,
Inject,
PLATFORM_ID,
} from '@angular/core';

import {Subscription} from 'rxjs/Subscription';
Expand All @@ -26,6 +23,7 @@ import {BaseFxDirective} from '../core/base';
import {MediaChange} from '../../media-query/media-change';
import {MediaMonitor} from '../../media-query/media-monitor';
import {LayoutDirective} from '../flexbox/layout';
import {StyleUtils} from '../../utils/style-utils';

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

Expand Down Expand Up @@ -104,19 +102,18 @@ export class ShowHideDirective extends BaseFxDirective implements OnInit, OnChan
*
*/
constructor(monitor: MediaMonitor,
@Optional() @Self() protected _layout: LayoutDirective,
@Optional() @Self() protected layout: LayoutDirective,
protected elRef: ElementRef,
protected renderer: Renderer2,
@Inject(PLATFORM_ID) protected platformId: Object) {
protected styleUtils: StyleUtils) {

super(monitor, elRef, renderer, platformId);
super(monitor, elRef, styleUtils);

if (_layout) {
if (layout) {
/**
* The Layout can set the display:flex (and incorrectly affect the Hide/Show directives.
* Whenever Layout [on the same element] resets its CSS, then update the Hide/Show CSS
*/
this._layoutWatcher = _layout.layout$.subscribe(() => this._updateWithValue());
this._layoutWatcher = layout.layout$.subscribe(() => this._updateWithValue());
}
}

Expand All @@ -130,7 +127,7 @@ export class ShowHideDirective extends BaseFxDirective implements OnInit, OnChan
* unless it was already explicitly specified inline or in a CSS stylesheet.
*/
protected _getDisplayStyle(): string {
return this._layout ? 'flex' : super._getDisplayStyle();
return this.layout ? 'flex' : super._getDisplayStyle();
}


Expand Down
6 changes: 5 additions & 1 deletion src/lib/api/ext/show.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {FlexLayoutModule} from '../../module';

import {customMatchers} from '../../utils/testing/custom-matchers';
import {makeCreateTestComponent, expectNativeEl} from '../../utils/testing/helpers';
import {ServerStylesheet} from '../../utils/server-stylesheet';
import {StyleUtils} from '../../utils/style-utils';

describe('show directive', () => {
let fixture: ComponentFixture<any>;
Expand All @@ -39,7 +41,9 @@ describe('show directive', () => {
declarations: [TestShowComponent],
providers: [
BreakPointRegistry, DEFAULT_BREAKPOINTS_PROVIDER,
{provide: MatchMedia, useClass: MockMatchMedia}
{provide: MatchMedia, useClass: MockMatchMedia},
ServerStylesheet,
StyleUtils,
]
});
});
Expand Down
6 changes: 5 additions & 1 deletion src/lib/api/ext/style.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import {customMatchers} from '../../utils/testing/custom-matchers';
import {
makeCreateTestComponent, expectNativeEl
} from '../../utils/testing/helpers';
import {ServerStylesheet} from '../../utils/server-stylesheet';
import {StyleUtils} from '../../utils/style-utils';

describe('style directive', () => {
let fixture: ComponentFixture<any>;
Expand All @@ -43,7 +45,9 @@ describe('style directive', () => {
declarations: [TestStyleComponent, LayoutDirective, StyleDirective],
providers: [
BreakPointRegistry, DEFAULT_BREAKPOINTS_PROVIDER,
{provide: MatchMedia, useClass: MockMatchMedia}
{provide: MatchMedia, useClass: MockMatchMedia},
ServerStylesheet,
StyleUtils,
]
});
});
Expand Down
Loading

0 comments on commit ee9d608

Please sign in to comment.