Angular Material design設計

 

官網:css

https://material.io/design/html

https://meterial.io/componentsgit

優秀的Meterial design站點:github

http://materialdesignblog.com/typescript

 

並非萬能的,都有約束條件。npm

優勢:兼容性好,可擴展性強,可測試性好,對主題的支持好。api

缺點:組件不是特別豐富。markdown

安裝:app

//其它方式
$ sudo cnpm i --save @angular/material@2.0.0-beta.7async

$ sudo yarn add @angular/material --save

$ npm i @angular/material --save

安裝的版本是"@angular/material": "^7.1.0",

有個依賴要手動安裝

$ sudo yarn add @angular/cdk@6.4.6 --save

 

1、相關報錯 

一、控制檯報警告:Could not find Angular Material core theme。

在styles.scss中引入angular materail主題:

@import '~@angular/material/prebuilt-themes/deeppurple-amber.css';

內建主題有這幾種:

能夠用scss自定義主題。

二、ERROR Error: Found the synthetic listener @transform.start. Please include either "BrowserAnimationsModule" or "NoopAnimationsModule" in your application.

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
三、mat-toolbar,mat-sidenav和mat-sidenav-container不是已知的元素?
Uncaught Error: Template parse errors: 'mat-icon' is not a known element: 1. If 'mat-icon' is an Angular component, then verify that it is part of this module.
應該將模塊導入與組件相關的module.ts文件中。好比我是在header這個組件中用到了mat-icon,而個人header組件是放在core Module中的,那就要在core Module中去import MatIconModule,而不是在app.module中引入。
四、ERROR Error: Could not find HttpClient provider for use with Angular Material icons.
使用SVG Icon的時候依賴Http。須要導入HttpClient。
五、NewTaskComponent.html:14 ERROR Error: MatDatepicker: No provider found for DateAdapter. You must import one of the following modules at your application root: MatNativeDateModule, MatMomentDateModule, or provide a custom implementation.
使用DatePicker的時候同時要導入MatNativeDateModule用做DateFormat。
六、core.es5.js:178 Could not find HammerJS. Certain Angular Material components may not work correctly.
移動端用HammerJS去處理一些事件。
$ npm install --save hammerjs
安裝好之後在coreModule中
import 'hammerjs';

2、Material組件

一、SidebarComponent側邊欄導航:

https://material.angular.io/components/sidenav/overview

<mat-sidenav-container> 
  <mat-sidenav #sidenav>
    <app-sidebar></app-sidebar>
  </mat-sidenav>
  <div class="site">
    <header>
      <app-header></app-header>
    </header>
    <main>
      <button (click)="sidenav.open()">打開側邊欄</button>
    </main>
    <footer>
      <app-footer></app-footer>
    </footer>
  </div>
</mat-sidenav-container>
View Code

 

根容器<mat-sidenav-container>

  • over模式【默認】
<mat-sidenav #sidenav mode="over">

  • side模式
<mat-sidenav #sidenav mode="side">並排顯示

  • push模式
<mat-sidenav #sidenav mode="push">至關於over和side的結合

 

 

  • position="end" 
    <mat-sidenav #sidenav mode="push" position="end">

側邊欄跑右邊了,默認start。最多能夠有2個側邊欄。

<mat-sidenav-container>
  <mat-sidenav #sidenav1 mode="push" position="end">
    <app-sidebar></app-sidebar>
  </mat-sidenav>
  <mat-sidenav #sidenav2 mode="push" position="start">
      <app-sidebar></app-sidebar>
    </mat-sidenav>
  <div class="site">
    <header>
      <app-header></app-header>
    </header>
    <main>
      <button (click)="sidenav1.open()">打開右邊側邊欄</button>
      <button (click)="sidenav2.open()">打開左邊側邊欄</button>
    </main>
    <footer>
      <app-footer></app-footer>
    </footer>
  </div>
</mat-sidenav-container>
View Code

 

 

  • toogle方法:相似open
<button (click)="sidenav.toggle()">打開側邊欄</button>
<mat-sidenav-container>
  <mat-sidenav #sidenav mode="side" >
    <app-sidebar></app-sidebar>
  </mat-sidenav>
  <div class="site">
    <header>
      <app-header></app-header>
    </header>
    <main>
      <button (click)="sidenav.toggle()">打開側邊欄</button>
    </main>
    <footer>
      <app-footer></app-footer>
    </footer>
  </div>
</mat-sidenav-container>
View Code

 

二、Toolbar完成頭部和尾部

https://material.angular.io/components/toolbar/overview

用於頭部,標題欄

顏色primary紫色,accent黃色,warn紅色。

主色:primary

配色:accent

用官方的material io 的COLOR TOOL能夠調出合適的顏色。主色深紫,輔助色黃色效果以下。

 

