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

Commit ba5285c

Browse files
committed
fixup! feat(core): add memoization to style generation
1 parent dd060a0 commit ba5285c

File tree

15 files changed

+131
-102
lines changed

15 files changed

+131
-102
lines changed

src/lib/core/base/base.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -112,16 +112,15 @@ export abstract class BaseDirective implements OnDestroy, OnChanges {
112112
/** Add styles to the element using predefined style builder */
113113
protected addStyles(input: string, parent?: Object) {
114114
let styleDefinition: StyleDefinition;
115-
if (this._styleCache.has(input)) {
115+
if (this._styleBuilder!.shouldCache && this._styleCache.has(input)) {
116116
styleDefinition = this._styleCache.get(input)!;
117117
this._applyStyleToElement(styleDefinition);
118118
} else {
119-
const {shouldCache, styles} = this._styleBuilder!.buildStyles(input, parent);
120-
this._applyStyleToElement(styles);
121-
if (shouldCache) {
122-
this._styleCache.set(input, styles);
119+
styleDefinition = this._styleBuilder!.buildStyles(input, parent);
120+
this._applyStyleToElement(styleDefinition);
121+
if (this._styleBuilder!.shouldCache) {
122+
this._styleCache.set(input, styleDefinition);
123123
}
124-
styleDefinition = styles;
125124
}
126125
this._styleBuilder!.sideEffect(input, styleDefinition, parent);
127126
}

src/lib/core/style-builder/style-builder.ts

+12-9
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,22 @@
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} from '@angular/core';
98
import {StyleDefinition} from '../style-utils/style-utils';
109

11-
@Injectable()
10+
/** A class that encapsulates CSS style generation for common directives */
1211
export abstract class StyleBuilder {
13-
abstract buildStyles(input: string, parent?: Object): StyleBuilderOutput;
1412

13+
/** Whether to cache the generated output styles */
14+
shouldCache = true;
15+
16+
/** Build the styles given an input string and configuration object from a host */
17+
abstract buildStyles(input: string, parent?: Object): StyleDefinition;
18+
19+
/**
20+
* Run a side effect computation given the input string and the computed styles
21+
* from the build task and the host configuration object
22+
* NOTE: This should be a no-op unless an algorithm is provided in a subclass
23+
*/
1524
sideEffect(_input: string, _styles: StyleDefinition, _parent?: Object) {
16-
// This should be a no-op unless an algorithm is provided in a subclass
1725
}
1826
}
19-
20-
export interface StyleBuilderOutput {
21-
styles: StyleDefinition;
22-
shouldCache: boolean;
23-
}

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

+8-7
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,14 @@ import {
2020
MediaChange,
2121
MediaMonitor,
2222
StyleBuilder,
23-
StyleBuilderOutput,
23+
StyleDefinition,
2424
StyleUtils,
2525
} from '@angular/flex-layout/core';
2626

2727
@Injectable({providedIn: 'root'})
2828
export class FlexAlignStyleBuilder extends StyleBuilder {
29-
constructor() {
30-
super();
31-
}
32-
buildStyles(input: string): StyleBuilderOutput {
33-
const styles: {[key: string]: string | number} = {};
29+
buildStyles(input: string) {
30+
const styles: StyleDefinition = {};
3431

3532
// Cross-axis
3633
switch (input) {
@@ -45,7 +42,7 @@ export class FlexAlignStyleBuilder extends StyleBuilder {
4542
break;
4643
}
4744

48-
return {styles, shouldCache: true};
45+
return styles;
4946
}
5047
}
5148

@@ -128,4 +125,8 @@ export class FlexAlignDirective extends BaseDirective implements OnInit, OnChang
128125

129126
this.addStyles(value && (value + '') || '');
130127
}
128+
129+
protected _styleCache = flexAlignCache;
131130
}
131+
132+
const flexAlignCache: Map<string, StyleDefinition> = new Map();

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

+7-6
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
BaseDirective,
1111
MediaMonitor,
1212
StyleBuilder,
13-
StyleBuilderOutput,
13+
StyleDefinition,
1414
StyleUtils,
1515
} from '@angular/flex-layout/core';
1616

@@ -24,11 +24,8 @@ const FLEX_FILL_CSS = {
2424

2525
@Injectable({providedIn: 'root'})
2626
export class FlexFillStyleBuilder extends StyleBuilder {
27-
constructor() {
28-
super();
29-
}
30-
buildStyles(_input: string): StyleBuilderOutput {
31-
return {styles: FLEX_FILL_CSS, shouldCache: true};
27+
buildStyles(_input: string) {
28+
return FLEX_FILL_CSS;
3229
}
3330
}
3431

@@ -50,4 +47,8 @@ export class FlexFillDirective extends BaseDirective {
5047
super(monitor, elRef, styleUtils, styleBuilder);
5148
this.addStyles('');
5249
}
50+
51+
protected _styleCache = flexFillCache;
5352
}
53+
54+
const flexFillCache: Map<string, StyleDefinition> = new Map();

src/lib/flex/flex-offset/flex-offset.spec.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,8 @@ describe('flex-offset directive', () => {
224224

225225
@Injectable({providedIn: FlexModule})
226226
export class MockFlexOffsetStyleBuilder extends StyleBuilder {
227-
constructor() {
228-
super();
229-
}
230227
buildStyles(_input: string) {
231-
return {styles: {'margin-top': '10px'}, shouldCache: false};
228+
return {'margin-top': '10px'};
232229
}
233230
}
234231

src/lib/flex/flex-offset/flex-offset.ts

+17-6
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
MediaChange,
2424
MediaMonitor,
2525
StyleBuilder,
26-
StyleBuilderOutput,
26+
StyleDefinition,
2727
StyleUtils,
2828
} from '@angular/flex-layout/core';
2929
import {Subscription} from 'rxjs';
@@ -38,10 +38,7 @@ export interface FlexOffsetParent {
3838

3939
@Injectable({providedIn: 'root'})
4040
export class FlexOffsetStyleBuilder extends StyleBuilder {
41-
constructor() {
42-
super();
43-
}
44-
buildStyles(offset: string, parent: FlexOffsetParent): StyleBuilderOutput {
41+
buildStyles(offset: string, parent: FlexOffsetParent) {
4542
const isPercent = String(offset).indexOf('%') > -1;
4643
const isPx = String(offset).indexOf('px') > -1;
4744
if (!isPx && !isPercent && !isNaN(+offset)) {
@@ -51,7 +48,7 @@ export class FlexOffsetStyleBuilder extends StyleBuilder {
5148
const styles = isFlowHorizontal(parent.layout) ? {[horizontalLayoutKey]: `${offset}`} :
5249
{'margin-top': `${offset}`};
5350

54-
return {styles, shouldCache: true};
51+
return styles;
5552
}
5653
}
5754

@@ -189,6 +186,20 @@ export class FlexOffsetDirective extends BaseDirective implements OnInit, OnChan
189186
// The flex-direction of this element's flex container. Defaults to 'row'.
190187
const layout = this._getFlexFlowDirection(this.parentElement, true);
191188
const isRtl = this._directionality.value === 'rtl';
189+
if (layout === 'row' && isRtl) {
190+
this._styleCache = flexOffsetCacheRowRtl;
191+
} else if (layout === 'row' && !isRtl) {
192+
this._styleCache = flexOffsetCacheRowLtr;
193+
} else if (layout === 'column' && isRtl) {
194+
this._styleCache = flexOffsetCacheColumnRtl;
195+
} else if (layout === 'column' && !isRtl) {
196+
this._styleCache = flexOffsetCacheColumnLtr;
197+
}
192198
this.addStyles((value && (value + '') || ''), {layout, isRtl});
193199
}
194200
}
201+
202+
const flexOffsetCacheRowRtl: Map<string, StyleDefinition> = new Map();
203+
const flexOffsetCacheColumnRtl: Map<string, StyleDefinition> = new Map();
204+
const flexOffsetCacheRowLtr: Map<string, StyleDefinition> = new Map();
205+
const flexOffsetCacheColumnLtr: Map<string, StyleDefinition> = new Map();

src/lib/flex/flex-order/flex-order.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,16 @@ import {
2020
MediaChange,
2121
MediaMonitor,
2222
StyleBuilder,
23-
StyleBuilderOutput,
23+
StyleDefinition,
2424
StyleUtils,
2525
} from '@angular/flex-layout/core';
2626

2727
@Injectable({providedIn: 'root'})
2828
export class FlexOrderStyleBuilder extends StyleBuilder {
29-
constructor() {
30-
super();
31-
}
32-
buildStyles(value: string): StyleBuilderOutput {
29+
buildStyles(value: string) {
3330
const val = parseInt(value, 10);
3431
const styles = {order: isNaN(val) ? 0 : val};
35-
return {styles, shouldCache: true};
32+
return styles;
3633
}
3734
}
3835

@@ -112,4 +109,8 @@ export class FlexOrderDirective extends BaseDirective implements OnInit, OnChang
112109

113110
this.addStyles(value || '');
114111
}
112+
113+
protected _styleCache = flexOrderCache;
115114
}
115+
116+
const flexOrderCache: Map<string, StyleDefinition> = new Map();

src/lib/flex/flex/flex.spec.ts

+13-9
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,7 @@ describe('flex directive', () => {
858858
});
859859

860860
describe('with column basis zero disabled', () => {
861+
let styleBuilder: FlexStyleBuilder;
861862
beforeEach(() => {
862863
jasmine.addMatchers(customMatchers);
863864

@@ -875,16 +876,22 @@ describe('flex directive', () => {
875876
});
876877
});
877878

878-
it('should set flex basis to auto', async(() => {
879+
it('should set flex basis to auto', () => {
879880
componentWithTemplate(`
880881
<div fxLayout='column'>
881882
<div fxFlex></div>
882883
</div>
883884
`);
884-
fixture.detectChanges();
885-
let element = queryFor(fixture, '[fxFlex]')[0];
886-
expectEl(element).toHaveStyle({'flex': '1 1 auto'}, styler);
887-
}));
885+
inject([FlexStyleBuilder], (_styleBuilder: FlexStyleBuilder) => {
886+
styleBuilder = _styleBuilder;
887+
888+
// Reset the cache because the layout config is only set at startup
889+
styleBuilder.shouldCache = false;
890+
fixture.detectChanges();
891+
let element = queryFor(fixture, '[fxFlex]')[0];
892+
expectEl(element).toHaveStyle({'flex': '1 1 auto'}, styler);
893+
});
894+
});
888895
});
889896

890897
describe('with custom builder', () => {
@@ -927,11 +934,8 @@ describe('flex directive', () => {
927934

928935
@Injectable({providedIn: FlexModule})
929936
export class MockFlexStyleBuilder extends StyleBuilder {
930-
constructor() {
931-
super();
932-
}
933937
buildStyles(_input: string) {
934-
return {styles: {'flex': '1 1 30%'}, shouldCache: false};
938+
return {'flex': '1 1 30%'};
935939
}
936940
}
937941

src/lib/flex/flex/flex.ts

+24-14
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
StyleUtils,
2828
validateBasis,
2929
StyleBuilder,
30-
StyleBuilderOutput,
30+
StyleDefinition,
3131
} from '@angular/flex-layout/core';
3232
import {Subscription} from 'rxjs';
3333

@@ -41,19 +41,16 @@ export type FlexBasisAlias = 'grow' | 'initial' | 'auto' | 'none' | 'nogrow' | '
4141
interface FlexBuilderParent {
4242
direction: string;
4343
hasWrap: boolean;
44-
useColumnBasisZero: boolean | undefined;
4544
}
4645

4746
@Injectable({providedIn: 'root'})
4847
export class FlexStyleBuilder extends StyleBuilder {
49-
constructor() {
48+
constructor(@Inject(LAYOUT_CONFIG) protected layoutConfig: LayoutConfigOptions) {
5049
super();
5150
}
52-
buildStyles(input: string, parent: FlexBuilderParent): StyleBuilderOutput {
53-
let grow: string | number;
54-
let shrink: string | number;
55-
let basis: string | number;
56-
[grow, shrink, basis] = input.split('_');
51+
buildStyles(input: string, parent: FlexBuilderParent) {
52+
let [grow, shrink, ...basisParts]: (string|number)[] = input.split(' ');
53+
let basis = basisParts.join(' ');
5754

5855
// The flex-direction of this element's flex container. Defaults to 'row'.
5956
const direction = (parent.direction.indexOf('column') > -1) ? 'column' : 'row';
@@ -100,7 +97,7 @@ export class FlexStyleBuilder extends StyleBuilder {
10097
};
10198
switch (basis || '') {
10299
case '':
103-
const useColumnBasisZero = parent.useColumnBasisZero !== false;
100+
const useColumnBasisZero = this.layoutConfig.useColumnBasisZero !== false;
104101
basis = direction === 'row' ? '0%' : (useColumnBasisZero ? '0.000000001px' : 'auto');
105102
break;
106103
case 'initial': // default
@@ -195,7 +192,7 @@ export class FlexStyleBuilder extends StyleBuilder {
195192
}
196193
}
197194

198-
return {styles: extendObject(css, {'box-sizing': 'border-box'}), shouldCache: true};
195+
return extendObject(css, {'box-sizing': 'border-box'}) as StyleDefinition;
199196
}
200197
}
201198

@@ -313,12 +310,25 @@ export class FlexDirective extends BaseDirective implements OnInit, OnChanges, O
313310
flexBasis = this._mqActivation.activatedInput;
314311
}
315312

316-
let basis = String(flexBasis).replace(';', '');
317-
let parts = validateBasis(basis, this._queryInput('grow'), this._queryInput('shrink'));
313+
const basis = String(flexBasis).replace(';', '');
314+
const parts = validateBasis(basis, this._queryInput('grow'), this._queryInput('shrink'));
318315
const addFlexToParent = this.layoutConfig.addFlexToParent !== false;
319316
const direction = this._getFlexFlowDirection(this.parentElement, addFlexToParent);
320317
const hasWrap = this._layout && this._layout.wrap;
321-
const useColumnBasisZero = this.layoutConfig.useColumnBasisZero;
322-
this.addStyles(parts.join('_'), {direction, hasWrap, useColumnBasisZero});
318+
if (direction === 'row' && hasWrap) {
319+
this._styleCache = flexRowWrapCache;
320+
} else if (direction === 'row' && !hasWrap) {
321+
this._styleCache = flexRowCache;
322+
} else if (direction === 'column' && hasWrap) {
323+
this._styleCache = flexColumnWrapCache;
324+
} else if (direction === 'column' && !hasWrap) {
325+
this._styleCache = flexColumnCache;
326+
}
327+
this.addStyles(parts.join(' '), {direction, hasWrap});
323328
}
324329
}
330+
331+
const flexRowCache: Map<string, StyleDefinition> = new Map();
332+
const flexColumnCache: Map<string, StyleDefinition> = new Map();
333+
const flexRowWrapCache: Map<string, StyleDefinition> = new Map();
334+
const flexColumnWrapCache: Map<string, StyleDefinition> = new Map();

src/lib/flex/layout-align/layout-align.spec.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -453,11 +453,9 @@ describe('layout-align directive', () => {
453453

454454
@Injectable({providedIn: FlexModule})
455455
export class MockLayoutAlignStyleBuilder extends StyleBuilder {
456-
constructor() {
457-
super();
458-
}
456+
shouldCache = false;
459457
buildStyles(_input: string) {
460-
return {styles: {'justify-content': 'flex-end'}, shouldCache: false};
458+
return {'justify-content': 'flex-end'};
461459
}
462460
}
463461

0 commit comments

Comments
 (0)