官網:

https://material.io/design/

https://meterial.io/components

優秀的Meterial design站點:

http://materialdesignblog.com/

並不是萬能的,都有約束條件。

優點:相容性好,可擴充套件性強,可測試性好,對主題的支援好。

缺點:元件不是特別豐富。

安裝:

//其它方式
$ sudo cnpm i --save @angular/[email protected]

$ sudo yarn add @angular/material --save

$ npm i @angular/material --save

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

有個依賴要手動安裝

$ sudo yarn add @angular/[email protected] --save

一、相關報錯

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

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

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

內建主題有這幾種:

可以用scss自定義主題。

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

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
3、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中引入。
4、ERROR Error: Could not find HttpClient provider for use with Angular Material icons.
使用SVG Icon的時候依賴Http。需要匯入HttpClient。
5、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。
6、core.es5.js:178 Could not find HammerJS. Certain Angular Material components may not work correctly.
移動端用HammerJS去處理一些事件。
$ npm install --save hammerjs
安裝好以後在coreModule中

import 'hammerjs';

二、Material元件

1、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>

根容器<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>

  • 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>

2、Toolbar完成頭部和尾部

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

用於頭部,標題欄

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

主色:primary

配色:accent

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

1、居中用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>

2、兩行內容怎麼實現?

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>

3、選單button調出左邊sidebar

問題,頭部的選單按鈕不知道sidebar在哪裡,涉及到父子元件通訊

4、 用一個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/ 找到圖示對應文字替換掉即可。

5、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);
}
}

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

3、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會隱藏。 

4、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>

5、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>

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 =[,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,];
days.forEach((d)=>{
ir.addSvgIcon(
`day${d}`,
ds.bypassSecurityTrustResourceUrl(`${dayDir}/day${d}.svg`)
);
});
};

在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>

6、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>

註冊頁面頭像列表可以用GridList實現。

7、dialog

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

對話方塊很特殊,和menu一樣需要在模組中的entryComponents中宣告。

由於它們是需要事先完全隱藏,或者隱藏一部分。一開始模組載入的時候是看不到dialog的,點選按鈕才能彈出。對於這種情況,需要一個預載入,就需要放在模組的entryComponents中。

1、從呼叫者向Dialog傳遞資料

傳遞資料:

不能像其它元件一樣,通過路由去傳遞資料。需要從它的呼叫者去寫。

在按鈕的click事件中寫:

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

接收資料:

在Dialog的構造中注入MD_DIALOG_DATA就可以取得資料。

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

2、在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);
});
}

3、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

8、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;
}

9、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>

10、複選框,單選元件和下拉框

<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>

11、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>