一、居中用flex怎麼實現?

在想居中元素兩邊都放了自動擴大充滿的元素。

在styles.scss中加入

.fill-remaining-space {
  // 使用 flexbox 填充剩餘空間
  // @angular/material 中的不少控件使用了 flex 佈局
  flex: 1 1 auto;
}
<mat-toolbar color="primary">
  <span class="fill-remaining-space"></span>
  <span>&copy; strof 版權聲明</span>
  <span class="fill-remaining-space"></span>
</mat-toolbar>

 

二、兩行內容怎麼實現?

mat-toolbar-row也是flex容器。 用<mat-toolbar-row>支持多行。
<mat-toolbar color="primary">
  <mat-toolbar-row>
    <span class="fill-remaining-space"></span>
    <span>&copy; starof 版權聲明</span>
    <span class="fill-remaining-space"></span>
  </mat-toolbar-row>
  <mat-toolbar-row>
    <span class="fill-remaining-space"></span>
    <span>這是第二行</span>
    <span class="fill-remaining-space"></span>
  </mat-toolbar-row>
</mat-toolbar>

三、菜單button調出左邊sidebar

問題,頭部的菜單按鈕不知道sidebar在哪裏,涉及到 父子組件通訊

四、 用一個icon代替菜單2個文字

https://material.angular.io/components/icon/overview

使用圖標字體,基於字體的,好處佔用資源小,矢量圖,內建material icon支持。

線用mat-icon,導入MatIconModule。
在index.html中引入material-icon字體庫。參考: https://google.github.io/material-design-icons/
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
使用
<button mat-icon-button (click)="openSidebar()">
        <mat-icon class="material-icons">menu</mat-icon>
</button>

想要使用其它圖標直接,去https://material.io/tools/icons/ 找到圖標對應文字替換掉便可。

五、MatIcon也支持SVG圖標。

找SVG資源, http://iconfont.cn/ 阿里爸爸矢量圖標庫

 

把下載好的SVG放在項目的assets中。

最終效果:

實現
  constructor(private iconRegistry: MatIconRegistry,
    private sanitizer: DomSanitizer) {
    iconRegistry.addSvgIcon('gift', sanitizer.bypassSecurityTrustResourceUrl('assets/gift.svg'));
  }
<mat-icon svgIcon="gift"></mat-icon>

問題,每次使用一個新圖標都要註冊一遍。圖標分散在各個組件的constructor中,難以維護。

更好的作法:

在app下建立一個utils目錄,在utils裏建一個svg.util.ts。

import { MatIconRegistry } from "@angular/material";
import { DomSanitizer } from "@angular/platform-browser";

export const loadSvgResources = (ir: MatIconRegistry, ds: DomSanitizer) => {
  ir.addSvgIcon("gift", ds.bypassSecurityTrustResourceUrl("assets/gift.svg"));
};

不在組件的constructor裏導入裏,在core module裏只加載一次。

export class CoreModule {
  constructor(
    @Optional() @SkipSelf() parent: CoreModule, //加上@SkipSelf()註解
    ir: MatIconRegistry,
    ds: DomSanitizer
  ) {
    if (parent) {
      throw new Error("模塊已經存在,不能再次加載");
    }
    loadSvgResources(ir, ds);
  }
}

這樣其它頁面只要用就能夠,解決了裝載資源分散的問題。

三、Input組件matInputModule

能夠經過color取值warn,accent等來改變input輸入框的顏色。

能夠經過floatLabel取值float,always,auto來改變Label顯示方式。

能夠設置hintLabel或者<mat-hint>來添加更多提示信息。

能夠經過給<mat-form-field>裏的元素設置matSuffix指令來給輸入框添加後綴,設置matPrefix指令加前綴。

https://material.angular.io/components/form-field/overview

<mat-form-field class="example-full-width" color="warn" floatLabel="auto" hintLabel="Max 10 characters">
            <input #input matInput placeholder="您的email">
            <span matSuffix>@gmail.com</span>
            <mat-hint align="end">{{input.value?.length || 0}}/10</mat-hint>
</mat-form-field>

<mat-error> :只有驗證不經過時纔出現,對兩種類型表單都是。
<mat-hint>:和<mat-error>在同一個位置去顯示,當error顯示時,hint會隱藏。 

四、Card組件和Button組件 

raised:3D效果。
icon:透明的圓形button,圖片是hover上去的效果。
fab:fast action button.
 
