網站搜尋

javascript - 當元件首次載入時,改變 mat-grid-list 的欄數


我正在實作一個 mat-grid-list 圖片列表,並且根據螢幕大小動態改變欄位數量。這運作得很好,除了在第一次載入時,無論螢幕大小如何,它都會預設為 3 欄,直到發生某個事件。所以如果你導航到另一個元件再回來,你會再次看到 3 欄。我嘗試過在構造函數或其他生命週期鉤子中添加一些程式碼,但沒有成功,可能只是我沒有正確實作。在 TypeScript 檔案中,products 物件陣列、構造函數,以及 ngOnInit() 鉤子都不相關。第一張圖片是它載入時的樣子,因為預設是 3 欄,第二張圖片是我點擊某個東西或調整螢幕大小後的樣子,這第二張圖片才是應該的樣子。你會注意到,在 TypeScript 檔案中,我將預設列數設為 3,但如果我移除它就會出現錯誤。任何關於設計方面的反饋也都歡迎。

 <!-- html -->
<div #gridView>
    <mat-grid-list cols="{{columnNum}}" gutterSize="5rem" rowHeight="25rem">
        <mat-grid-tile *ngFor="let product of products">
            <img src="{{ product.image }}" alt="">
        </mat-grid-tile>
    </mat-grid-list>
</div>

TypeScript 檔案

import { AfterViewInit, Component, HostListener, OnInit, ViewChild } from '@angular/core';
import { ProductsService } from '../services/products.service';

@Component({
  selector: 'app-gallery',
  templateUrl: './gallery.component.html',
  styleUrls: ['./gallery.component.css']
})
export class GalleryComponent implements OnInit, AfterViewInit {
  products : {name: string, productId: string, image: string, price: number, desc: string, size: string, isPrintAvailable: boolean}[] = [];
  @ViewChild('gridView') gridView: any;
  columnNum = 3; //initial count
  tileSize = 450; //one tile should have this width

  setColNum(){
     let width = this.gridView.nativeElement.offsetWidth;
     this.columnNum = Math.trunc(width/this.tileSize);
   }

   //calculating upon loading
   ngAfterViewInit() {
     this.setColNum();
   }

   //recalculating upon browser window resize
   @HostListener('window:resize', ['$event'])
   onResize() {
     this.setColNum();
   }

  constructor(private productService: ProductsService) {}

  ngOnInit(): void {
    this.products = this.productService.getProducts();
  }

}

它載入時的樣子

在像點擊或調整螢幕大小這樣的事件之後,它看起來的樣子(如果我離開再返回,它會回到最上面的圖片)

答案#1

在一頁又一頁的閱讀之後,我找到了一個解決方案,它不需要修改 TypeScript 文件,而且運作得非常完美。基本上,你只需要創建一個指令來替換 cols 屬性,而指令會處理剩下的部分。我會添加 mat-grid-list 標籤的程式碼以及 directive 的程式碼,但這是原始頁面,以便我可以向作者致謝:https://www.mymiller.name/wordpress/programming/angular-material-grid-layout-responsive/

html

<mat-grid-list [gridCols]="{xs: 1, sm: 2, md: 3, lg: 4, xl: 5}">

指令

import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { Directive, Input, OnInit } from '@angular/core';
import { MatGridList } from '@angular/material/grid-list';

export interface GridColumns {
  xs: number;
  sm: number;
  md: number;
  lg: number;
  xl: number;
}
@Directive({
  selector: '[gridCols]'
})
export class GridColsDirective implements OnInit {
  private gridCols: GridColumns = {xs: 1, sm: 2, md: 4, lg: 6, xl: 8};

  public get cols(): GridColumns {
    return this.gridCols;
  }

  @Input('gridCols')
  public set cols(map: GridColumns) {
    if (map && ('object' === (typeof map))) {
      this.gridCols = map;
    }
  }

  public constructor(private grid: MatGridList, private breakpointObserver: BreakpointObserver) {
    if(this.grid != null) {
      this.grid.cols = this.gridCols.md;
    }
  }

  public ngOnInit(): void {
    if(this.grid != null) {
      this.grid.cols = this.gridCols.md;
    }
    this.breakpointObserver.observe([
      Breakpoints.XSmall,
      Breakpoints.Small,
      Breakpoints.Medium,
      Breakpoints.Large,
      Breakpoints.XLarge
    ]).subscribe(result => {

      if (result.breakpoints[Breakpoints.XSmall]) {
        this.grid.cols = this.gridCols.xs;
      }
      if (result.breakpoints[Breakpoints.Small]) {
        this.grid.cols = this.gridCols.sm;
      }
      if (result.breakpoints[Breakpoints.Medium]) {
        this.grid.cols = this.gridCols.md;
      }
      if (result.breakpoints[Breakpoints.Large]) {
        this.grid.cols = this.gridCols.lg;
      }
      if (result.breakpoints[Breakpoints.XLarge]) {
        this.grid.cols = this.gridCols.xl;
      }
    });
  }
}

答案#2

問題首先是你將 columnNum 設為 3,然後它會根據你的視窗大小改變。問題是,如果你立即改變大小,你會得到 ExpressionChangedAfterItHasBeenCheckedError,所以 columnNum 的改變需要一些時間。有一些方法可以解決這個問題,其中一種方法是使用 setTimeout 函數或使用 cdr.detectChanges()。

setColNum() {
    let width = this.gridView.nativeElement.offsetWidth;
    setTimeout(() => {
      this.columnNum = Math.trunc(width / this.tileSize);
    }, 0);
  }

或

constructor(private cdr: ChangeDetectorRef) {}
ngAfterViewInit() {
    this.setColNum();
    this.cdr.detectChanges();
  }