Angular Material design设计

 

官网:

https://material.io/design/

https://meterial.io/components

优秀的Meterial design站点:

http://materialdesignblog.com/

 

并不是万能的,都有约束条件。

优点:兼容性好,可扩展性强,可测试性好,对主题的支持好。

缺点:组件不是特别丰富。

安装:

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

$ 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

 

如果是用ng add @angular/material  会自动安装cdk(Component Dev Kit)依赖。

一、相关报错 

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的时候涉及到URL的解析,依赖Http。需要导入HttpClientModule。
 
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>
View Code

 

根容器<mat-sidenav-container>

3种模式:over, side和push

  • 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

 

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在哪里,涉及到父子组件通信
在header组件中发射一个toggle事件,在app组件中监听到后打开sidebar。
@Output() toggle = new EventEmitter();
  constructor() { }

  ngOnInit(): void {
  }
  openSideBar(){
    this.toggle.emit('clicked');
  }
<app-header (toggle)="sidenav.open()"></app-header>
 

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/ 找到图标对应文字替换掉即可。

开始按钮的背景颜色不透明,需要把button也变成meterial风格,给button上加上 mat-icon-button,并且需要导入MatButtonModule。

import { MatButtonModule } from '@angular/material/button';

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

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

6、GridList

用途:用于相似的数据展示,尤其是图片,类似相册等

行为很像表格,有colspan, rowspan属性

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

<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实现。

7、dialog

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

对话框很特殊,和menu一样需要在模块中的entryComponents中声明。

由于它们是需要事先完全隐藏,或者隐藏一部分。一开始模块加载的时候是看不到dialog的,点击按钮才能弹出。对于这种情况,需要一个预加载,就需要放在模块的entryComponents中。

从支持 Ivy 的 9.0.0 开始,entryComponents 属性就不再需要了,参见弃用指南

1、从调用者向Dialog传递数据

传递数据:

不能像其它组件一样,通过路由去传递数据。

需要从在的调用者去写,在按钮的click事件中写:传入dialog的类,用一个字典传入任意数据。

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

接收数据:

在Dialog的构造中注入MD_DIALOG_DATA就可以取得数据。

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

2、在Dialog把数据往回传,传给调用者。

弹窗组件中通过dialogRef关闭传值。

调用者通过注册afterClosed事件拿到数据。

//NewProject 组件中注入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样式支持主题

默认不支持主题,

 Dialog是一个浮动层,通过注入OverlayContainer来设置样式。利用data传值动态设置。

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

//通过传入dark来切换class
ngOnInit(): void {
    console.log(JSON.stringify(this.data));
    const themeClass = this.data.dark ? 'myapp-dark-theme' : '';
    this.oc.getContainerElement().classList.add(themeClass);
}

8、autoComplete 

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

 

mat-autocomplete配合matInput输入框来用,成对出现。mapInput中指定引用[matAutocomplete="auto"]。

在mat-autocomplete中指定#auto=matAutocomplete。

<form>
    <h2 mat-dialog-title>邀请组员</h2>
    <div mat-dialog-content>
        <mat-form-field class="full-width">
            <input type="text" matInput placeholder="组员姓名" [matAutocomplete]="autoMembers">
        </mat-form-field>
    </div>
    <div mat-dialog-actions>
        <button type="button" mat-raised-button color="primary" (click)="onSave()">保存</button>
        <button type="button" mat-button mat-dialog-close>关闭</button>
    </div>
</form>
<mat-autocomplete #autoMembers="matAutocomplete" [displayWith]="displayUser">
    <mat-option *ngFor="let item of items" [value]="item">
        {{item.name}}
    </mat-option>
</mat-autocomplete>

如果有些情况,希望<mat-option>的value显示的是item,但是希望input中显示的是item的name,或者显示的是另一个组件,或者是一个icon之类的,这种情况可以用displayWith指令,

让[displayWith]="displayFn"函数。

displayWith传入一个函数,指明Input中具体怎样显示.

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

9、menu

弹出菜单,包括两部分。

一部分是它的触发者,通常是按钮,或者右键点击时候调出菜单。实现的时候就是用matMenuTriggerFor指令,指向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、复选框,单选组件和下拉框

几种和HTML对应组件非常像的Material组件:<mat-checkbox>和<mat-radio>和<mat-select>

checkbox还要chenged事件处理

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

Tooltip

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

Radio Button 为一组数据而存在,数据量少,减少点击用radio。

<mat-radio-group>
            <mat-radio-button *ngFor="let priority of priorities" [value]="priority.value">{{priority.label}}
            </mat-radio-button>
</mat-radio-group>

 

select 一组数据而存在,数据量大用select。

 

<mat-select placeholder="请选择目标列表">
            <mat-option *ngFor="let list of lists" [value]="list">
                {{list.name}}
            </mat-option>
</mat-select>

 

11、DatePicker

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

两个组件配合使用,[matDatepicker]="picker"让datepicker选中的结果在input中显示。

toggle用matSuffix把toggle作为input的后缀,toggle触发调起datepicker。

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

 12,tabs

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

一个mat-tab-group里面套多个mat-tab。

<mat-tab-group>
  <mat-tab label="First"> Content 1 </mat-tab>
  <mat-tab label="Second"> Content 2 </mat-tab>
  <mat-tab label="Third"> Content 3 </mat-tab>
</mat-tab-group>

 

posted @ 2018-08-24 08:29  starof  阅读(5341)  评论(0编辑  收藏  举报