mat-button Rectangular text button w/ no elevation
mat-raised-button Rectangular contained button w/ elevation
mat-flat-button Rectangular contained button w/ no elevation
mat-stroked-button Rectangular outlined button w/ no elevation
mat-icon-button Circular button with a transparent background, meant to contain an icon
mat-fab Circular button w/ elevation, defaults to theme's accent color
mat-mini-fab
Card適合圖文形式突出某一主題。 https://material.angular.io/components/card/overview
  <mat-card class="example-card">
    <mat-card-header>
      <mat-card-title>每日佳句</mat-card-title>
      <mat-card-subtitle>知足感在於不斷的努力,而不是現有成就。全心努力定會勝利滿滿。</mat-card-subtitle>
    </mat-card-header>
    <img mat-card-image src="/assets/images/quote_fallback.jpg" alt="">
    <mat-card-content>
      Satisfaction lies in the effort, not in the attainment. Full effort is full victory.
    </mat-card-content>
  </mat-card>
View Code

五、List

https://material.angular.io/components/list/overview

包括都行列表,帶圖標列表,帶頭像列表,密集列表(dense list)和多段列表。

分爲<mat-list> 和<mat-nav-list>

在側滑菜單中使用List。

matNavList鼠標hover上去會有高亮效果。

<mat-nav-list>
  <h3 mat-subheader>項目</h3>
  <mat-list-item>
    <mat-icon mat-list-icon svgIcon="projects"></mat-icon>
    <h4 mat-line>項目首頁</h4>
    <p mat-line mat-subheader> 查看您的全部項目</p>
  </mat-list-item>
  <h3 mat-subheader>日曆</h3>
  <mat-list-item>
    <mat-icon mat-list-icon svgIcon="month"></mat-icon>
    <h4 mat-line>月視圖</h4>
    <p mat-line mat-subheader> 按月查看您的任務</p>
  </mat-list-item>
  <mat-list-item>
    <mat-icon mat-list-icon svgIcon="week"></mat-icon>
    <h4 mat-line>周視圖</h4>
    <p mat-line mat-subheader> 按周查看您的任務</p>
  </mat-list-item>
  <mat-list-item>
    <mat-icon mat-list-icon svgIcon="day"></mat-icon>
    <h4 mat-line>日視圖</h4>
    <p mat-line mat-subheader> 按天查看您的任務</p>
  </mat-list-item>
</mat-nav-list>
View Code

Dense list會讓列表的字體shrink來顯示更多內容。用法<mat-nav-list dense>。

把日式圖的圖標改變成當天的日前,在svg.util.ts中加入提早準備好的30天日期day1.svg到day3-.svg. 

import { MatIconRegistry } from "@angular/material";
import { DomSanitizer } from "@angular/platform-browser";

export const loadSvgResources = (ir: MatIconRegistry, ds: DomSanitizer) => {
  const imgDir = "assets/img";
  const sidebarDir = `${imgDir}/sidebar`;
  const dayDir = `${imgDir}/days`;
  ir.addSvgIcon(
    "day",
    ds.bypassSecurityTrustResourceUrl(`${sidebarDir}/day.svg`)
  );
  ir.addSvgIcon(
    "month",
    ds.bypassSecurityTrustResourceUrl(`${sidebarDir}/month.svg`)
  );
  ir.addSvgIcon(
    "project",
    ds.bypassSecurityTrustResourceUrl(`${sidebarDir}/project.svg`)
  );
  ir.addSvgIcon(
    "projects",
    ds.bypassSecurityTrustResourceUrl(`${sidebarDir}/projects.svg`)
  );
  ir.addSvgIcon(
    "week",
    ds.bypassSecurityTrustResourceUrl(`${sidebarDir}/week.svg`)
  );

  const days =[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31];
  days.forEach((d)=>{
    ir.addSvgIcon(
      `day${d}`,
      ds.bypassSecurityTrustResourceUrl(`${dayDir}/day${d}.svg`)
    );
  });
};
View Code

在sidebar.component.ts中引入一個類庫day-fns。 

npm uninstall --save date-fns //先安裝date-fns
npm install --save-dev @types/date-fns //再date-fns針對typescript也可使用的類型

import { Component, OnInit } from '@angular/core';
import {getDate} from 'date-fns'; //getDate取得是一個月的幾號

@Component({
  selector: 'app-sidebar',
  templateUrl: './sidebar.component.html',
  styleUrls: ['./sidebar.component.scss']
})
export class SidebarComponent implements OnInit {
  today = 'day';
  constructor() { }

  ngOnInit() {
    this.today=`day${getDate(new Date())}`; //today對應icon的名字
  }

}

模版中icon對應到today。

<mat-icon mat-list-icon [svgIcon]="today"></mat-icon>

六、GridList

