我们在做项目时,有时也需要自己封装一些组件库的功能。因为无论是什么组件库,可能都封装了一些自己不想用、但人家却认为是必需的功能,如果你不去读人家的代码,那遇到坑的概率就比较大。
我最近也遇到了一个primeng的坑,他封装的checkbox在点击时,不知道加了什么东西,会影响列表布局,因此只能自己手写一个了。
angular的规矩比较多,因此封装组件比起vue要复杂一些,咱们一步一步来。
新建组件
首先我们通过命令生成一个component组件。
1
|
ng g c components/checkbox
|
待实现的功能
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 |
//father.component.html:
<ul> <li *ngFor=”let item of listData”> <app-checkbox [(ngModel)]=”item.checked” [label]=”item.title” (onChange)=”doSth($event,item)”></app-checkbox> </li> </ul> //father.component.ts: import { Component, OnInit } from ‘@angular/core’; @Component({ selector: ‘app-father’, templateUrl: ‘./father.component.html’, styleUrls: [‘./father.component.scss’] }) export class FatherComponent implements OnInit { listData = [{ checked:false, title:’张三’ },{ checked:true, title:’李四’ }]; doSth(obj,item){ item.checked = obj.checked; console.log(item); } |
设计思路
checkbox组件主要是利用了label标签的原理。
如果在label标签里面放input,当不设置label的for属性时,点击label会默认点到input上。
因此我们可以把双向绑定仍旧放在input即可,然后我们画一个自己的checkbox样式,将原来的input隐藏。
html结构
1
2 3 4 5 6 7 8 9 |
<label>
<!–假的input框–> <span> <i>√</i> </span> <!–真的input–> <input type=”checkbox” /> <span>文本信息</span> </label> |
我们用一个span标签绘制一个假的checkbox框,原来的input需要隐藏掉,这里的对勾请大家自行找个字体库哈。
scss样式
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 |
.mycheck {
color:#606266; cursor:pointer; white-space: nowrap; .inner{ /*假的input*/ display: inline-block; border:1px solid #646464; width:20px; height:20px; line-height:20px; color:#606266; text-align: center; transition:background-color 0.2s,color 0.2s,border-color 0.2s,box-shadow 0.2s; background-color:#fff; } .origin{ /*隐藏真的input*/ position:absolute; visibility: hidden; } .title{ margin-left:5px; } } |
隐藏真实的input,我们不能用display:none,因为这样input就从文档流删掉了,因此需要用visibility。不过这还不够,因为visibility会占空位,因此我们还需设置绝对定位让他脱离文档流。
ts逻辑实现
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
import { Component, OnInit,Input,Output,EventEmitter,forwardRef } from ‘@angular/core’;
import { ControlValueAccessor,NG_VALUE_ACCESSOR } from ‘@angular/forms’ @Component({ selector: ‘app-checkbox’, templateUrl: ‘./checkbox.component.html’, styleUrls: [‘./checkbox.component.scss’], providers:[{ // 固定写法,照着抄就行 provide:NG_VALUE_ACCESSOR, useExisting:forwardRef(()=>CheckboxComponent), multi:true }] }) export class CheckboxComponent implements OnInit, ControlValueAccessor{ @Input() label:string; //ControlValueAccessor要求实现的 ngOnInit(): void { } |
我们再重复一下实现逻辑。
首先我们把checkbox作为一个独立体看待。
我们设计了value计算属性,但这个value什么时候会被改变呢?
就是ControlValueAccessor接口提供的writeValue方法。
ControlValueAccessor接口需要实现writeValue、registerOnChange、registerOnTouched三个方法,其他两个方法分别是注册change和click事件。
click我们没用到,因此就没写什么东西,主要是用了change事件。
我们在注册时用onModelChange事件接收了形参,这就完成了初始化,然后在writeValue使用onModelChange即可实现赋值。
这些都是固定的写法,大家照着抄就行了。
修正模板
最后我们修正一下模板。
1
2 3 4 5 6 7 8 9 |
<label [ngClass]=”{‘isChecked’: value}”>
<!–假的input框–> <span> <i *ngIf=”value”>√</i> </span> <!–真的input–> <input type=”checkbox” [(ngModel)]=”value” (change)=”handleChange($event)”/> <span>{{label}}</span> </label> |