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 `StylerService` 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
* While not in the browser (ssr), intercept all style calls and reroute them to the virtual stylesheet.
* For server-side rendering, add a new type of MediaQueryList similar to the MockMediaQueryList to support manual activation/deactivation of breakpoints

Fixes #373.

> See [Design Doc](https://docs.google.com/document/d/1fg04ihw42dJJHGd6fugdiBe39iJot8aErhiE7CjwfmQ/edit#)
  • Loading branch information
CaerusKaru authored and ThomasBurleson committed Jan 18, 2018
1 parent 9214328 commit 193fdab
Show file tree
Hide file tree
Showing 33 changed files with 1,064 additions and 216 deletions.
15 changes: 12 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,15 @@
* 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';
<<<<<<< Updated upstream
import {StyleUtils} from '../../utils/style-utils';
=======
import {StylerService} from '../../utils/styling/styler';
>>>>>>> Stashed changes

export class MockElementRef extends ElementRef {
constructor() {
Expand All @@ -21,7 +26,11 @@ 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
<<<<<<< Updated upstream
component = new BaseFxDirectiveAdapter('', {} as MediaMonitor, new MockElementRef(), {} as StyleUtils); // tslint:disable-line:max-line-length
=======
component = new BaseFxDirectiveAdapter('', {} as MediaMonitor, new MockElementRef(), {} as StylerService); // tslint:disable-line:max-line-length
>>>>>>> Stashed changes
});
describe('cacheInput', () => {
it('should call _cacheInputArray when source is an array', () => {
Expand Down
17 changes: 13 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,17 @@
* 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';
<<<<<<< Updated upstream
import {StyleUtils} from '../../utils/style-utils';
=======
import {StylerService} from '../../utils/styling/styler';
>>>>>>> Stashed changes


/**
Expand Down Expand Up @@ -48,9 +53,13 @@ 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);
<<<<<<< Updated upstream
protected _styleUtils: StyleUtils) {
super(_mediaMonitor, _elementRef, _styleUtils);
=======
protected _styler: StylerService) {
super(_mediaMonitor, _elementRef, _styler);
>>>>>>> Stashed changes
}

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

import {buildLayoutCSS} from '../../utils/layout-validator';
import {
StyleDefinition,
lookupStyle,
lookupInlineStyle,
applyStyleToElement,
applyStyleToElements,
lookupAttributeValue,
<<<<<<< Updated upstream
StyleUtils,
} from '../../utils/style-utils';
=======
StylerService,
} from '../../utils/styling/styler';
>>>>>>> Stashed changes

import {ResponsiveActivation, KeyOptions} from '../core/responsive-activation';
import {MediaMonitor} from '../../media-query/media-monitor';
Expand Down Expand Up @@ -70,8 +68,11 @@ export abstract class BaseFxDirective implements OnDestroy, OnChanges {
*/
constructor(protected _mediaMonitor: MediaMonitor,
protected _elementRef: ElementRef,
protected _renderer: Renderer2,
@Inject(PLATFORM_ID) protected _platformId: Object) {
<<<<<<< Updated upstream
protected _styleUtils: StyleUtils) {
=======
protected _styler: StylerService) {
>>>>>>> Stashed changes
}

// *********************************************
Expand Down Expand Up @@ -137,19 +138,28 @@ 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';
<<<<<<< Updated upstream
return this._styleUtils.lookupStyle(source, query);
=======
return this._styler.lookupStyle(source, query);
>>>>>>> Stashed changes
}

/**
* 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);
<<<<<<< Updated upstream
return this._styleUtils.lookupAttributeValue(source, attribute);
=======
return this._styler.lookupAttributeValue(source, attribute);
>>>>>>> Stashed changes
}

/**
Expand All @@ -158,15 +168,29 @@ 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');

<<<<<<< Updated upstream
value = this._styleUtils.lookupStyle(target, query) || 'row';
hasInlineValue = this._styleUtils.lookupInlineStyle(target, query);
=======
value = this._styler.lookupStyle(target, query) || 'row';
hasInlineValue = this._styler.lookupInlineStyle(target, query);
>>>>>>> Stashed changes

if (!hasInlineValue && addIfMissing) {
applyStyleToElements(this._renderer, buildLayoutCSS(value), [target]);
const style = buildLayoutCSS(value);
const elements = [target];
<<<<<<< Updated upstream
this._styleUtils.applyStyleToElements(style, elements);
=======
this._styler.applyStyleToElements(style, elements);
>>>>>>> Stashed changes
}
}

Expand All @@ -178,16 +202,23 @@ 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) {
<<<<<<< Updated upstream
this._styleUtils.applyStyleToElement(element, style, value);
=======
this._styler.applyStyleToElement(element, style, value);
>>>>>>> Stashed changes
}

/**
* 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[]) {
<<<<<<< Updated upstream
this._styleUtils.applyStyleToElements(style, elements);
=======
this._styler.applyStyleToElements(style, elements);
>>>>>>> Stashed changes
}

/**
Expand Down
15 changes: 14 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,13 @@ import {BreakPointRegistry} from '../../media-query/breakpoints/break-point-regi

import {ClassDirective} from './class';
import {MediaQueriesModule} from '../../media-query/_module';
<<<<<<< Updated upstream
import {ServerStylesheet} from '../../utils/server-stylesheet';
import {StyleUtils} from '../../utils/style-utils';
=======
import {ServerStylesheet} from '../../utils/styling/server-stylesheet';
import {StylerService} from '../../utils/styling/styler';
>>>>>>> Stashed changes

describe('class directive', () => {
let fixture: ComponentFixture<any>;
Expand All @@ -46,7 +53,13 @@ describe('class directive', () => {
declarations: [TestClassComponent, ClassDirective],
providers: [
BreakPointRegistry, DEFAULT_BREAKPOINTS_PROVIDER,
{provide: MatchMedia, useClass: MockMatchMedia}
{provide: MatchMedia, useClass: MockMatchMedia},
ServerStylesheet,
<<<<<<< Updated upstream
StyleUtils,
=======
StylerService,
>>>>>>> Stashed changes
]
});
});
Expand Down
25 changes: 20 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,11 @@ 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';
<<<<<<< Updated upstream
import {StyleUtils} from '../../utils/style-utils';
=======
import {StylerService} from '../../utils/styling/styler';
>>>>>>> Stashed changes

/** NgClass allowed inputs **/
export type NgClassType = string | string[] | Set<string> | {[klass: string]: any};
Expand Down Expand Up @@ -95,8 +98,13 @@ 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);
<<<<<<< Updated upstream
protected _styleUtils: StyleUtils) {
super(monitor, _ngEl, _styleUtils);
=======
protected _styler: StylerService) {
super(monitor, _ngEl, _styler);
>>>>>>> Stashed changes
this._configureAdapters();
}

Expand Down Expand Up @@ -139,7 +147,14 @@ 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,
<<<<<<< Updated upstream
this._styleUtils
=======
this._styler
>>>>>>> Stashed changes
);
if (!this._ngClassInstance) {
// Create an instance NgClass Directive instance only if `ngClass=""` has NOT been defined on
Expand Down
15 changes: 14 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,13 @@ import {
} from '../../utils/testing/helpers';
import {ShowHideDirective} from './show-hide';
import {MediaQueriesModule} from '../../media-query/_module';
<<<<<<< Updated upstream
import {ServerStylesheet} from '../../utils/server-stylesheet';
import {StyleUtils} from '../../utils/style-utils';
=======
import {ServerStylesheet} from '../../utils/styling/server-stylesheet';
import {StylerService} from '../../utils/styling/styler';
>>>>>>> Stashed changes

describe('hide directive', () => {
let fixture: ComponentFixture<any>;
Expand Down Expand Up @@ -60,7 +67,13 @@ describe('hide directive', () => {
declarations: [TestHideComponent, ShowHideDirective],
providers: [
BreakPointRegistry, DEFAULT_BREAKPOINTS_PROVIDER,
{provide: MatchMedia, useClass: MockMatchMedia}
{provide: MatchMedia, useClass: MockMatchMedia},
ServerStylesheet,
<<<<<<< Updated upstream
StyleUtils,
=======
StylerService,
>>>>>>> Stashed changes
]
});
});
Expand Down
24 changes: 16 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,15 @@ import {
OnInit,
OnChanges,
Renderer2,
Inject,
PLATFORM_ID,
} from '@angular/core';

import {BaseFxDirective} from '../core/base';
import {MediaMonitor} from '../../media-query/media-monitor';
<<<<<<< Updated upstream
import {StyleUtils} from '../../utils/style-utils';
=======
import {StylerService} from '../../utils/styling/styler';
>>>>>>> Stashed changes

/**
* This directive provides a responsive API for the HTML <img> 'src' attribute
Expand Down Expand Up @@ -57,12 +60,17 @@ 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,
<<<<<<< Updated upstream
protected _styleUtils: StyleUtils) {
super(_monitor, _elRef, _styleUtils);
=======
protected _styler: StylerService) {
super(_monitor, _elRef, _styler);
>>>>>>> Stashed changes
this._cacheInput('src', _elRef.nativeElement.getAttribute('src') || '');
}

/**
Expand Down
Loading

0 comments on commit 193fdab

Please sign in to comment.