欢迎各位兄弟 发布技术文章
这里的技术是共享的
前两天我们很精实的学习的跟Input相关的3个元件-Input、Autocomplete和Datepicker。今天我们用比较轻松的步调来介绍另一个常用的元件-Select。
在Angular Material中,Select的设计是依循着menus这个单元来设计的,毕竟select与menu其实本质上是大同小异的,都是用来从多个选项中选择一个或多个答案,所以在设计上风格也不会差太大,只是menu的概念偏向于选择后执行某个动作,而select则是在作为表单中保存资料的一个元件,也因此跟menu最大不同的是,select支援ngModel
和formControlName
这类的表单绑定功能。
在Angular Material中,Select跟Input这类元件一样,都需要跟<mat-form-field>
一起合作,才能发挥最大的威力,而Select的元件都在MatSelectModule
之中,加入这个Module之后,我们可以简单地组合使用<mat-select>
和<mat-option>
来达到类似HTML的<select>
功能,但更具有Material Design的风格。
<mat-select>
和<mat-option>
使用上非常类似于HTML的<select>
和<option>
,因此使用上非常容易上手:
<mat-form-field>
<mat-select formControlName="interest">
<mat-option *ngFor="let item of interestList" [value]="item.id">{{ item.name }}</mat-option>
</mat-select>
</mat-form-field>
看起来是不是跟我们直接用<select>
/ <option>
产生的语法差不多啊!但画面上可就是满满的Material Design了!
小技巧:如果没有要搭配表单的话,可以直接使用
[(value)]
来达到two way binding的效果。
上面的select虽然充满了Material Design的风格,但还差了一点,就是没有像Input类型的placeholder来替代label显示栏位名称。在原生HTML中select
是没有placeholder可以使用的,比较常见的技巧是透过一个<option [value]="null">text</option>
来假装成placeholder,但在Angular Material中我们可以在<mat-select>
元件中直接使用placeholder
,实在是非常方便!
<mat-form-field>
<mat-select formControlName="interest" placeholder="你的休閒愛好?">
<mat-option *ngFor="let item of interestList" [value]="item.id">{{ item.name }}</mat-option>
</mat-select>
</mat-form-field>
结果如下:
画面上看起来就更加一致啦!
不管是<mat-select>
还是<mat-option>
,都能够透过disabled
来设定其允许被选取的状态。我们先看看在<mat-select>
加上disabled
属性后的样式:
<mat-form-field>
<mat-select formControlName="interest" placeholder="你的休閒愛好?" disabled>
<mat-option *ngFor="let item of interestList" [value]="item.id">{{ item.name }}</mat-option>
</mat-select>
</mat-form-field>
结果如下:
可以看到整个select变成虚线底线,代表了disabled的状态。
接着我们看看<mat-option>
加上disabled
属性后的结果:
<mat-form-field>
<mat-select formControlName="interest" placeholder="你的休閒愛好?">
<mat-option *ngFor="let item of interestList; let index = index" [value]="item.id" [disabled]="index === 1">{{ item.name }}</mat-option>
</mat-select>
</mat-form-field>
结果如下:
被设定为disabled
属性的选项变成灰底,也就无法选取啰。
目前设定好<mat-option>
并进行选择后,是无法取消目前的选取状态的,这会使得选项非必填无法反悔,这时候我们可以加入一个没有设定[value]
(其实也就是[value]="undefined"
)的<mat-option>
,当选到这个选项时,就会自动跳回没有选择任何选项的模式,也就是还看得到placeholder的状态:
<mat-form-field>
<mat-select formControlName="interest" placeholder="你的休閒愛好?">
<mat-option [value]="undefined">無</mat-option>
<mat-option *ngFor="let item of interestList; let index = index" [value]="item.id" [disabled]="index === 1">{{ item.name }}</mat-option>
</mat-select>
</mat-form-field>
结果如下:
当我们选到「无」的时候,就会自动换成最原始显示placeholder的状态啦!
HTML的select可以使用<optgroup label="xxx">
来为<option>
分组放置,Angular Material当然也不会忽略掉这部分,在Angular Material中我们可以使用<mat-optgroup>
来达到一样的功能,要设定整个群组的disabled状态也完全没问题!
<mat-select formControlName="interest" placeholder="你的休閒愛好?">
<mat-option [value]="undefined">無</mat-option>
<mat-optgroup *ngFor="let parent of nestInterestList; let index = index" [label]="parent.name" [disabled]="index===1">
<mat-option *ngFor="let child of parent.subItems" [value]="child.id">{{ child.name }}</mat-option>
</mat-optgroup>
</mat-select>
结果如下:
资料依照mat-optgroup
方式分群组显示,而当mat-optgroup
有设定disabled
时,整个群组也会直接变成不可选取的灰色状态,使用上真的跟原生的感觉很像呢!
跟原生的select一样,我们也可以透过设定mutiple
属性,让<mat-select>
变为可复选状态,此时binding的资料将会变成一个阵列,同时选择[value]="undefined"
的mat-option
也只会让阵列中多一个undefined元素而已,要还原成原来的状态只需要不勾选任何选项即可。
<mat-form-field>
<mat-select formControlName="interest" placeholder="你的休閒愛好?" multiple>
...
</mat-select>
</mat-form-field>
结果如下:
在
mutiple
中属性有一点值得注意的是,这个属性不可为动态的,也就是无法以设定[multiple]="isMultiple"
这样的方式来切换单/多选状态,因为这个状态会影响显示的逻辑,因此是在元件产生时就决定好了,若需要切换mutiple状态,请使用ngIf
或ngSwitch
来变换,例如:
<mat-select *ngIf="isMultiple" multiple>
...
</mat-select>
<mat-select *ngIf="!isMultiple">
...
</mat-select>
<mat-select>
有自己的显示资料的方式,这部分我们可以透过<mat-select-trigger>
覆盖掉原来的显示资料方式:
<mat-form-field>
<mat-select formControlName="interest" #selectInterest placeholder="你的休閒愛好?" multiple>
<mat-select-trigger>
共選擇了 {{ selectInterest.selected.length }} 項興趣
</mat-select-trigger>
...
</mat-select>
</mat-form-field>
成果如下:
我们成功地自订了资料显示的逻辑,透过<mat-select-trigger>
资料呈现就不再死板啦!
预设的<mat-select>
在选取时会有一个涟漪的效果,如下:
如果不喜欢,可以设定disableRipple
:
<mat-form-field>
<mat-select ... disableRipple>
...
</mat-select>
</mat-form-field>
结果如下:
没有特效可能看起来会比较清爽,但也可能会比较无聊,至于怎么样显示比较好,就看个人喜好啰。
<mat-select>
提供了panelClass
属性,方便我们自订下拉选单的样式,我们可以先在<mat-select>
中设定这个属性:
<mat-form-field>
<mat-select ... panelClass="blue-dropdown">
...
</mat-select>
</mat-form-field>
接着需要提醒的是,由于这些功能都被拉到共用的Angular CDK的关系,这个panel其实会被放在我们使用的component之外,因此若直接在component.css中设定会无效,除非额外设定ViewEncapsulation
,或是直接在全域的style.css中加入就好:
.blue-dropdown .mat-select-content {
background: rgba(0, 0, 255, 0.5);
}
成果如下:
今天我们介绍了另一个实用的元件<mat-select>
,这个元件使用上几乎跟HTML原生的<select>
一样,大部分的设定都非常直觉好懂,同时Angular Material又针对<mat-select>
增加了更多的功能及细微可以调整的地方,让下拉选单的功能更加丰富好用!
关于这几天我们一直提到一个包在我们的表单控制项之外的元件<mat-form-field>
,是一个让输入控制栏位活起来的强大外皮,明天我们再来仔细地介绍它!
本日的程式码GitHub:https://github.com/wellwind/it-ironman-demo-angular-material/tree/day-12-select
分支:day-12-select
大大你好:
想请教一个问题,select可以针对显示的内容来排序吗?而不是针对key值
for (var key in this.sharedService.lists) {
if (this.sharedService.lists[key]["FATHER_NAME" ] == "Priority")
{
this.translate.get(this.sharedService.lists[key]
["LS_NAME"]).subscribe((res: string) => {
let severity = res;
let lists = { key: this.sharedService.lists[key]
["LS_ID"].toString(), value: res, defaultValue: '' };
field.options.push(lists);
});
}
}
field.options.sort(?) ;
因为找不到范例,所以跟你请教,谢谢
您好,select 下的option 本来就是针对资料本身顺序呈现,因此若有需要应该是自行在程式中排序,在使用ngFor 显示每笔资料才是正确的方式。
了解,谢谢!
您好
想请问
如果选项中含有"其他"
然后需求使用者如果选其他的话需要输入
这样的话
用
方案1.select然后option值等于时触发input比较好?
还是
方案2.维持matAutocomplete的input?
因为
方案1.想说就需要写option的index =多少时要跳出input
方案2.直接就是input,但怕这样无法用reactive form的验证器,怕选了"其他"但使用者没在input后面继续填东西
所以想问大大想法
来自 https://ithelp.ithome.com.tw/articles/10194840
前几天我们介绍了几个Angular Material常见的文字型表单栏位控制项,如Input、Autocomplete、Datepicker和Select。这些控制项在Angular有一些特点,就是它们都会使用一个<mat-form-field>
元件包起来,这个元件到底是有什么能耐,让这些表单栏位都离不开它呢?就让我们继续看下去吧!
<mat-form-field>
被用来包住几个常见的文字栏位,并根据「Material Design中的text fields设计指南」来提这些栏位加上如底线、label和提示讯息等功能能,并符合Material Design中的建议,让我们不需要重新造轮子,就能用一致的显示方式来打造我们的介面!
下面几种功能在设计上都能够搭配<mat-form-field>
使用
<input matInput>
和<textarea matInput>
<mat-select>
<mat-chip-list>
:我们会在未来的文章中继续介绍
接下来我们所提到的每个form field的功能,在以上3种状况下,都可以使用。
要使用<mat-form-field>
必须加入MatFormFieldModule,在之前的文章我们也应该都已经加入了!
在Material Design中针对这些输入型的栏位有个很特殊的设计,称之为floating label,也就是我们常用的label位置可以放在input里面,像是placeholder一样,如下图:
当元件被focus的时候,这个label会自动浮到上面去,如下
也因此在Angular Material中,我们不需要特别去加一个<label>
,直接设定placeholder
属性即可,它同时就兼具了placeholder和label的作用,非常的方便。
这个效果相信看过之前的文章应该已经非常清楚了,而这个效果其实是可以自己设定的,我们可以透过设定floatLabel
,来决定这个label的显示模式,目前有以下几种:
auto:预设值,当focus时,label会自动自动往上浮起来变成label的角色。
always:代表永远都是浮起来的状态,这时候可以直接将它视为一个单纯的label存在。
never:代表永远不要浮起来,这时候可以把它直接当作一个单纯的placeholder。
我们可以修改一下之前问卷调查的程式,随意找几个由<mat-form-field>
包起来的元件做设定,例如:
<mat-form-field floatLabel="always">
<input name="name" matInput placeholder="姓名" formControlName="name" required>
</mat-form-field>
<mat-form-field floatLabel="never">
<input type="text" name="nickname" matInput placeholder="暱稱" />
</mat-form-field>
结果如下:
姓名栏位我们设定了floatLabel="always"
,因此一开始它不会被当作是一个placeholder,而是一直以label的形式存在;而设定了floatLabel="never"
的昵称栏位,一开始看起来没什么变化,但当focus时,它不会往上浮动变成label的形式,有资料时提示就会自动消失,变成了单纯的placeholder的感觉。
如果希望所有的<mat-form-field>
都保持一样的显示方式,不需要一个一个元件设定,只需要注入MAT_LABEL_GLOBAL_OPTIONS
即可,ex:
@NgModule({
providers: [
{provide: MAT_LABEL_GLOBAL_OPTIONS, useValue: {float: 'always'}}
]
})
结果如下:
可以看到除了我们自己有设定never
的栏位以外,其他没设定的通通都变成了always
的模式了!
在预设情况,只要我们的表单元件有加上required
,在显示文字时都会加上一个星号(*),这是必填栏位的习惯用法,如果不想要显示这个星号,可以设定hideRequiredMarker
属性。
<mat-form-field floatLabel="always" [hideRequiredMarker]="true">
<input name="name" matInput placeholder="姓名" formControlName="name" required>
</mat-form-field>
结果如下:
原本的姓名栏位旁边多了一个星号
设定后就不会再显示啰
尽管不提示星号,当有invalid状态时,还是会变成红色提醒哩
一般来说我们可以直接使用placeholder
属性提供文字描述,同时具有label的角色,但毕竟透过<label>
标签可以提供更复杂的label显示,如果希望placeholder也能提供更复杂的样式,可以改成在<mat-form-field>
中加入<mat-placeholder>
这个元件
<mat-form-field floatLabel="always" [hideRequiredMarker]="true">
<input name="name" matInput placeholder="姓名" formControlName="name" required>
<mat-placeholder>
<mat-icon>person</mat-icon>請輸入您的姓名</mat-placeholder>
</mat-form-field>
看看结果:
这时候的画面好像怪怪的,姓名这两个字重复出现了,这是因为placeholder
和<mat-placeholder>
同时出现的关系,这时候我们还能在开发人员工具中看到以下错误询息:
提醒了我们placeholder
和<mat-placeholder>
不应该同时出现,虽然看起来还是都会显示,但也不能保证就一定会正常,还是先把placeholder
属性拿掉保留<mat-placeholder>
就好吧!
<mat-form-field floatLabel="always" [hideRequiredMarker]="true">
<input name="name" matInput placeholder="姓名" formControlName="name" required>
<mat-placeholder>
<mat-icon>person</mat-icon>請輸入您的姓名</mat-placeholder>
</mat-form-field>
这时候就正常多啦!
我们也能把floatLabel设回auto
看看在placeholder的模式下是否也能正常显示:
<mat-form-field floatLabel="auto" [hideRequiredMarker]="true">
<input name="name" matInput placeholder="姓名" formControlName="name" required>
<mat-placeholder>
<mat-icon>person</mat-icon>請輸入您的姓名
</mat-placeholder>
</mat-form-field>
结果如下:
看来<mat-placeholder>
在任何情况下都能够正确显示哩!
在之前介绍matInput
的时候,我们已经用过<mat-hint>
了,透过<mat-hint>
我们可以为元件提供基础的说明文字,我们也能够直接设定<mat-form-field>
的hintLabel
达到同样的效果,不过hintLabel
比较适合用在单纯的文字显示上,而<mat-hint>
则可以做更复杂的显示。
除此之外<mat-hint>
还可以设定align
属性,透过设定为start
或end
来决定对齐的方向,而hintLabel
则无法设定align
,只会出现在start
的位置
范例程式如下:
<mat-form-field ... hintLabel="最多輸入5個字">
<input name="name" matInput formControlName="name" maxlength="5" required>
...
<mat-hint align="end">
已輸入: {{ surveyForm.get('basicQuestions')?.get('name')?.value?.length || 0 }} / 5
</mat-hint>
</mat-form-field>
结果如下:
是不是没花多少程式码,就完成一堆常用的功能啦!
温馨提醒:
align
不能重复,否则会产生错误讯息,虽然目前看起来还是会显示只是排版可能会乱掉,但还是尽量避免啰。
在之前的input篇我们也介绍过了,可以使用<mat-error>
来提示错误讯息,这个<mat-error>
只有在<mat-form-field>
发生错误时才会显示,因此在简单的情境下我们可以少写一些ngIf
或ngSwitch
:
<mat-form-field>
<input name="name" matInput required>
...
<mat-error>姓名欄位有錯誤</mat-error>
</mat-form-field>
当然比较复杂例如需要依照条件显示不同错误提示的时候,还是一样需要搭配ngIf
或ngSwitch
来显示。
之前我们也提过,Angular Material中特别定义了primary
、accent
和warn
,而大多数的Angular Material元件都可以直接透过color="xxx"
的方式,来改变颜色,<mat-form-field>
当然也不例外
<mat-form-field floatLabel="never" color="accent">
<input type="text" name="nickname" matInput placeholder="暱稱" />
</mat-form-field>
结果如下:
当昵称栏位被focus,原本预设应该显示primary
的颜色,但因为我们设定了color="accent"
的关系,就变成另一种颜色啰。
最后我们再来看一个表单中常用的功能,在输入控制项前后加上icon图示;我们可以透过matPrefix
和matSuffix
,把元素放到输入控制项的前面或后面,如下
<mat-form-field>
<input type="number" matInput placeholder="請問您的平均日常消費?">
<mat-icon matPrefix>attach_money</mat-icon>
<span matSuffix>新台幣</span>
</mat-form-field>
结果如下:
不管是mat-icon还是什么都好,只要加入matPrefix
或matSuffix
,就会帮你放到对的位置,真的是很方便啊!
在<mat-form-field>
中,至少需要加入一个MatFormFieldControl
,也就是<mat-form-field>
所能支援的控制项,否则会出现错误讯息:
Error: mat-form-field must contain a MatFormFieldControl
今天我们介绍了<mat-form-field>
这个component,也总算解了之前介绍input等相关原件事为何一定要加上<mat-form-field>
的迷惑,原来<mat-form-field>
具有这么多细致的功能,让我们的能用更加一致的方式,为我们的表单添加上更多的变化,实在是居家旅行 杀人灭口 必备良药啊XD!
问卷调查篇预计还有两篇,再介绍4个元件就会结束了,到时候我们就能把所有表单相关的控制项(FormControl)全部掌握,要设计各种Material Design风格的表单也会越来越轻松啰!
本日的程式码GitHub:https://github.com/wellwind/it-ironman-demo-angular-material/tree/day-13-form-field
分支:day-13-form-field
material 真的考虑到很多实务上会遇到的状况,使用这个可以省去很多自己实作的时间
真的!光是想这些特效及运用情境都要自己去写,就觉得还好有Angular Material都帮我写好了XD
(只是实务上不一定都有机会使用就是了)
解决了,感谢!
请问,是否可改变required产生的*符号的颜色
thx
您好:
可以直接调整mat-placeholder-required
和mat-form-field-required-marker
的class样式来达到你的目标
解决了,感谢!
如果使用textarea 可以帮textarea加个外框吗
没有相关属性,但可以直接使用CSS处理掉喔!
但也不能保证就一定会正常,还是先把placeholder拿掉保留就好吧!
下方图片placeholder还是存在的XD
下方图片的placeholder是<mat-placeholder>
,拿掉的是placeholder=""
这个属性,抱歉造成误解,已经调整文字说明XD
来自 https://ithelp.ithome.com.tw/articles/10195100
昨天我们介绍了Angular Material的Form Field,为我们的输入类型控制项带来一致的显示成果;今天我们来介绍三个相对简单但是使用频率也非常高的元件-Checkbox、Radio和Slide Toggle,这三个元件有很多类似的地方,因此很适合放在一起学习,遇到类似的地方,还可以当做复习哩。
在Material Design中,Checkbox、Radio和Slide Toggle都被归类在Selection controls中,基本上的概念跟我们使用HTML原生的Checkbox和Radio没什么不同;Checkbox主要是用来提供使用者选择多个选项使用的,而Radio则是提供使用者进行单一选项的选择。
不同的是,Material Design对于focused和pressed状态提出了更容易被识别的效果,如下图:
图片来源:https://material.io/guidelines/components/selection-controls.html#selection-controls-checkbox
另外在Selection controls中还有提到一个元件-Switch,跟同是开关型的Checkbox类似,但建议上Checkbox是提供多个选项时使用,而Switch则是单纯的状态开跟关而已。
例如興趣
可以选择很多种,这时候就可以用Checkbox
;而某个功能的關閉或打開
,则适合使用Switch
。
在Angular Material中Checkbox的功能包装在一个<mat-checkbox>
元件中,要使用这个元件必须先载入MatCheckboxModule
模组。
<mat-checkbox>
是表单元件的一种,因此支援和FormsModule
或ReactiveFormsModule
搭配使用,在这边我们使用ReactiveFormsModule先建立一个资料模型,大致内容如下:
this.surveyForm = new FormGroup({
...,
mainQuestinos: new FormGroup({
payForBook: new FormControl(false),
payForMusic: new FormControl(false),
payForMovie: new FormControl(true),
})
})
接着我们就可以直接使用<mat-checkbox>
来binding这些资料啰
<div>除了生活消費外,你最常花費在哪些項目上?</div>
<mat-checkbox formControlName="payForBook">書籍</mat-checkbox>
<mat-checkbox formControlName="payForMusic">音樂</mat-checkbox>
<mat-checkbox formControlName="payForMovie">電影</mat-checkbox>
一个落落大方的checkbox完成,由于我们的payForMovie的值是true
,因此预设会是被勾选的状态;我们可以试着勾选/取消勾选某个项目看看它的特效,或是使用tab来切换不同的checkbox,看看被focus的checkbox的状态。
在<mat-checkbox>
中,我们不需要跟<input type="checkbox">
一样需要特地用一个<label>
设定标签内容,只需要在<mat-checkbpx>
里面写入要显示的label内容即可,不过这么一来我们不就无法控制label和checkbox谁要在前面谁要在后面了吗?还好Angular Material早就想到这一点了,我们可以透过设定labelPosition
属性为after
(预设)或before
来决定label要显示在checkbox的后面还是前面。
<mat-checkbox formControlName="payForBook" labelPosition="before">書籍</mat-checkbox>
结果如下:
label就跑到checkbox之前啦!(老实说还真的不太习惯@@)
Indeterminate是checkbox一种有趣的状态,这种状态代表的是介于"选取"和"未选取"之间,常常使用在一个checkbox代表全选/取消全选的时候,透过Indeterminate可以得知目前的状态是"部分被选取"。
以下举个例子,我们在原来的功能加上一个全选的checkbox,并设定inteterminate
状态及一些事件绑定:
<mat-checkbox formControlName="payForAll" [indeterminate]="indeterminateSelectedPayFor" (change)="checkAllChange($event)">全部</mat-checkbox>
<mat-checkbox formControlName="payForBook" (change)="payForChange()">書籍</mat-checkbox>
<mat-checkbox formControlName="payForMusic" (change)="payForChange()">音樂</mat-checkbox>
<mat-checkbox formControlName="payForMovie" (change)="payForChange()">電影</mat-checkbox>
接着调整一下程式码,如果选择全部时会依照状态决定是否要「全选」,或「全不选」;只选择部分选项时,则会将indeterminate
状态设定为true
:
export class SurveyComponent implements OnInit {
indeterminateSelectedPayFor: boolean;
constructor(private httpClient: HttpClient) {
this.surveyForm = new FormGroup({
...
mainQuestions: new FormGroup({
payForAll: new FormControl(false),
payForBook: new FormControl(false),
payForMusic: new FormControl(false),
payForMovie: new FormControl(true)
})
});
}
ngOnInit() {
this._setSelectAllState();
}
checkAllChange($event: MatCheckboxChange) {
this.surveyForm
.get('mainQuestions')
.get('payForBook')
.setValue($event.checked);
this.surveyForm
.get('mainQuestions')
.get('payForMusic')
.setValue($event.checked);
this.surveyForm
.get('mainQuestions')
.get('payForMovie')
.setValue($event.checked);
this._setSelectAllState();
}
payForChange() {
this._setSelectAllState();
}
private _setSelectAllState() {
const payForBook = this.surveyForm.get('mainQuestions').get('payForBook').value;
const payForMusic = this.surveyForm.get('mainQuestions').get('payForMusic').value;
const payForMovie = this.surveyForm.get('mainQuestions').get('payForMovie').value;
const count = (payForBook ? 1 : 0) + (payForMusic ? 1 : 0) + (payForMovie ? 1 : 0);
this.surveyForm.get('mainQuestions').get('payForAll').setValue(count === 3);
this.indeterminateSelectedPayFor = count > 0 && count < 3;
}
}
接着就来看看结果吧:
很酷吧!透过indeterminate
的状态调整,Checkbox功能变得更加丰富了!
<mat-checkbox>
预设点击的行为如下:
改变checked
状态,原来为开会变成关,反之亦然。
改变indeterminate
状态,不管原来是true
或false
,只要使用者滑鼠点击,都会变成false
我们可以透过设定MAT_CHECKBOX_CLICK_ACTION
来改变这个行为,例如:
import { MAT_CHECKBOX_CLICK_ACTION } from '@angular/material';
@Component({
...
providers: [
{ provide: MAT_CHECKBOX_CLICK_ACTION, useValue: 'noop' }
]
})
export class SurveyComponent implements OnInit {
结果如下:
由于我们设定MAT_CHECKBOX_CLICK_ACTION
为noop
,代表按下去时除了特效以外什么都不会做,因此我们必须自己加上click事件必撰写程式来调整状态,但是我们也有了最大的调整弹性。
MAT_CHECKBOX_CLICK_ACTION
有3种状态可以设定
noop:按下去后什么都不做,必须在click事件完成该处理的事情,最麻烦但也最有弹性。
check:只会切换checked
状态,而不会改变indeterminate
状态。
check-indeterminate:预设行为,会切换checked
状态以及把indeterminate
设为false
。
改变颜色没有什么特别的技巧,跟其他Angular Material的元件一样,只要加上color
属性即可:
<mat-checkbox color="primary">書籍</mat-checkbox>
<mat-checkbox color="accent">音樂</mat-checkbox>
<mat-checkbox color="warn">電影</mat-checkbox>
成果如下:
接下来我们来聊聊Radio的使用方式,我们可以在加入MatRadioModule
后,搭配<mat-radio-group>
和<mat-radio-button>
来完成一组单选题。
由于<mat-radio>
是单选的状态,为了避免画面上所有的<mat-radio>
只能有一个被选择,我们还需要再使用<mat-radio-group>
来包住<mat-radio>
,在<mat-radio-group>
中,只能会有一个<mat-radio>
被选取。这样的概念跟<select>
和<optoin>
的组合很像,因此实际上的表单元件是<mat-radio-group>
。
直接来看看怎么使用吧!
<div>對於Angular,你的評價是?</div>
<mat-radio-group formControlName="angularLikeScore">
<mat-radio-button [value]="1">超討厭</mat-radio-button>
<mat-radio-button [value]="2">討厭</mat-radio-button>
<mat-radio-button [value]="3">普通</mat-radio-button>
<mat-radio-button [value]="4">喜歡</mat-radio-button>
<mat-radio-button [value]="5">超喜歡</mat-radio-button>
</mat-radio-group>
<div>對於Angular Material,你的評價是?</div>
<mat-radio-group formControlName="angularMaterialLikeScore">
...
</mat-radio-group>
成果如下:
可以看到每个<mat-radio-group>
是彼此独立内的,里面<mat-radio-button>
都只能有一个被选取。
跟前面介绍的<mat-checkbox>
一样,我们都可以用labelPosition
来调整label的位置,而且在<mat-radio-group>
和<mat-radio-button>
中都可以使用;在<mat-radio-group>
设定的话,里面所有的<mat-radio-button>
都会照着这个规则走;在<mat-radio-button>
设定的话,则会盖过<mat-radio-group>
的设定。
<mat-radio-group formControlName="angularMaterialLikeScore" labelPosition="before">
<mat-radio-button [value]="1">超討厭</mat-radio-button>
<mat-radio-button [value]="2">討厭</mat-radio-button>
<mat-radio-button [value]="3">普通</mat-radio-button>
<mat-radio-button [value]="4">喜歡</mat-radio-button>
<mat-radio-button [value]="5" labelPosition="after">超喜歡</mat-radio-button>
</mat-radio-group>
成果如下:
一样,调整color
即可,不过目前无法针对<mat-radio-group>
整个群组一起调整,有点可惜;只能单一调整每个<mat-radio-button>
<mat-radio-button [value]="1" color="primary">超討厭</mat-radio-button>
<mat-radio-button [value]="2" color="accent">討厭</mat-radio-button>
<mat-radio-button [value]="3" color="warn">普通</mat-radio-button>
结果如下:
最后我们来讲Material Design的Switch功能,这个功能在Angular Material被设计成mat-slide-toggle
元件,因此我们必须先加入MatSlideToggleModule
。
<div>訂閱電子報</div>
<div>
<mat-slide-toggle formControlName="subscribeAngular">Angular技巧全集</mat-slide-toggle>
</div>
<div>
<mat-slide-toggle formControlName="subscribeAngularMaterial">Angular Material全攻略</mat-slide-toggle>
</div>
<div>
<mat-slide-toggle formControlName="subscribeNgRx">NgRx入門班</mat-slide-toggle>
</div>
结果如下:
我们不仅可以直接点击来切换状态,也可以用滑鼠拖曳,来慢慢的把状态移到另外一边,在放开滑鼠前,都可以后悔拉回原来的位置,很方便吧!
<mat-slide-toggle>
没有indeterminate
状态。
老规矩,透过labelPosition
就可以达成了。
<mat-slide-toggle labelPosition="before">Angular技巧全集</mat-slide-toggle>
结果如下:
还是老规矩,加上color
:
<mat-slide-toggle formControlName="subscribeAngular" color="primary">Angular技巧全集</mat-slide-toggle>
<mat-slide-toggle formControlName="subscribeAngularMaterial" color="accent">Angular Material全攻略</mat-slide-toggle>
<mat-slide-toggle formControlName="subscribeNgRx" color="warn">NgRx入門班</mat-slide-toggle>
结果如下:
今天我们把Checkbox、Radio和Slide Toggle这3个元件的功能都做了介绍,这3个元件都跟状态的改变非常相关,而除了Checkbox有比较多变化外,其他的其实都不复杂,而且有许多共同之处,因此学习上应该也满轻松的。
学会了Checkbox、Radio和Slide Toggle,之后遇到状态改变相关的画面,应该就能灵活运用啰。
本日的程式码GitHub:https://github.com/wellwind/it-ironman-demo-angular-material/tree/day-14-checkbox-radio-slide-toggle
分支:day-14-checkbox-radio-slide-toggle
这里好像表达错误,
接下来我们来聊聊Radio 的使用方式,我们可以在加入
MatCheckboxModule
后,搭配<mat-radio-group>
和<mat-radio-button>
来完成一组单选题。
应该不是MatCheckboxModule
,而是MatRadioModule
。
来自 https://ithelp.ithome.com.tw/articles/10195230
今天我们要介绍目前Angular Material里面关于Form Control的最后一个元件-Slider;完成Slider的学习之后,问卷页面也会在此告一段落,进入下一个画面的设计,学习更多Angular Material相关的元件,就让我们以比较轻松的脚步完成今天的课题吧!
在Material Design的Sliders设计指南中,Slider是一个允许使用者针对某个目标调整其强度的功能,例如音量、萤幕亮度等等,都很适合使用Slider来设计其功能,Slider的两端代表强度的最大值和最小值,习惯上最大值在右边,而最小值在左边。
图片来源:https://material.io/guidelines/components/sliders.html
我们可以在载入MatSliderModule
后,直接指用<mat-slider>
元件。
<mat-slider>
功能类似于HTML原生的<input type="range">
,我们一样可以使用min
、max
和step
这些参数;接下来我们试着使用<mat-slider>
自订一个RGB颜色选择器,如下:
<div>選擇你最喜歡的顏色</div>
<div>
<span>Red</span>
<mat-slider formControlName="favoriteColorRed" min="0" max="255" step="1"></mat-slider>
</div>
<div>
<span>Green</span>
<mat-slider formControlName="favoriteColorGreen" min="0" max="255" step="1"></mat-slider>
</div>
<div>
<span>Blue</span>
<mat-slider formControlName="favoriteColorBlue" min="0" max="255" step="1"></mat-slider>
</div>
<div>RGB: ({{ selectedColorRed }}, {{ selectedColorGreen }}, {{ selectedColorBlue }})</div>
<div style="width: 50px; height: 50px; border: 1px solid #000" [ngStyle]="{background: selectedColorStyle}"></div>
程式码中我们再加入对应的设定:
export class SurveyComponent implements OnInit {
surveyForm: FormGroup;
get selectedColorRed() {
return this.surveyForm.get('otherQuestions').get('favoriteColorRed').value;
}
get selectedColorGreen() {
return this.surveyForm.get('otherQuestions').get('favoriteColorGreen').value;
}
get selectedColorBlue() {
return this.surveyForm.get('otherQuestions').get('favoriteColorBlue').value;
}
// 組合顏色樣式
get selectedColorStyle() {
return `rgb(${this.selectedColorRed}, ${this.selectedColorGreen}, ${this.selectedColorBlue})`;
}
constructor() {
this.surveyForm = new FormGroup({
...,
otherQuestions: new FormGroup({
favoriteColorRed: new FormControl(0),
favoriteColorGreen: new FormControl(0),
favoriteColorBlue: new FormControl(0)
})
})
}
}
成果如下:
很简单吧!我们可以透过拖曳<mat-slider>
来调整每个颜色的强度,另外也能使用键盘的上下左右键来调整数值,甚至可以用PageDown
、PageUp
一次跳多一点的数字,以及Home
和End
直接跳到起始或结束,只能说Angular Material设计得太贴心啦!
目前<mat-slider>
可用的键盘对应如下:
预设下<mat-slider>
是水平的,但我们可以透过设定vertical
来改变成垂直的方向。
<mat-slider formControlName="favoriteColorRed" ... vertical></mat-slider>
结果如下:
如同文章开始时介绍过,预设情境下,最大值会放在右边,最小值在左边;但这个设定也是可以被改变的,只要加上invert
即可。
<mat-slider formControlName="favoriteColorGreen" min="0" max="255" step="1" invert></mat-slider>
结果如下:
原本设定绿色的Slider最小值0
就跑到另外一边去啰。
预设下,我们是看不到<mat-slider>
选择的数字的,这样有时候不太方便,如果是一个给予评价的Slider(例如1~5颗星),总不会单纯的是凭感觉去拉动吧!这时候我们可以透过thumbLabel
,来显示一个目前数值的标志,如下:
<div>關於這個問卷你覺得設計得如何?</div>
<mat-slider formControlName="surveyScore" min="0" max="100" step="10" thumbLabel></mat-slider>
成果如下:
我们也可以设定tickInterval
,替<mat-slider>
显示一个刻度标示,让我们在选择时更加的有把握!我们可以给予一个tickInterval
一个数值,代表每隔多少显示一个刻度标示;有一点需要特别注意的是,tickInterval
和step
是有关系的,我们可以想像成是:每步要移动几个数字(step),且每几步(tickInterval)放置一个标志(tick),因此假设我们设定每步移动5
,且每2
步放一个标志,代表每移动10
就会看到一个刻度。
另外我们也可以给一个字串auto
让Angular Material自己去算,这么一来Angular Material会在每30px
替我们加上一个刻度。
<mat-slider formControlName="surveyScore" min="0" max="100" step="5" thumbLabel [tickInterval]="2"></mat-slider>
结果如下:
今天我们用很轻松的步调介绍了<mat-slider>
这个元件,虽然它可以设定的参数不多,因此相对简单许多,但每个参数都有着华丽的效果,让使用者在操作Slider时能够更明确的知道自己目前的状态。
本日的程式码GitHub:https://github.com/wellwind/it-ironman-demo-angular-material/tree/day-15-slider
分支:day-15-slider
到今天我们终于把所有目前Angular Material的Form Control相关元件全部介绍完啦,代表着问卷表单的功能也该到此告一段落,相信到目前为止各位读者应该已经能够熟悉各种表单元件的使用了吧!如果有任何问题都欢迎提出来,大家一起学习讨论。
明天开始我们将开始透过另一个新的页面-部落格,来学习更多Angular Material相关的元件,让我们继续徜徉在Angular Material的丰富资源里面吧!明天见啰!!
Thumbnail 的形状同颜色可以改吗?
<mat-slider>
本身也可以设定color="xxx"
,这样会同时修改thumbLabel的颜色。
如果希望单独调整thumbLabel,就需要自己调整CSS了,主要是调整mat-slider-thumb-label
和mat-slider-thumb-label-text
这两个class
.mat-slider-thumb-label {
background-color: yellow !important;
}
.mat-slider-thumb-label-text {
color: black !important;
}
成果:
在Angular Material里面所有的东西都会搭上css class,所以要客制化还是很容易滴!
来自 https://ithelp.ithome.com.tw/articles/10195509
在前几天介绍完各式各样的表单元件后,今天我们要先来看看在Material Design其中一种显示清单资料的方法-Grid List,以及Angular Material中如何实现应用这样的方法,把一个简易的部落格版型给切出来!
在Material Design的Grid Lists设计指南中,Grid List是一种用网格呈现重复性资料方式,可以想像成是用来放置多组类似的元件的容器,具有水平和垂直的排列组合,来呈现资料。由于有水平和垂直的排列组合,我们也可以想像Grid List是一种类似表格式的排版(当然不会真的用<table>
去做排版)。
<
Grid List使用的是网格的概念,每一个格子我们会称之为Cell;Tile则代表用来放置内容的容器,且一个Tile是可以横跨多个Cells的。
<
以上图片来源:https://material.io/guidelines/components/grid-lists.html
接着我们就实际来写写看程式,毕竟对于工程师来说,还是透过写程式最容易理解一个概念啦!
在Angular Material中Grid List主要由两个元件组成,分别是:
<mat-grid-list>
:用来设定Grid List的基本状态
<mat-grid-tile>
:放置在<mat-grid-list>
中,代表一个又一个的tile,我们可以在<mat-grid-tile>
中设定它会横跨几个垂直或水平的cell。
只需要加入MatGridListModule
,就可以开始使用Grid List的功能啰。
虽然Grid List适合用来放置重复型的清单资料,但也很适合当作一个排版系统来使用,虽然针对排版Angular也有一套angular/flex-layout
可以使用,但这套目前还是beta版,且进度缓慢,拿来用在production还是会觉得抖,而且若flex不是很熟,使用上也不太容易;反观Angular Material的Grid Lsit,它的概念就相对简单很多,而且使用上非常贴近我们都熟悉的Table;虽然功能没那么强大,但是对于基本的排版可以说是完全没问题!因此接下来我们介绍时也会把它当作排版系统使用,但实际上你要用它来呈现清单资料等等是完全没问题的!
所有的Grid List都是使用<mat-grid-list>
作为起手式,而<mat-grid-list>
至少需要设定一个参数:cols
,代表这个Grid List会有几个栏位资料。
<mat-grid-list cols="3"></mat-grid-list>
在这里我们设定cols="3"
,<mat-grid-list>
会自动占满可以使用的宽度,然后平均分成3等分(cell)。
接着我们可以在里面放置<mat-grid-tile>
,代表每一个资料呈现的区块,<mat-grid-list>
则会依照cols
的设定,决定每列放置多少个<mat-grid-tile>
<mat-grid-list cols="3">
<mat-grid-tile style="background: red">
Tile 1
</mat-grid-tile>
<mat-grid-tile style="background: green">
Tile 2
</mat-grid-tile>
<mat-grid-tile style="background: blue">
Tile 3
</mat-grid-tile>
<mat-grid-tile style="background: yellow">
Tile 4
</mat-grid-tile>
</mat-grid-list>
结果如下图:
<
可以看到每个<mat-grid-tile>
所占的宽度为三分之一,因此第四个tile就会被放置到第二列(row)上,一个基本的Grid List就完成啰。
<mat-grid-list>
会依照col
的设定来切割cell,此时每个cell的宽度会平均,假设<mat-grid-list>
占满宽度后是300px
,此时每个cell的宽度就是100px
,同时<mat-grid-list>
也会将高度视为100px
,成为一个正方形,这么一来萤幕越宽,每个cell的高度就越高,这样的高度未必是我们要的,因此我们可以藉由设定rowHeight
来控制高度。
例如我们希望每列高度是100px
,则可以如下设定:
<mat-grid-list cols="3" rowHeight="100px">
...
</mat-grid-list>
结果如下:
<
每列的高度就会变成如我们所设定的,而非每个cell都是正方形了!
除此之外,我们也可以给予一个比例的字串,来决定宽跟高的比例,这时候高度就会依照目前tile宽度自动去计算
<mat-grid-list cols="3" rowHeight="4:1">
...
</mat-grid-list>
如同前面提到的,一个tile可以水平和垂直的跨越多个cell,在Angular Material中我们可以设定<mat-grid-tile>
的rowpspan
和colspan
(预设都是1)来控制每个tile跨越的列和栏。
觉得很眼熟吗?没错!就跟table的
rowspan
和colspan
是一样的概念,只是使用table排版是众所皆知的罪恶(?),而在<mat-grid-list>
中所使用的排版系统是flex,但是排版起来却像容易理解哩。
接着我们来试试看作以下调整:
首先把原来的Tile 1当作是广告横幅,因此要横向跨越2个cell(也就是colspan
)
<mat-grid-tile style="background: red" colspan="2">
Tile 1(橫幅廣告)
</mat-grid-tile>
结果:
<
此时Tile 2就被挤到最右边,正好可以当作是右边的最新文章等等的清单,让他垂直跨越5个cell(也就是rowspan
)
<mat-grid-tile style="background: green" rowspan="5">
Tile 2(右邊清單資訊)
</mat-grid-tile>
结果:
<
接着我们先看Tile 4,一样希望他是一个在最下面的横幅广告,因此让他跨越2个cell,程式码如Tile 1的设定,结果如下:
<
由于Tile 2垂直跨越5个cell的关系,导致第二列只剩下2个cell可以用,而原来的Tile 3已经占用1个cell,所以占用2个cell的Tile 2无法盎在Tile 3旁边,就被推到下方去了。
接着我们测试看看,在第二列只剩下2个cell可以用的情看下,设定colspan=3
会有什么结果:
<
结果就是因为放不下了,会一路找到可以放置的空间,再把它放下去。
那么如果设定colspan=4
让它超过原来cols=3
的设定呢?
<
结果就是一片空白,然后跳出错误讯息啦!这种细节Angular Material才不会忘记呢!
测试到此结束,让我们认真的设定Tile 3,我们希望Tile 3用来放置接下来部落格列表的资讯,我们可以合并使用colspan
和rowspan
填满剩下的空间!
<mat-grid-tile style="background: blue" colspan="2" rowspan="3">
Tile 3(文章內容區)
</mat-grid-tile>
成果如下,是不是越来越有一个部落格版型的样子啦!
<
我们可以在<mat-grid-list>
中使用gutterSize
来调整每个cell之间的间距,可以使用px
、em
或rem
为单位
<mat-grid-list cols="3" rowHeight="100px" gutterSize="20px">
结果如下:
<
在每个tile内,我们可以使用<mat-grid-tile-header>
和<mat-grid-tile-footer>
,为我们的<mat-grid-tile>
加上header跟footer:
<mat-grid-tile style="background: green" rowspan="5">
<mat-grid-tile-header>
<h3 mat-line>功能清單</h3>
<span mat-line>選擇你要的</span>
<mat-icon>list</mat-icon>
</mat-grid-tile-header>
<mat-grid-tile-footer>
<span mat-line>生是IT人</span>
<span mat-line>死是IT魂</span>
<span mat-line>但我不想死</span>
<mat-icon>thumb_up</mat-icon>
</mat-grid-tile-footer>
Tile 2(右邊清單資訊)
</mat-grid-tile>
结果如下:
<
今天我们介绍了一个很重要的元件-Grid List,透过Grid List我们可以用比较视觉化的方式来呈现重复性的资料,同时我们也能使用Grid List来当作的排版系统使用,不管是呈现资料还是依照需求切割画面都非常好用。
藉由Grid List我们今天完成了基本部落格版型的排版,明天我们要来介绍Material Design中最常用来呈现资料的Card啰!
本日的程式码GitHub:https://github.com/wellwind/it-ironman-demo-angular-material/tree/day-16-grid
分支:day-16-grid-list
您好喔,想请问一下grid里面的元素好像都会被预设为类似flex:left堆叠成同一排,若想让他分行(display:inline 设了没有反应),等排版该如何设置,文件里只提到mat-grid-tile-header和mat-grid-tile-footer,但似乎没有content。rowHeight似乎真的是固定的所以用它来排版似乎会有点障碍,无法浮动拉长,会盖掉内容orz
根据上下文,这段程式码似乎写错了,col="3" 才对
<mat-grid-list cols="2">
<mat-grid-tile style="background: red">
Tile 1
</mat-grid-tile>
<mat-grid-tile style="background: green">
Tile 2
</mat-grid-tile>
<mat-grid-tile style="background: blue">
Tile 3
</mat-grid-tile>
<mat-grid-tile style="background: yellow">
Tile 4
</mat-grid-tile>
</mat-grid-list>
来自 https://ithelp.ithome.com.tw/articles/10195672
在昨天介绍完Grid List之后,今天我们要介绍一个Material Design中被使用频率非常高,且个人认为是Material Design中很经典的元件-Card。我们将利用Card来当作部落格列表的呈现方式。
在Material Design的Cards设计指南中,Card是用来针对某一个主题放置图片、文字或连结的地方,它能够把这些图片、文字或连结都整合到一个卡片之中,而这个卡片就代表了我们的目标主题。
由于Card是Material Design中最主要的资料显示方式,因此里面的资料呈现也有很多不同丰富的变化,建议可以直接进去说明文里面看,会对于Card的呈现方式更加的有感觉。
在开始使用Card之前,我们要先将原来的Grid List排版做点调整,我们预期的最终结果如下:
在主要的文章清单中,最上方位有两个置顶文章,而下面会显示6篇文章,每篇宽度占1格cell;高度在规划后,置顶文章需要占掉2格cell,清单文章需要占掉6格cell,6篇共3列,就会用掉18格cell的高度,加上置顶文章的2格,共20格cell的高度;另外加上上下横幅各1格,加起来总共会用掉22格cell的高度,因此原来放置右边选单的Tile 2高度我们会重新设定为rowspan="22"
。
另外,昨天我们把Tile 3当作整个放文章的容器,但目前<mat-grid-list>
是没办法直接巢状使用的,也就是在<mat-grid-tile>
中,我们是无法加上<mat-grid-list>
的,虽然不会出错,但也不会显示内容(实际上是跑掉了),比较简单的方式就是重新规划好cols
及rowHeight
,每个Tile里面就直接放置内容就好。
另外我们也把gutterSize
拿掉,以免影响内容显示,调整后的程式码大致如下:
<mat-grid-list cols="3" rowHeight="100px">
<mat-grid-tile style="background: red" colspan="2">
Tile 1(橫幅廣告)
</mat-grid-tile>
<mat-grid-tile style="background: green" rowspan="22">
Tile 2(右邊清單資訊)
</mat-grid-tile>
<mat-grid-tile rowspan="2" style="background: blue">
置頂文章1
</mat-grid-tile>
<mat-grid-tile rowspan="2" style="background: blue">
置頂文章2
</mat-grid-tile>
<mat-grid-tile *ngFor="let post of posts$ | async" rowspan="6" style="background: blue">
{{ post.title }}
</mat-grid-tile>
<mat-grid-tile style="background: yellow" colspan="2">
Tile 4(下方橫幅廣告)
</mat-grid-tile>
</mat-grid-list>
看起来如下:
接下来在加入MatCardModule
之后,我们就正式开始用Card元件来填满我们的tile吧!
我们可以使用<mat-card>
立刻建立一个简单的卡片,如下:
<mat-grid-tile *ngFor="let post of posts$ | async" rowspan="6">
<mat-card>
{{ post.title }}
</mat-card>
</mat-grid-tile>
一个简单的卡片就完成啦!
不过目前可以看到排版有点奇怪,由于<mat-grid-tile>
会把资料都置中,因此卡片的位置也会放在正中间,并且随着内容变宽变高,这个问题不难,用CSS调整一下就好:
.post-tile {
align-self: flex-start;
width: 100%;
margin: 5px;
}
接着只要为卡片加上这个class
<mat-card class="post-tile"></mat-card>
就可以看到如下结果,卡片就乖乖地放到比较适合的位置啰。
在<mat-card>
内,有一些内建的directive,如下:
directive | 内容 |
---|---|
<mat-card-title> | 卡片标题 |
<mat-card-subtitle> | 卡片子标题 |
<mat-card-content> | 主要的卡片内容,会在四周留下一些空白来放置文字 |
<img mat-card-image> | 卡片的图片,会随着卡片大小自动延展 |
<mat-card-actions> | 卡片底部用来放置一些执行动作按钮的区块 |
<mat-card-footer> | 卡片的最底部 |
这些directives可以为卡片内的内容加上一些内建的显示样式,我们直接试着把这些directives加到卡片中吧:
<mat-grid-tile *ngFor="let post of posts$ | async" rowspan="6">
<mat-card class="post-tile">
<mat-card-title>{{ post.title.substring(0, 15) }}...</mat-card-title>
<mat-card-subtitle>User Id: {{ post.userId }}</mat-card-subtitle>
<img mat-card-avatar src="https://picsum.photos/300/300/?random" />
<img mat-card-image src="https://picsum.photos/300/300/?random" />
<mat-card-content>{{ post.body.substring(0, 100) }}...</mat-card-content>
<mat-card-actions>
<button mat-button color="paimray">繼續閱讀</button>
<button mat-button color="accent">編輯</button>
</mat-card-actions>
<mat-card-footer>IT鐵人賽系列好文</mat-card-footer>
</mat-card>
</mat-grid-tile>
我们使用的图片服务:https://picsum.photos,由于图片每次是随机的,所以接下来的截图中显示图片的部份每次都会不一样。
结果如下:
一张内容丰富的卡片就完成啰!
另外针对<mat-card-actions>
,我们也可以设定align
为start
(预设值)或end
,来决定按钮的对齐方向
<mat-card-actions align="end">
<button mat-button color="paimray">繼續閱讀</button>
<button mat-button color="accent">編輯</button>
</mat-card-actions>
结果如下:
在上面的卡片有一个看起来奇怪的地方,就是<mat-card-title>
、<mat-card-subtitle>
和<img mat-card-avatar>
是一列一列显示的,如下:
这有点不符合我们的期待,也太占空间了,这时候我们可以使用<mat-card-header>
这个元件把上述的directives包起来:
<mat-card-header>
<mat-card-title>{{ post.title.substring(0, 15) }}...</mat-card-title>
<mat-card-subtitle>User Id: {{ post.userId }}</mat-card-subtitle>
<img mat-card-avatar src="https://picsum.photos/300/300/?random" />
</mat-card-header>
成果如下:
关于mat-card-image
,由于图片预设会以正方形呈现,且随着卡片大小自动延伸,但我们的<mat-grid-tile>
高度是固定的,这时候可能反而会因为图片太大而造成卡片下方的资料无法完整显示,如下
如果以显示完整资料为主,不介意图片形状被改变的话,这时候我们可以透过设定CSS来固定图片的高度,例如:
.post-tile .mat-card-image {
max-height: 300px;
}
.mat-card-image
这个CSS class是哪里来的呢?
在所有<mat-card>
的directive再加入后都会自动加入一个与名称相同的class( 实际上所有的Angular Material元件都会这做,方便我们自订样式 ),因此我们的<img mat-card-image>
在显示时实际上会是<img mat-card-image class="mat-card-image">
,这么一来就可以轻松透过CSS来做一些样式的调整,在这里我们可以使用这种技巧,直接去调整mat-card-image
这个class。
成果如下:
我们刚刚使用过了<mat-card-header>
来组合<mat-card-title>
、<mat-card-subtitle>
和<img mat-card-avatar>
,而<img mat-card-avatar>
主要是用来放置使用者的头像,在Material Design中对于卡片还有另外一种显示方式,是把缩图放在标题的右边,如下图:
这个功能我们可以使用<mat-card-title-group>
来达成,<mat-card-title-group>
可以组合以下directive:
<mat-card-title>
<mat-card-subtitle>
以下其中一个
<img mat-card-sm-image>
:80 x 80
<img mat-card-md-image>
:112 x 112
<img mat-card-lg-image>
:152 x 152
我们可以试试看把预期放置顶文章的部分,用这种方式显示资料:
<mat-grid-tile rowspan="2">
<mat-card class="post-tile">
<mat-card-title-group>
<mat-card-title>置頂文章1</mat-card-title>
<mat-card-subtitle>2917/01/04</mat-card-subtitle>
<img mat-card-sm-image src="https://picsum.photos/300/300/?random" />
</mat-card-title-group>
<mat-card-content>文章內容1...</mat-card-content>
<mat-card-actions>
<button mat-button color="paimray">繼續閱讀</button>
</mat-card-actions>
</mat-card>
</mat-grid-tile>
成果如下:
是不是有另外一种感觉啊!
<mat-card>
是可以被focus的,我们只需要设定好它的tabindex
即可,透过这种设定我们可以让卡片跟清单一样,拥有被选择的功能:
<mat-grid-tile *ngFor="let post of posts$ | async; let index = index" rowspan="6">
<mat-card class="post-tile" [tabindex]="index">
...
</mat-card>
</mat-grid-tile>
我们为卡片加上:focus
的样式,来看看确认它是可以被选中的:
mat-card:focus {
background: lightblue;
}
成果如下:
可以看到被focus的<mat-card>
不仅拥有浏览器内建被选择的提示框,我们也可以直接使用:focus
的css selector来设定它的样式,非常方便吧!
今天我们介绍Angular Material中的卡片元件(card),这个元件的应用范围非常广,也是目前很主流的一种显示模式,几乎各种资料的显示都可以使用卡片来呈现,只是变换不同的样式而已,因此好好的把卡片学会,对于使用Angular Material开发应用程式会有很大的帮助喔!
本日的程式码GitHub:https://github.com/wellwind/it-ironman-demo-angular-material/tree/day-17-card
分支:day-17-card
来自 https://ithelp.ithome.com.tw/articles/10195851
昨天我们介绍了好用的Card,并透过Card来显示部落格的文章,今天我们要来介绍两个跟显示进度有关系的元件,分别是Progress Bar和Progress Spinner,尽管这两个元件相对简单很多,但在SPA架构下,这两个元件可以说是非常核心的功能哩。
Progress Bar和Progress Spinner被归纳在Material Design的Progress & activity设计指南中,主要是用来提示使用者内容正在读取中,依照显示的位置不同,可以使用长条的显示(Progress Bar)、或是圆形的显示(Progress Spinner),适合使用在读取(或重新整理)新的资料时,告知使用者内容正在读取中;依照使用情境不同,我们也能选择让使用者知道目前确定的进度( Determinate indicators ),或是显示不确定的进度( Indeterminate indicators )。
图片来源:https://material.io/guidelines/components/progress-activity.html
##开始使用Angular Material的Progress Bar
让我们先从Progress Bar开始学习起,依照古老(也就是前10多篇)的惯例,我们可以在加入MatProgressBarModule
后,开始使用Progress Bar的相关功能。
我们在原来的部落格边栏部分,放一个简单的Progress Bar看看,只需要加入<mat-progress-bar>
就好
<mat-grid-tile rowspan="22">
...
<div class="blog-sidebard-content">
<h4>
發文進度
</h4>
<mat-progress-bar></mat-progress-bar>
</div>
</mat-grid-tile>
另外我们加了一个小样式,让资料呈现比较清楚
.blog-sidebard-content {
align-self: flex-start;
padding-top: 100px;
width: 95%;
}
结果如下:
一个超基本的Progress Bar就完成啦!接着我们把进度填进去吧。
要显示进度,只需要设定value
属性即可,这里我们没有min
、max
等属性可以设定,预设就是0~100,低于或超过都不会影响呈现的变化:
<mat-progress-bar value="60"></mat-progress-bar>
使用value="60"
或是[value]="60"
都可以,字串或数字对结果不会有影响,结果如下:
进度就明显的显示上去啰,例外一个有趣的是,当数字在变化时,还能够看到动画的效果!
<mat-progress-bar [value]="progress"></mat-progress-bar>
<button mat-raised-button (click)="progress = progress - 10">-10</button>
<button mat-raised-button (click)="progress = progress + 10">+10</button>
成果如下:
很生动吧!
<mat-progress-bar>
有一个属性mode
,具有4种模式,分别代表不同的显示方式:
determinate:预设值,会依照value
属性决定进度
buffer:除了原来的value
以外还可以设定bufferValue
属性,会在value
和bufferValue
之间多一块缓冲区,而空白的部分则会变成另一种效果的显示方式
<h4>Buffer Progress Bar</h4>
<mat-progress-bar mode="buffer" value="30" bufferValue="60"></mat-progress-bar>
成果:
indeterminate:代表不确定的进度,value
和bufferValue
属性都不能使用,当需要使用者进行等待的时候适用。
<h4>indeterminate Progress Bar</h4>
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
成果:
query:跟indeterminate一样,但进度的方向刚好相反,适合在loading之前的前置准备时使用,要进入loading时再调整成indeterminate
<h4>Query Progress Bar</h4>
<mat-progress-bar mode="query"></mat-progress-bar>
成果:
一样还是老规则,只需要设定color
为primary
、accent
和warn
即可切换颜色。
<h4>Primary</h4>
<mat-progress-bar mode="buffer" color="primary"></mat-progress-bar>
<h4>Accent</h4>
<mat-progress-bar mode="buffer" color="accent"></mat-progress-bar>
<h4>Warn</h4>
<mat-progress-bar mode="buffer" color="warn"></mat-progress-bar>
结果如下:
由于是扁长型的显示方式,因此放在一些独立显示的元件如Card下可以说是非常适合:
<mat-card class="post-tile" [tabindex]="index">
...
<mat-card-footer>
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
</mat-card-footer>
</mat-card>
成果如下:
当某个按钮按下去,直接在卡片下方显示loading状态,看起来完全没有违和感啊!
Progress Spinner基本上概念跟Progress Bar大同小异,只是从直条显示变成圆形而已,加入MatProgressSpinnerModule
后,直接使用<mat-progress-spinner>
即可:
<h4>Progress Spinner</h4>
<mat-progress-spinner [value]="progress"></mat-progress-spinner>
<button mat-raised-button (click)="progress = progress - 10">-10</button>
<button mat-raised-button (click)="progress = progress + 10">+10</button>
结果:
很简单吧!
<mat-progress-spinner>
的mode
只有两个:
determinate:预设值,会透过value
决定进度显示。
indeterminate:代表进度不确定,会忽略value
的设定。
<h4>Indeterminate Progress Spinner</h4>
<mat-progress-spinner mode="indeterminate"></mat-progress-spinner>
成果:
<mat-progress-spinner>
还有一个strokeWidth
属性,我们可以透过这个属性来调整它的线条宽度
<h4>Spinner Stroke Width</h4>
<mat-progress-spinner value="60" [strokeWidth]="strokeWidth"></mat-progress-spinner>
<button mat-raised-button (click)="strokeWidth = strokeWidth - 1">-1</button>
{{ strokeWidth }}
<button mat-raised-button (click)="strokeWidth = strokeWidth + 1">+1</button>
成果如下:
除了线条宽度以外,透过diameter
属性,我们也能调整圆圈的直径大小:
<h4>Spinner Stroke Width</h4>
<mat-progress-spinner value="60" [diameter]="diameter"></mat-progress-spinner>
<button mat-raised-button (click)="diameter = diameter - 10">-10</button>
{{ diameter }}
<button mat-raised-button (click)="diameter = diameter + 10">+10</button>
成果如下:
因为indeterminate
模式在spinner实在太常用了,因此还有了一个<mat-spinner>
可以使用;<mat-spinner>
可以想像成是<mat-progress-spinner mode="indeterminate">
的缩写,因此不能额外设定mode
和value
,不过strokeWidth
和diameter
依然也都可以设定:
<h4>Very Small Spinner</h4>
<mat-spinner [strokeWidth]="1" [diameter]="30"></mat-spinner>
成果如下:
Progress Spinner由于体积较大的关系,通常会放置在预期会有内容,但目前还没有内容的地方,等到内容产生后再将其隐藏,例如我们可以在部落格文章读取完之前,先放上一个Progress Spinner
<ng-template #loading>
<mat-grid-tile colspan="2">
<mat-spinner></mat-spinner>
</mat-grid-tile>
</ng-template>
<ng-container *ngIf="posts$ | async as posts; else loading">
<mat-grid-tile *ngFor="let post of posts; let index = index" rowspan="6">
...
</mat-grid-tile>
</ng-container>
结果如下:
看起来就变得清楚多啦!
在SPA架构下,loading的功能可以说是非常的重要,透过Progress Bar和Progress Spinner,我们可以用很明确的方式让使用者知道目前的进度状态。
Progress Bar可以附加在别元件区块的上下方,让我们知道这个元件目前的状态是在读取中。同时它也有着多采多姿的显示模式。
Progress Spinner显示的模式相对单纯,但对于资料显示前的空白时间可以说是最重要的一个元件,没有这个元件,在读取资料到画面上前,只会是一片的空白,光想就觉得可怕啊!
把这两个元件学好,遇到需要显示loading状况时就不怕没有武器可以应对啰。
本日的程式码GitHub:https://github.com/wellwind/it-ironman-demo-angular-material/tree/day-18-progress-bar-progress-spinner
分支:day-18-progress-bar-progress-spinner
来自 https://ithelp.ithome.com.tw/articles/10196030