diff --git a/src/lib/core/base/base.ts b/src/lib/core/base/base.ts index 89e5bbe94..83802d347 100644 --- a/src/lib/core/base/base.ts +++ b/src/lib/core/base/base.ts @@ -109,9 +109,22 @@ export abstract class BaseDirective implements OnDestroy, OnChanges { return this._elementRef.nativeElement; } + /** Add styles to the element using predefined style builder */ protected addStyles(input: string, parent?: Object) { - const styles: StyleDefinition = this._styleBuilder!.buildStyles(input, parent); - this._applyStyleToElement(styles); + const builder = this._styleBuilder!; + const useCache = builder.shouldCache; + + let genStyles: StyleDefinition | undefined = this._styleCache.get(input); + + if (!genStyles || !useCache) { + genStyles = builder.buildStyles(input, parent); + if (useCache) { + this._styleCache.set(input, genStyles); + } + } + + this._applyStyleToElement(genStyles); + builder.sideEffect(input, genStyles, parent); } /** Access the current value (if any) of the @Input property */ @@ -246,4 +259,7 @@ export abstract class BaseDirective implements OnDestroy, OnChanges { * getComputedStyle() during ngOnInit(). */ protected _hasInitialized = false; + + /** Cache map for style computation */ + protected _styleCache: Map = new Map(); } diff --git a/src/lib/core/style-builder/style-builder.ts b/src/lib/core/style-builder/style-builder.ts index e9b078e14..27345861b 100644 --- a/src/lib/core/style-builder/style-builder.ts +++ b/src/lib/core/style-builder/style-builder.ts @@ -5,10 +5,22 @@ * 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 {Injectable} from '@angular/core'; import {StyleDefinition} from '../style-utils/style-utils'; -@Injectable() +/** A class that encapsulates CSS style generation for common directives */ export abstract class StyleBuilder { + + /** Whether to cache the generated output styles */ + shouldCache = true; + + /** Build the styles given an input string and configuration object from a host */ abstract buildStyles(input: string, parent?: Object): StyleDefinition; + + /** + * Run a side effect computation given the input string and the computed styles + * from the build task and the host configuration object + * NOTE: This should be a no-op unless an algorithm is provided in a subclass + */ + sideEffect(_input: string, _styles: StyleDefinition, _parent?: Object) { + } } diff --git a/src/lib/flex/flex-align/flex-align.ts b/src/lib/flex/flex-align/flex-align.ts index 71524a7fb..a41865aa2 100644 --- a/src/lib/flex/flex-align/flex-align.ts +++ b/src/lib/flex/flex-align/flex-align.ts @@ -21,28 +21,28 @@ import { MediaMonitor, StyleBuilder, StyleDefinition, - StyleUtils + StyleUtils, } from '@angular/flex-layout/core'; @Injectable({providedIn: 'root'}) -export class FlexAlignStyleBuilder implements StyleBuilder { - buildStyles(input: string): StyleDefinition { - const css: {[key: string]: string | number} = {}; +export class FlexAlignStyleBuilder extends StyleBuilder { + buildStyles(input: string) { + const styles: StyleDefinition = {}; // Cross-axis switch (input) { case 'start': - css['align-self'] = 'flex-start'; + styles['align-self'] = 'flex-start'; break; case 'end': - css['align-self'] = 'flex-end'; + styles['align-self'] = 'flex-end'; break; default: - css['align-self'] = input; + styles['align-self'] = input; break; } - return css; + return styles; } } @@ -125,4 +125,8 @@ export class FlexAlignDirective extends BaseDirective implements OnInit, OnChang this.addStyles(value && (value + '') || ''); } + + protected _styleCache = flexAlignCache; } + +const flexAlignCache: Map = new Map(); diff --git a/src/lib/flex/flex-fill/flex-fill.ts b/src/lib/flex/flex-fill/flex-fill.ts index afdcfc0e4..568f460fe 100644 --- a/src/lib/flex/flex-fill/flex-fill.ts +++ b/src/lib/flex/flex-fill/flex-fill.ts @@ -23,8 +23,8 @@ const FLEX_FILL_CSS = { }; @Injectable({providedIn: 'root'}) -export class FlexFillStyleBuilder implements StyleBuilder { - buildStyles(_input: string): StyleDefinition { +export class FlexFillStyleBuilder extends StyleBuilder { + buildStyles(_input: string) { return FLEX_FILL_CSS; } } @@ -47,4 +47,8 @@ export class FlexFillDirective extends BaseDirective { super(monitor, elRef, styleUtils, styleBuilder); this.addStyles(''); } + + protected _styleCache = flexFillCache; } + +const flexFillCache: Map = new Map(); diff --git a/src/lib/flex/flex-offset/flex-offset.spec.ts b/src/lib/flex/flex-offset/flex-offset.spec.ts index 65f34dd27..342fa0d4c 100644 --- a/src/lib/flex/flex-offset/flex-offset.spec.ts +++ b/src/lib/flex/flex-offset/flex-offset.spec.ts @@ -223,7 +223,7 @@ describe('flex-offset directive', () => { }); @Injectable({providedIn: FlexModule}) -export class MockFlexOffsetStyleBuilder implements StyleBuilder { +export class MockFlexOffsetStyleBuilder extends StyleBuilder { buildStyles(_input: string) { return {'margin-top': '10px'}; } diff --git a/src/lib/flex/flex-offset/flex-offset.ts b/src/lib/flex/flex-offset/flex-offset.ts index 2c9f3173d..d1a540ca8 100644 --- a/src/lib/flex/flex-offset/flex-offset.ts +++ b/src/lib/flex/flex-offset/flex-offset.ts @@ -31,23 +31,24 @@ import {Subscription} from 'rxjs'; import {Layout, LayoutDirective} from '../layout/layout'; import {isFlowHorizontal} from '../../utils/layout-validator'; -interface FlexOffsetParent { +export interface FlexOffsetParent { layout: string; isRtl: boolean; } @Injectable({providedIn: 'root'}) -export class FlexOffsetStyleBuilder implements StyleBuilder { - buildStyles(offset: string, parent: FlexOffsetParent): StyleDefinition { +export class FlexOffsetStyleBuilder extends StyleBuilder { + buildStyles(offset: string, parent: FlexOffsetParent) { const isPercent = String(offset).indexOf('%') > -1; const isPx = String(offset).indexOf('px') > -1; if (!isPx && !isPercent && !isNaN(+offset)) { offset = offset + '%'; } const horizontalLayoutKey = parent.isRtl ? 'margin-right' : 'margin-left'; - - return isFlowHorizontal(parent.layout) ? {[horizontalLayoutKey]: `${offset}`} : + const styles = isFlowHorizontal(parent.layout) ? {[horizontalLayoutKey]: `${offset}`} : {'margin-top': `${offset}`}; + + return styles; } } @@ -185,6 +186,20 @@ export class FlexOffsetDirective extends BaseDirective implements OnInit, OnChan // The flex-direction of this element's flex container. Defaults to 'row'. const layout = this._getFlexFlowDirection(this.parentElement, true); const isRtl = this._directionality.value === 'rtl'; + if (layout === 'row' && isRtl) { + this._styleCache = flexOffsetCacheRowRtl; + } else if (layout === 'row' && !isRtl) { + this._styleCache = flexOffsetCacheRowLtr; + } else if (layout === 'column' && isRtl) { + this._styleCache = flexOffsetCacheColumnRtl; + } else if (layout === 'column' && !isRtl) { + this._styleCache = flexOffsetCacheColumnLtr; + } this.addStyles((value && (value + '') || ''), {layout, isRtl}); } } + +const flexOffsetCacheRowRtl: Map = new Map(); +const flexOffsetCacheColumnRtl: Map = new Map(); +const flexOffsetCacheRowLtr: Map = new Map(); +const flexOffsetCacheColumnLtr: Map = new Map(); diff --git a/src/lib/flex/flex-order/flex-order.ts b/src/lib/flex/flex-order/flex-order.ts index 176aff157..644a8c784 100644 --- a/src/lib/flex/flex-order/flex-order.ts +++ b/src/lib/flex/flex-order/flex-order.ts @@ -21,14 +21,15 @@ import { MediaMonitor, StyleBuilder, StyleDefinition, - StyleUtils + StyleUtils, } from '@angular/flex-layout/core'; @Injectable({providedIn: 'root'}) -export class FlexOrderStyleBuilder implements StyleBuilder { - buildStyles(value: string): StyleDefinition { +export class FlexOrderStyleBuilder extends StyleBuilder { + buildStyles(value: string) { const val = parseInt(value, 10); - return {order: isNaN(val) ? 0 : val}; + const styles = {order: isNaN(val) ? 0 : val}; + return styles; } } @@ -108,4 +109,8 @@ export class FlexOrderDirective extends BaseDirective implements OnInit, OnChang this.addStyles(value || ''); } + + protected _styleCache = flexOrderCache; } + +const flexOrderCache: Map = new Map(); diff --git a/src/lib/flex/flex/flex.spec.ts b/src/lib/flex/flex/flex.spec.ts index c85fbd8e7..e10e1fae2 100644 --- a/src/lib/flex/flex/flex.spec.ts +++ b/src/lib/flex/flex/flex.spec.ts @@ -858,6 +858,7 @@ describe('flex directive', () => { }); describe('with column basis zero disabled', () => { + let styleBuilder: FlexStyleBuilder; beforeEach(() => { jasmine.addMatchers(customMatchers); @@ -875,16 +876,20 @@ describe('flex directive', () => { }); }); - it('should set flex basis to auto', async(() => { + it('should set flex basis to auto', () => { componentWithTemplate(`
`); + styleBuilder = TestBed.get(FlexStyleBuilder); + + // Reset the cache because the layout config is only set at startup + styleBuilder.shouldCache = false; fixture.detectChanges(); let element = queryFor(fixture, '[fxFlex]')[0]; expectEl(element).toHaveStyle({'flex': '1 1 auto'}, styler); - })); + }); }); describe('with custom builder', () => { @@ -926,7 +931,7 @@ describe('flex directive', () => { }); @Injectable({providedIn: FlexModule}) -export class MockFlexStyleBuilder implements StyleBuilder { +export class MockFlexStyleBuilder extends StyleBuilder { buildStyles(_input: string) { return {'flex': '1 1 30%'}; } diff --git a/src/lib/flex/flex/flex.ts b/src/lib/flex/flex/flex.ts index c3d9e3182..f4e34cd5c 100644 --- a/src/lib/flex/flex/flex.ts +++ b/src/lib/flex/flex/flex.ts @@ -41,16 +41,16 @@ export type FlexBasisAlias = 'grow' | 'initial' | 'auto' | 'none' | 'nogrow' | ' interface FlexBuilderParent { direction: string; hasWrap: boolean; - useColumnBasisZero: boolean | undefined; } @Injectable({providedIn: 'root'}) -export class FlexStyleBuilder implements StyleBuilder { - buildStyles(input: string, parent: FlexBuilderParent): StyleDefinition { - let grow: string | number; - let shrink: string | number; - let basis: string | number; - [grow, shrink, basis] = input.split('_'); +export class FlexStyleBuilder extends StyleBuilder { + constructor(@Inject(LAYOUT_CONFIG) protected layoutConfig: LayoutConfigOptions) { + super(); + } + buildStyles(input: string, parent: FlexBuilderParent) { + let [grow, shrink, ...basisParts]: (string|number)[] = input.split(' '); + let basis = basisParts.join(' '); // The flex-direction of this element's flex container. Defaults to 'row'. const direction = (parent.direction.indexOf('column') > -1) ? 'column' : 'row'; @@ -97,7 +97,7 @@ export class FlexStyleBuilder implements StyleBuilder { }; switch (basis || '') { case '': - const useColumnBasisZero = parent.useColumnBasisZero !== false; + const useColumnBasisZero = this.layoutConfig.useColumnBasisZero !== false; basis = direction === 'row' ? '0%' : (useColumnBasisZero ? '0.000000001px' : 'auto'); break; case 'initial': // default @@ -192,7 +192,7 @@ export class FlexStyleBuilder implements StyleBuilder { } } - return extendObject(css, {'box-sizing': 'border-box'}); + return extendObject(css, {'box-sizing': 'border-box'}) as StyleDefinition; } } @@ -310,12 +310,25 @@ export class FlexDirective extends BaseDirective implements OnInit, OnChanges, O flexBasis = this._mqActivation.activatedInput; } - let basis = String(flexBasis).replace(';', ''); - let parts = validateBasis(basis, this._queryInput('grow'), this._queryInput('shrink')); + const basis = String(flexBasis).replace(';', ''); + const parts = validateBasis(basis, this._queryInput('grow'), this._queryInput('shrink')); const addFlexToParent = this.layoutConfig.addFlexToParent !== false; const direction = this._getFlexFlowDirection(this.parentElement, addFlexToParent); const hasWrap = this._layout && this._layout.wrap; - const useColumnBasisZero = this.layoutConfig.useColumnBasisZero; - this.addStyles(parts.join('_'), {direction, hasWrap, useColumnBasisZero}); + if (direction === 'row' && hasWrap) { + this._styleCache = flexRowWrapCache; + } else if (direction === 'row' && !hasWrap) { + this._styleCache = flexRowCache; + } else if (direction === 'column' && hasWrap) { + this._styleCache = flexColumnWrapCache; + } else if (direction === 'column' && !hasWrap) { + this._styleCache = flexColumnCache; + } + this.addStyles(parts.join(' '), {direction, hasWrap}); } } + +const flexRowCache: Map = new Map(); +const flexColumnCache: Map = new Map(); +const flexRowWrapCache: Map = new Map(); +const flexColumnWrapCache: Map = new Map(); diff --git a/src/lib/flex/layout-align/layout-align.spec.ts b/src/lib/flex/layout-align/layout-align.spec.ts index b62b2bb2c..0feff33af 100644 --- a/src/lib/flex/layout-align/layout-align.spec.ts +++ b/src/lib/flex/layout-align/layout-align.spec.ts @@ -452,7 +452,8 @@ describe('layout-align directive', () => { }); @Injectable({providedIn: FlexModule}) -export class MockLayoutAlignStyleBuilder implements StyleBuilder { +export class MockLayoutAlignStyleBuilder extends StyleBuilder { + shouldCache = false; buildStyles(_input: string) { return {'justify-content': 'flex-end'}; } diff --git a/src/lib/flex/layout-align/layout-align.ts b/src/lib/flex/layout-align/layout-align.ts index 8761f4a4e..cc3cac287 100644 --- a/src/lib/flex/layout-align/layout-align.ts +++ b/src/lib/flex/layout-align/layout-align.ts @@ -23,7 +23,7 @@ import { MediaMonitor, StyleBuilder, StyleDefinition, - StyleUtils + StyleUtils, } from '@angular/flex-layout/core'; import {Subscription} from 'rxjs'; @@ -31,15 +31,14 @@ import {extendObject} from '../../utils/object-extend'; import {Layout, LayoutDirective} from '../layout/layout'; import {LAYOUT_VALUES, isFlowHorizontal} from '../../utils/layout-validator'; -interface LayoutAlignParent { +export interface LayoutAlignParent { layout: string; } @Injectable({providedIn: 'root'}) -export class LayoutAlignStyleBuilder implements StyleBuilder { - buildStyles(align: string, parent: LayoutAlignParent): StyleDefinition { - let css: {[key: string]: string} = {}, - [mainAxis, crossAxis] = align.split(' '); +export class LayoutAlignStyleBuilder extends StyleBuilder { + buildStyles(align: string, parent: LayoutAlignParent) { + const css: StyleDefinition = {}, [mainAxis, crossAxis] = align.split(' '); // Main axis switch (mainAxis) { @@ -105,7 +104,7 @@ export class LayoutAlignStyleBuilder implements StyleBuilder { !isFlowHorizontal(parent.layout) ? '100%' : null : null, 'max-height': crossAxis === 'stretch' ? isFlowHorizontal(parent.layout) ? '100%' : null : null, - }); + }) as StyleDefinition; } } @@ -204,6 +203,8 @@ export class LayoutAlignDirective extends BaseDirective implements OnInit, OnCha } const layout = this._layout || 'row'; + this._styleCache = layout === 'row' ? + layoutAlignHorizontalCache : layoutAlignVerticalCache; this.addStyles(value || '', {layout}); } @@ -223,3 +224,6 @@ export class LayoutAlignDirective extends BaseDirective implements OnInit, OnCha this.addStyles(value, {layout: this._layout || 'row'}); } } + +const layoutAlignHorizontalCache: Map = new Map(); +const layoutAlignVerticalCache: Map = new Map(); diff --git a/src/lib/flex/layout-gap/layout-gap.spec.ts b/src/lib/flex/layout-gap/layout-gap.spec.ts index 24c87c474..b322de8bb 100644 --- a/src/lib/flex/layout-gap/layout-gap.spec.ts +++ b/src/lib/flex/layout-gap/layout-gap.spec.ts @@ -434,7 +434,7 @@ describe('layout-gap directive', () => { }); @Injectable({providedIn: FlexModule}) -export class MockLayoutGapStyleBuilder implements StyleBuilder { +export class MockLayoutGapStyleBuilder extends StyleBuilder { buildStyles(_input: string) { return {'margin-top': '12px'}; } diff --git a/src/lib/flex/layout-gap/layout-gap.ts b/src/lib/flex/layout-gap/layout-gap.ts index 53c1bf488..e1c6ffb54 100644 --- a/src/lib/flex/layout-gap/layout-gap.ts +++ b/src/lib/flex/layout-gap/layout-gap.ts @@ -32,10 +32,10 @@ import {Subscription} from 'rxjs'; import {Layout, LayoutDirective} from '../layout/layout'; import {LAYOUT_VALUES} from '../../utils/layout-validator'; -interface LayoutGapParent { - layout: string; +export interface LayoutGapParent { directionality: string; items: HTMLElement[]; + layout: string; } const CLEAR_MARGIN_CSS = { @@ -46,56 +46,40 @@ const CLEAR_MARGIN_CSS = { }; @Injectable({providedIn: 'root'}) -export class LayoutGapStyleBuilder implements StyleBuilder { - constructor(private styler: StyleUtils) {} +export class LayoutGapStyleBuilder extends StyleBuilder { + constructor(private _styler: StyleUtils) { + super(); + } - buildStyles(gapValue: string, parent: LayoutGapParent): StyleDefinition { + buildStyles(gapValue: string, parent: LayoutGapParent) { + if (gapValue.endsWith(GRID_SPECIFIER)) { + gapValue = gapValue.slice(0, gapValue.indexOf(GRID_SPECIFIER)); + + // Add the margin to the host element + return buildGridMargin(gapValue, parent.directionality); + } else { + return {}; + } + } + + sideEffect(gapValue: string, _styles: StyleDefinition, parent: LayoutGapParent) { const items = parent.items; if (gapValue.endsWith(GRID_SPECIFIER)) { - gapValue = gapValue.substring(0, gapValue.indexOf(GRID_SPECIFIER)); + gapValue = gapValue.slice(0, gapValue.indexOf(GRID_SPECIFIER)); // For each `element` children, set the padding const paddingStyles = buildGridPadding(gapValue, parent.directionality); - const marginStyles = buildGridMargin(gapValue, parent.directionality); - this.styler.applyStyleToElements(paddingStyles, items); - - // Add the margin to the host element - return marginStyles; + this._styler.applyStyleToElements(paddingStyles, parent.items); } else { const lastItem = items.pop(); // For each `element` children EXCEPT the last, // set the margin right/bottom styles... - this.styler.applyStyleToElements(this._buildCSS(gapValue, parent), items); + const gapCss = buildGapCSS(gapValue, parent); + this._styler.applyStyleToElements(gapCss, items); // Clear all gaps for all visible elements - this.styler.applyStyleToElements(CLEAR_MARGIN_CSS, [lastItem!]); - return {}; - } - } - - private _buildCSS(gapValue: string, parent: LayoutGapParent): StyleDefinition { - let key, margins: {[key: string]: string | null} = {...CLEAR_MARGIN_CSS}; - - switch (parent.layout) { - case 'column': - key = 'margin-bottom'; - break; - case 'column-reverse': - key = 'margin-top'; - break; - case 'row': - key = parent.directionality === 'rtl' ? 'margin-left' : 'margin-right'; - break; - case 'row-reverse': - key = parent.directionality === 'rtl' ? 'margin-right' : 'margin-left'; - break; - default : - key = parent.directionality === 'rtl' ? 'margin-left' : 'margin-right'; - break; + this._styler.applyStyleToElements(CLEAR_MARGIN_CSS, [lastItem!]); } - margins[key] = gapValue; - - return margins; } } @@ -137,13 +121,13 @@ export class LayoutGapDirective extends BaseDirective @Input('fxLayoutGap.lt-xl') set gapLtXl(val: string) { this._cacheInput('gapLtXl', val); }; /* tslint:enable */ - constructor(monitor: MediaMonitor, - elRef: ElementRef, - @Optional() @Self() container: LayoutDirective, - private _zone: NgZone, - private _directionality: Directionality, - styleUtils: StyleUtils, - styleBuilder: LayoutGapStyleBuilder) { + constructor(protected monitor: MediaMonitor, + protected elRef: ElementRef, + @Optional() @Self() protected container: LayoutDirective, + protected _zone: NgZone, + protected _directionality: Directionality, + protected styleUtils: StyleUtils, + protected styleBuilder: LayoutGapStyleBuilder) { super(monitor, elRef, styleUtils, styleBuilder); if (container) { // Subscribe to layout direction changes @@ -250,15 +234,27 @@ export class LayoutGapDirective extends BaseDirective }); if (items.length > 0) { - this.addStyles(gapValue, { - directionality: this._directionality.value, - items, - layout: this._layout - }); + const directionality = this._directionality.value; + const layout = this._layout; + if (layout === 'row' && directionality === 'rtl') { + this._styleCache = layoutGapCacheRowRtl; + } else if (layout === 'row' && directionality !== 'rtl') { + this._styleCache = layoutGapCacheRowLtr; + } else if (layout === 'column' && directionality === 'rtl') { + this._styleCache = layoutGapCacheColumnRtl; + } else if (layout === 'column' && directionality !== 'rtl') { + this._styleCache = layoutGapCacheColumnLtr; + } + this.addStyles(gapValue, {directionality, items, layout}); } } } +const layoutGapCacheRowRtl: Map = new Map(); +const layoutGapCacheColumnRtl: Map = new Map(); +const layoutGapCacheRowLtr: Map = new Map(); +const layoutGapCacheColumnLtr: Map = new Map(); + const GRID_SPECIFIER = ' grid'; function buildGridPadding(value: string, directionality: string): StyleDefinition { @@ -284,3 +280,29 @@ function buildGridMargin(value: string, directionality: string): StyleDefinition return {'margin': `${marginTop} ${marginRight} ${marginBottom} ${marginLeft}`}; } + +function buildGapCSS(gapValue: string, + parent: {directionality: string, layout: string}): StyleDefinition { + let key, margins: {[key: string]: string | null} = {...CLEAR_MARGIN_CSS}; + + switch (parent.layout) { + case 'column': + key = 'margin-bottom'; + break; + case 'column-reverse': + key = 'margin-top'; + break; + case 'row': + key = parent.directionality === 'rtl' ? 'margin-left' : 'margin-right'; + break; + case 'row-reverse': + key = parent.directionality === 'rtl' ? 'margin-right' : 'margin-left'; + break; + default : + key = parent.directionality === 'rtl' ? 'margin-left' : 'margin-right'; + break; + } + margins[key] = gapValue; + + return margins; +} diff --git a/src/lib/flex/layout/layout.spec.ts b/src/lib/flex/layout/layout.spec.ts index 7cf922e29..8164548b7 100644 --- a/src/lib/flex/layout/layout.spec.ts +++ b/src/lib/flex/layout/layout.spec.ts @@ -22,8 +22,7 @@ import {customMatchers} from '../../utils/testing/custom-matchers'; import {makeCreateTestComponent, expectNativeEl, expectEl} from '../../utils/testing/helpers'; import {queryFor} from '../../utils/testing/helpers'; import {FlexModule} from '../module'; -import {Layout, LayoutStyleBuilder} from './layout'; -import {ReplaySubject} from 'rxjs'; +import {LayoutStyleBuilder} from './layout'; describe('layout directive', () => { let fixture: ComponentFixture; @@ -365,13 +364,10 @@ describe('layout directive', () => { }); @Injectable({providedIn: FlexModule}) -export class MockLayoutStyleBuilder implements StyleBuilder { - buildStyles(_input: string, parent: {announcer: ReplaySubject}) { - parent.announcer.next({ - direction: 'column', - wrap: false - }); - return {'display': 'inline-flex'}; +export class MockLayoutStyleBuilder extends StyleBuilder { + shouldCache = false; + buildStyles(_input: string) { + return {'display': 'inline-flex', 'flex-direction': 'row'}; } } diff --git a/src/lib/flex/layout/layout.ts b/src/lib/flex/layout/layout.ts index 161533a27..b90763413 100644 --- a/src/lib/flex/layout/layout.ts +++ b/src/lib/flex/layout/layout.ts @@ -21,7 +21,7 @@ import { MediaMonitor, StyleBuilder, StyleDefinition, - StyleUtils + StyleUtils, } from '@angular/flex-layout/core'; import {Observable, ReplaySubject} from 'rxjs'; @@ -32,19 +32,21 @@ export type Layout = { wrap: boolean; }; -interface LayoutParent { +export interface LayoutParent { announcer: ReplaySubject; } @Injectable({providedIn: 'root'}) -export class LayoutStyleBuilder implements StyleBuilder { - buildStyles(input: string, parent: LayoutParent): StyleDefinition { - const css = buildLayoutCSS(input); +export class LayoutStyleBuilder extends StyleBuilder { + buildStyles(input: string, _parent: LayoutParent) { + const styles = buildLayoutCSS(input); + return styles; + } + sideEffect(_input: string, styles: StyleDefinition, parent: LayoutParent) { parent.announcer.next({ - direction: css['flex-direction'], - wrap: !!css['flex-wrap'] && css['flex-wrap'] !== 'nowrap' + direction: styles['flex-direction'] as string, + wrap: !!styles['flex-wrap'] && styles['flex-wrap'] !== 'nowrap' }); - return css; } } @@ -140,8 +142,10 @@ export class LayoutDirective extends BaseDirective implements OnInit, OnChanges, if (this._mqActivation) { value = this._mqActivation.activatedInput; } - this.addStyles(value || '', {announcer: this._announcer}); } + protected _styleCache = layoutCache; } + +const layoutCache: Map = new Map();