<mat-grid-list cols="8" rowHeight="1:1">
        <mat-grid-tile colspan="2">
          1
        </mat-grid-tile>
        <mat-grid-tile>
          2
        </mat-grid-tile>
        <mat-grid-tile>
          3
        </mat-grid-tile>
        <mat-grid-tile>
          4
        </mat-grid-tile>
        <mat-grid-tile rowspan="2">
          5
        </mat-grid-tile>
        <mat-grid-tile>
          6
        </mat-grid-tile>
        <mat-grid-tile>
          7
        </mat-grid-tile>
        <mat-grid-tile>
          8
        </mat-grid-tile>
        <mat-grid-tile>
          9
        </mat-grid-tile>
        <mat-grid-tile>
          10
        </mat-grid-tile>
        <mat-grid-tile>
          11
        </mat-grid-tile>
        <mat-grid-tile>
          12
        </mat-grid-tile>
        <mat-grid-tile>
          13
        </mat-grid-tile>
        <mat-grid-tile>
          14
        </mat-grid-tile>
      </mat-grid-list>
View Code

註冊頁面頭像列表能夠用GridList實現。

七、dialog

https://material.angular.io/components/dialog/overview

對話框很特殊,和menu同樣須要在模塊中的entryComponents中聲明。

因爲它們是須要事先徹底隱藏,或者隱藏一部分。一開始模塊加載的時候是看不到dialog的,點擊按鈕才能彈出。對於這種狀況,須要一個預加載,就須要放在模塊的entryComponents中。

一、從調用者向Dialog傳遞數據

傳遞數據:

不能像其它組件同樣,經過路由去傳遞數據。須要從它的調用者去寫。

在按鈕的click事件中寫:

const dialogRef = dialog.open(YourDialog, {data:'Your data'});

接收數據:

在Dialog的構造中注入MD_DIALOG_DATA就能夠取得數據。

constructor(@Inject(MAT_DIALOG_DATA) private data: any) { }

二、在Dialog把數據往回傳,傳給調用者。

//注入MatDialogRef 
 constructor(@Inject(MAT_DIALOG_DATA) private data: any,
    public dialogRef: MatDialogRef<NewProjectComponent>, ) { }
//在點擊保存按鈕時候發送數據
  onSave() {
    this.dialogRef.close('I received your message');
  }


//調用者open的時候拿到一個引用,註冊afterClosed事件返回一個Observable openNewProjectDialog() { // this.dialog.open(NewProjectComponent,{data:'this is a dialog'}); const dialogRef = this.dialog.open(NewProjectComponent, { data: "this is a dialog" }); dialogRef.afterClosed().subscribe((result)=>{ console.log(result); }); }

三、Dialog樣式支持主題

//注入OverlayContainer  
constructor(@Inject(MAT_DIALOG_DATA) private data: any,
    public dialogRef: MatDialogRef<NewProjectComponent>,
    private oc: OverlayContainer) { }

//經過傳入dark來切換class
  ngOnInit() {
    console.log(JSON.stringify(this.data));
    this.oc.themeClass = this.data.dark ? 'myapp-dark-theme' : 'null';
  }

報錯:ERROR in src/app/project/new-project/new-project.component.ts(18,13): error TS2339: Property 'themeClass' does not exist on type 'OverlayContainer'.

 參考:

https://gist.github.com/tomastrajan/ee29cd8e180b14ce9bc120e2f7435db7

八、autoComplete 

https://material.angular.io/components/autocomplete/overview

 

<form class="example-form">
  <mat-form-field class="example-full-width">
    <input type="text" placeholder="Assignee" aria-label="Assignee" matInput [formControl]="myControl" [matAutocomplete]="auto">
    <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
      <mat-option *ngFor="let option of filteredOptions | async" [value]="option">
        {{option.name}}
      </mat-option>
    </mat-autocomplete>
  </mat-form-field>
</form>

displayWith傳入一個函數,指明具體怎樣顯示

  displayFn(user?: User): string | undefined {
    return user ? user.name : undefined;
  }

九、menu

 

<button mat-button [matMenuTriggerFor]="menu">Menu</button>
<mat-menu #menu="matMenu">
  <button mat-menu-item>Item 1</button>
  <button mat-menu-item>Item 2</button>
</mat-menu>

 

十、複選框,單選組件和下拉框

<mat-checkbox>和<mat-radio>和<mat-select>

checkbox還要chenged事件處理

  <mat-checkbox [checked]="item.completed" class="status">  </mat-checkbox>

Tooptip

  <div class="content" mat-line [ngClass]="{'completed':item.completed}">
    <span [matTooltip]="item.desc">{{item.desc}}</span>
  </div>

 

十一、DatePicker

https://material.angular.io/components/datepicker/overview 

<mat-form-field>
  <input matInput [matDatepicker]="picker" placeholder="Choose a date">
  <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
  <mat-datepicker #picker></mat-datepicker>
</mat-form-field>

相關文章
相關標籤/搜索