diff --git a/src/lib/flexbox/api/base.ts b/src/lib/flexbox/api/base.ts
index aba765f38..faca463d9 100644
--- a/src/lib/flexbox/api/base.ts
+++ b/src/lib/flexbox/api/base.ts
@@ -8,6 +8,7 @@
import {ElementRef, Renderer, OnDestroy} from '@angular/core';
import {applyCssPrefixes} from '../../utils/auto-prefixer';
+import {buildLayoutCSS} from '../../utils/layout-validator';
import {ResponsiveActivation, KeyOptions} from '../responsive/responsive-activation';
import {MediaMonitor} from '../../media-query/media-monitor';
@@ -93,6 +94,28 @@ export abstract class BaseFxDirective implements OnDestroy {
return value.trim();
}
+ protected _getParentFlowDirection(source?: HTMLElement, addIfMissing = true): string {
+ let value = "";
+ let element: HTMLElement = source || this._elementRef.nativeElement;
+ let parent = element ? element.parentNode : null;
+
+ if ( parent ) {
+ let directionKeys = Object.keys(applyCssPrefixes({'flex-direction': ''}));
+ let findDirection = (target) => directionKeys.reduce((direction, key) => {
+ return direction || target[key];
+ }, null);
+
+ let immediateValue = findDirection(parent['style']);
+ value = immediateValue || findDirection(getComputedStyle(parent as Element));
+ if ( !immediateValue && addIfMissing ) {
+ value = value || 'row';
+ this._applyStyleToElements(buildLayoutCSS(value), [element.parentElement]);
+ }
+ }
+
+ return value ? value.trim() : "";
+ }
+
/**
* Applies styles given via string pair or object map to the directive element.
*/
diff --git a/src/lib/flexbox/api/flex.spec.ts b/src/lib/flexbox/api/flex.spec.ts
index e0bb98088..aeb14c00f 100644
--- a/src/lib/flexbox/api/flex.spec.ts
+++ b/src/lib/flexbox/api/flex.spec.ts
@@ -103,6 +103,58 @@ describe('flex directive', () => {
});
});
+ it('should work fxLayout parents', () => {
+ fixture = componentWithTemplate(`
+
+ `);
+ fixture.detectChanges();
+ let nodes = queryFor(fixture, "[fxFlex]");
+
+ // parent flex-direction found with 'column' with child height stylings
+ expect(nodes[0].nativeElement).toHaveCssStyle({ 'min-height': '30px' });
+ expect(nodes[0].nativeElement).not.toHaveCssStyle({ 'min-width': '30px' });
+ });
+
+ it('should not work with non-direct-parent fxLayouts', async(() => {
+ fixture = componentWithTemplate(`
+
+ `);
+ fixture.detectChanges();
+ let element = queryFor(fixture, "[fxFlex]")[0].nativeElement;
+ let parent = queryFor(fixture, ".test")[0].nativeElement;
+
+ setTimeout(() => {
+ // The parent flex-direction not found;
+ // A flex-direction should have been auto-injected to the parent...
+ // fallback to 'row' and set child width styles accordingly
+ expect(parent).toHaveCssStyle({ 'flex-direction': 'row' });
+ expect(element).toHaveCssStyle({ 'min-width': '40px' });
+ expect(element).not.toHaveCssStyle({ 'min-height': '40px' });
+ });
+
+ }));
+
+ it('should work with styled-parent flex directions', () => {
+ fixture = componentWithTemplate(`
+
+ `);
+ fixture.detectChanges();
+ let nodes = queryFor(fixture, "[fxFlex]");
+
+ // parent flex-direction found with 'column'; set child with height styles
+ expect(nodes[0].nativeElement).toHaveCssStyle({ 'min-height': '60px' });
+ });
+
it('should work with "1 1 auto" values', () => {
fixture = componentWithTemplate(`
diff --git a/src/lib/flexbox/api/flex.ts b/src/lib/flexbox/api/flex.ts
index a25135e7e..ab7bab5db 100644
--- a/src/lib/flexbox/api/flex.ts
+++ b/src/lib/flexbox/api/flex.ts
@@ -17,14 +17,12 @@
SimpleChanges,
SkipSelf,
} from '@angular/core';
-import {Subscription} from 'rxjs/Subscription';
import {extendObject} from '../../utils/object-extend';
import {BaseFxDirective} from './base';
import {MediaChange} from '../../media-query/media-change';
import {MediaMonitor} from '../../media-query/media-monitor';
-import {LayoutDirective} from './layout';
import {LayoutWrapDirective} from './layout-wrap';
import {validateBasis} from '../../utils/basis-validator';
@@ -51,12 +49,6 @@ export class FlexDirective extends BaseFxDirective implements OnInit, OnChanges,
/** The flex-direction of this element's flex container. Defaults to 'row'. */
protected _layout = 'row';
- /**
- * Subscription to the parent flex container's layout changes.
- * Stored so we can unsubscribe when this directive is destroyed.
- */
- protected _layoutWatcher: Subscription;
-
/* tslint:disable */
@Input('fxShrink') set shrink(val) { this._cacheInput("shrink", val); };
@Input('fxGrow') set grow(val) { this._cacheInput("grow", val); };
@@ -84,7 +76,6 @@ export class FlexDirective extends BaseFxDirective implements OnInit, OnChanges,
constructor(monitor: MediaMonitor,
elRef: ElementRef,
renderer: Renderer,
- @Optional() @SkipSelf() protected _container: LayoutDirective,
@Optional() @SkipSelf() protected _wrap: LayoutWrapDirective) {
super(monitor, elRef, renderer);
@@ -92,15 +83,6 @@ export class FlexDirective extends BaseFxDirective implements OnInit, OnChanges,
this._cacheInput("flex", "");
this._cacheInput("shrink", 1);
this._cacheInput("grow", 1);
-
- if (_container) {
- // If this flex item is inside of a flex container marked with
- // Subscribe to layout immediate parent direction changes
- this._layoutWatcher = _container.layout$.subscribe((direction) => {
- // `direction` === null if parent container does not have a `fxLayout`
- this._onLayoutChange(direction);
- });
- }
}
/**
@@ -108,7 +90,7 @@ export class FlexDirective extends BaseFxDirective implements OnInit, OnChanges,
*/
ngOnChanges(changes: SimpleChanges) {
if (changes['flex'] != null || this._mqActivation) {
- this._onLayoutChange();
+ this._updateStyle();
}
}
@@ -120,27 +102,17 @@ export class FlexDirective extends BaseFxDirective implements OnInit, OnChanges,
this._listenForMediaQueryChanges('flex', '', (changes: MediaChange) => {
this._updateStyle(changes.value);
});
- this._onLayoutChange();
+ this._updateStyle();
}
ngOnDestroy() {
super.ngOnDestroy();
- if (this._layoutWatcher) {
- this._layoutWatcher.unsubscribe();
- }
}
- /**
- * Caches the parent container's 'flex-direction' and updates the element's style.
- * Used as a handler for layout change events from the parent flex container.
- */
- protected _onLayoutChange(direction?: string) {
- this._layout = direction || this._layout || "row";
- this._updateStyle();
- }
-
protected _updateStyle(value?: string|number) {
+ this._layout = this._getParentFlowDirection() || "row";
+
let flexBasis = value || this._queryInput("flex") || '';
if (this._mqActivation) {
flexBasis = this._mqActivation.activatedInput;
@@ -159,7 +131,7 @@ export class FlexDirective extends BaseFxDirective implements OnInit, OnChanges,
shrink: number|string,
basis: string|number|FlexBasisAlias) {
let css, isValue;
- let direction = (this._layout === 'column') || (this._layout == 'column-reverse') ?
+ let direction = (this._layout === 'column') || (this._layout === 'column-reverse') ?
'column' :
'row';
diff --git a/src/lib/flexbox/api/layout-align.ts b/src/lib/flexbox/api/layout-align.ts
index 828c5eac5..25b067632 100644
--- a/src/lib/flexbox/api/layout-align.ts
+++ b/src/lib/flexbox/api/layout-align.ts
@@ -23,7 +23,8 @@ import {BaseFxDirective} from './base';
import {MediaChange} from '../../media-query/media-change';
import {MediaMonitor} from '../../media-query/media-monitor';
-import {LAYOUT_VALUES, LayoutDirective} from './layout';
+import {LayoutDirective} from './layout';
+import {LAYOUT_VALUES} from '../../utils/layout-validator';
/**
diff --git a/src/lib/flexbox/api/layout-gap.ts b/src/lib/flexbox/api/layout-gap.ts
index e1f9213ef..ea09302b8 100644
--- a/src/lib/flexbox/api/layout-gap.ts
+++ b/src/lib/flexbox/api/layout-gap.ts
@@ -20,10 +20,10 @@ import {
import {Subscription} from 'rxjs/Subscription';
import {BaseFxDirective} from './base';
+import {LayoutDirective} from './layout';
import {MediaChange} from '../../media-query/media-change';
import {MediaMonitor} from '../../media-query/media-monitor';
-import {LayoutDirective, LAYOUT_VALUES} from './layout';
-
+import {LAYOUT_VALUES} from '../../utils/layout-validator';
/**
* 'layout-padding' styling directive
* Defines padding of child elements in a layout container
diff --git a/src/lib/flexbox/api/layout-wrap.ts b/src/lib/flexbox/api/layout-wrap.ts
index a1330b50d..bfd3ce5d1 100644
--- a/src/lib/flexbox/api/layout-wrap.ts
+++ b/src/lib/flexbox/api/layout-wrap.ts
@@ -19,10 +19,10 @@ import {Subscription} from 'rxjs/Subscription';
import {extendObject} from '../../utils/object-extend';
import {BaseFxDirective} from './base';
+import {LayoutDirective} from './layout';
import {MediaChange} from '../../media-query/media-change';
import {MediaMonitor} from '../../media-query/media-monitor';
-import {LayoutDirective, LAYOUT_VALUES} from './layout';
-
+import {validateWrapValue, LAYOUT_VALUES} from '../../utils/layout-validator';
/**
* @deprecated
* This functionality is now part of the `fxLayout` API
@@ -118,7 +118,7 @@ export class LayoutWrapDirective extends BaseFxDirective implements OnInit, OnCh
if (this._mqActivation) {
value = this._mqActivation.activatedInput;
}
- value = this._validateValue(value);
+ value = validateWrapValue(value);
this._applyStyleToElement(this._buildCSS(value));
}
@@ -133,28 +133,4 @@ export class LayoutWrapDirective extends BaseFxDirective implements OnInit, OnCh
'flex-direction' : this._layout || 'row'
});
}
-
- /**
- * Convert layout-wrap="" to expected flex-wrap style
- */
- protected _validateValue(value) {
- switch (value.toLowerCase()) {
- case 'reverse':
- case 'wrap-reverse':
- value = 'wrap-reverse';
- break;
-
- case 'no':
- case 'none':
- case 'nowrap':
- value = 'nowrap';
- break;
-
- // All other values fallback to "wrap"
- default:
- value = 'wrap';
- break;
- }
- return value;
- }
}
diff --git a/src/lib/flexbox/api/layout.ts b/src/lib/flexbox/api/layout.ts
index 0a90863eb..5fe25940d 100644
--- a/src/lib/flexbox/api/layout.ts
+++ b/src/lib/flexbox/api/layout.ts
@@ -21,9 +21,7 @@ import {Observable} from 'rxjs/Observable';
import {BaseFxDirective} from './base';
import {MediaChange} from '../../media-query/media-change';
import {MediaMonitor} from '../../media-query/media-monitor';
-
-export const LAYOUT_VALUES = ['row', 'column', 'row-reverse', 'column-reverse'];
-
+import {buildLayoutCSS} from '../../utils/layout-validator';
/**
* 'layout' flexbox styling directive
* Defines the positioning flow direction for the child elements: row or column
@@ -117,73 +115,17 @@ export class LayoutDirective extends BaseFxDirective implements OnInit, OnChange
if (this._mqActivation) {
value = this._mqActivation.activatedInput;
}
- let [direction, wrap] = this._validateValue(value);
// Update styles and announce to subscribers the *new* direction
- this._applyStyleToElement(this._buildCSS(direction, wrap));
- this._announcer.next(direction);
+ let css = buildLayoutCSS(value);
+
+ this._applyStyleToElement(css);
+ this._announcer.next(css['flex-direction']);
}
- /**
- * Build the CSS that should be assigned to the element instance
- * BUG:
- *
- * 1) min-height on a column flex container won’t apply to its flex item children in IE 10-11.
- * Use height instead if possible; height : vh;
- *
- * @todo - update all child containers to have "box-sizing: border-box"
- * This way any padding or border specified on the child elements are
- * laid out and drawn inside that element's specified width and height.
- *
- */
- protected _buildCSS(direction, wrap = null) {
- return {
- 'display': 'flex',
- 'box-sizing': 'border-box',
- 'flex-direction': direction,
- 'flex-wrap': !!wrap ? wrap : null
- };
- }
- /**
- * Validate the value to be one of the acceptable value options
- * Use default fallback of "row"
- */
- protected _validateValue(value) {
- value = value ? value.toLowerCase() : '';
- let [ direction, wrap ] = value.split(" ");
- if (!LAYOUT_VALUES.find(x => x === direction)) {
- direction = LAYOUT_VALUES[0];
- }
- return [direction, this._validateWrapValue(wrap)];
- }
- /**
- * Convert layout-wrap="" to expected flex-wrap style
- */
- protected _validateWrapValue(value) {
- if (!!value) {
- switch (value.toLowerCase()) {
- case 'reverse':
- case 'wrap-reverse':
- case 'reverse-wrap':
- value = 'wrap-reverse';
- break;
-
- case 'no':
- case 'none':
- case 'nowrap':
- value = 'nowrap';
- break;
-
- // All other values fallback to "wrap"
- default:
- value = 'wrap';
- break;
- }
- }
- return value;
- }
+
}
diff --git a/src/lib/utils/basis-validator.ts b/src/lib/utils/basis-validator.ts
index 24c30caa5..5a15d9911 100644
--- a/src/lib/utils/basis-validator.ts
+++ b/src/lib/utils/basis-validator.ts
@@ -1,4 +1,12 @@
/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * 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
+ */
+
+ /**
* The flex API permits 3 or 1 parts of the value:
* - `flex-grow flex-shrink flex-basis`, or
* - `flex-basis`
diff --git a/src/lib/utils/breakpoint-tools.ts b/src/lib/utils/breakpoint-tools.ts
index 008812b88..a6c4af0dd 100644
--- a/src/lib/utils/breakpoint-tools.ts
+++ b/src/lib/utils/breakpoint-tools.ts
@@ -1,4 +1,12 @@
-import {BreakPoint} from '../media-query/breakpoints/break-point';
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * 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 {BreakPoint} from '../media-query/breakpoints/break-point';
import {extendObject} from './object-extend';
const ALIAS_DELIMITERS = /(\.|-|_)/g;
diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts
index a6b236653..003f87346 100644
--- a/src/lib/utils/index.ts
+++ b/src/lib/utils/index.ts
@@ -9,6 +9,7 @@
export * from './add-alias';
export * from './auto-prefixer';
export * from './basis-validator';
+export * from './layout-validator';
export * from './breakpoint-tools';
export * from './object-extend';
export * from './style-transforms';
diff --git a/src/lib/utils/layout-validator.ts b/src/lib/utils/layout-validator.ts
new file mode 100644
index 000000000..61ad95bbb
--- /dev/null
+++ b/src/lib/utils/layout-validator.ts
@@ -0,0 +1,76 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * 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
+ */
+
+export const LAYOUT_VALUES = ['row', 'column', 'row-reverse', 'column-reverse'];
+
+/**
+ * Validate the direction|"direction wrap" value and then update the host's inline flexbox styles
+ */
+export function buildLayoutCSS(value: string) {
+ let [direction, wrap] = validateValue(value);
+ return buildCSS(direction, wrap);
+ }
+
+/**
+ * Convert layout-wrap="" to expected flex-wrap style
+ */
+export function validateWrapValue(value) {
+ if (!!value) {
+ switch (value.toLowerCase()) {
+ case 'reverse':
+ case 'wrap-reverse':
+ case 'reverse-wrap':
+ value = 'wrap-reverse';
+ break;
+
+ case 'no':
+ case 'none':
+ case 'nowrap':
+ value = 'nowrap';
+ break;
+
+ // All other values fallback to "wrap"
+ default:
+ value = 'wrap';
+ break;
+ }
+ }
+ return value;
+}
+
+
+/**
+ * Validate the value to be one of the acceptable value options
+ * Use default fallback of "row"
+ */
+function validateValue(value: string) {
+ value = value ? value.toLowerCase() : '';
+ let [direction, wrap] = value.split(" ");
+ if (!LAYOUT_VALUES.find(x => x === direction)) {
+ direction = LAYOUT_VALUES[0];
+ }
+ return [direction, validateWrapValue(wrap)];
+}
+
+/**
+ * Build the CSS that should be assigned to the element instance
+ * BUG:
+ * 1) min-height on a column flex container won’t apply to its flex item children in IE 10-11.
+ * Use height instead if possible; height : vh;
+ *
+ * This way any padding or border specified on the child elements are
+ * laid out and drawn inside that element's specified width and height.
+ */
+function buildCSS(direction, wrap = null) {
+ return {
+ 'display': 'flex',
+ 'box-sizing': 'border-box',
+ 'flex-direction': direction,
+ 'flex-wrap': !!wrap ? wrap : null
+ };
+}