diff --git a/src/lib/grid-list/grid-list-base.ts b/src/lib/grid-list/grid-list-base.ts new file mode 100644 index 00000000000..e64fef73dbf --- /dev/null +++ b/src/lib/grid-list/grid-list-base.ts @@ -0,0 +1,25 @@ +/** + * @license + * Copyright Google LLC 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 {InjectionToken} from '@angular/core'; + +/** + * Injection token used to provide a grid list to a tile and to avoid circular imports. + * @docs-private + */ +export const MAT_GRID_LIST = new InjectionToken('MAT_GRID_LIST'); + +/** + * Base interface for a `MatGridList`. + * @docs-private + */ +export interface MatGridListBase { + cols: number; + gutterSize: string; + rowHeight: number | string; +} diff --git a/src/lib/grid-list/grid-list.spec.ts b/src/lib/grid-list/grid-list.spec.ts index de774580e13..a62579af44f 100644 --- a/src/lib/grid-list/grid-list.spec.ts +++ b/src/lib/grid-list/grid-list.spec.ts @@ -139,6 +139,18 @@ describe('MatGridList', () => { expect(getStyle(tiles[2], 'top')).toBe('101px'); }); + it('should lay out the tiles correctly for a nested grid list', () => { + const fixture = createComponent(NestedGridList); + fixture.detectChanges(); + + const innerTiles = fixture.debugElement.queryAll( + By.css('mat-grid-tile mat-grid-list mat-grid-tile')); + + expect(getStyle(innerTiles[0], 'top')).toBe('0px'); + expect(getStyle(innerTiles[1], 'top')).toBe('101px'); + expect(getStyle(innerTiles[2], 'top')).toBe('202px'); + }); + it('should set the gutter size if passed', () => { const fixture = createComponent(GridListWithGutterSize); fixture.detectChanges(); @@ -635,3 +647,19 @@ class GridListWithRtl { } ` }) class GridListWithIndirectTileDescendants {} + + +@Component({template: ` +
+ + + + + + + + + + +
`}) +class NestedGridList { } diff --git a/src/lib/grid-list/grid-list.ts b/src/lib/grid-list/grid-list.ts index 73828c08b8d..0a15d12ad01 100644 --- a/src/lib/grid-list/grid-list.ts +++ b/src/lib/grid-list/grid-list.ts @@ -23,6 +23,7 @@ import {TileCoordinator} from './tile-coordinator'; import {TileStyler, FitTileStyler, RatioTileStyler, FixedTileStyler} from './tile-styler'; import {Directionality} from '@angular/cdk/bidi'; import {coerceNumberProperty} from '@angular/cdk/coercion'; +import {MAT_GRID_LIST, MatGridListBase} from './grid-list-base'; // TODO(kara): Conditional (responsive) column count / row size. @@ -40,10 +41,14 @@ const MAT_FIT_MODE = 'fit'; host: { 'class': 'mat-grid-list', }, + providers: [{ + provide: MAT_GRID_LIST, + useExisting: MatGridList + }], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, }) -export class MatGridList implements OnInit, AfterContentChecked { +export class MatGridList implements MatGridListBase, OnInit, AfterContentChecked { /** Number of columns being rendered. */ private _cols: number; @@ -139,16 +144,18 @@ export class MatGridList implements OnInit, AfterContentChecked { /** Computes and applies the size and position for all children grid tiles. */ private _layoutTiles(): void { if (!this._tileCoordinator) { - this._tileCoordinator = new TileCoordinator(this._tiles); + this._tileCoordinator = new TileCoordinator(); } + const tracker = this._tileCoordinator; + const tiles = this._tiles.filter(tile => !tile._gridList || tile._gridList === this); const direction = this._dir ? this._dir.value : 'ltr'; - this._tileCoordinator.update(this.cols); + this._tileCoordinator.update(this.cols, tiles); this._tileStyler.init(this.gutterSize, tracker, this.cols, direction); - this._tiles.forEach((tile, index) => { + tiles.forEach((tile, index) => { const pos = tracker.positions[index]; this._tileStyler.setStyle(tile, pos.row, pos.col); }); diff --git a/src/lib/grid-list/grid-tile.ts b/src/lib/grid-list/grid-tile.ts index 685795d52f1..1e3817a983e 100644 --- a/src/lib/grid-list/grid-tile.ts +++ b/src/lib/grid-list/grid-tile.ts @@ -11,14 +11,17 @@ import { ViewEncapsulation, ElementRef, Input, + Optional, ContentChildren, QueryList, AfterContentInit, Directive, ChangeDetectionStrategy, + Inject, } from '@angular/core'; import {MatLine, MatLineSetter} from '@angular/material/core'; import {coerceNumberProperty} from '@angular/cdk/coercion'; +import {MAT_GRID_LIST, MatGridListBase} from './grid-list-base'; @Component({ moduleId: module.id, @@ -36,7 +39,9 @@ export class MatGridTile { _rowspan: number = 1; _colspan: number = 1; - constructor(private _element: ElementRef) {} + constructor( + private _element: ElementRef, + @Optional() @Inject(MAT_GRID_LIST) public _gridList?: MatGridListBase) {} /** Amount of rows that the grid tile takes up. */ @Input() diff --git a/src/lib/grid-list/tile-coordinator.ts b/src/lib/grid-list/tile-coordinator.ts index 1400973251c..e0c36b5e9b4 100644 --- a/src/lib/grid-list/tile-coordinator.ts +++ b/src/lib/grid-list/tile-coordinator.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {QueryList} from '@angular/core'; import {MatGridTile} from './grid-tile'; /** @@ -53,19 +52,17 @@ export class TileCoordinator { /** The computed (row, col) position of each tile (the output). */ positions: TilePosition[]; - constructor(private _tiles: QueryList) {} - /** * Updates the tile positions. * @param numColumns Amount of columns in the grid. */ - update(numColumns: number) { + update(numColumns: number, tiles: MatGridTile[]) { this.columnIndex = 0; this.rowIndex = 0; this.tracker = new Array(numColumns); this.tracker.fill(0, 0, this.tracker.length); - this.positions = this._tiles.map(tile => this._trackTile(tile)); + this.positions = tiles.map(tile => this._trackTile(tile)); } /** Calculates the row and col position of a tile. */