问题拆解
- 如何封装表单控件?
- 通过onChange(官方api),将新的值通知给父表单控件或模型更新
onTouch 用于标记表单控件已被触摸(即用户曾与控件交互过),通常用于设置控件的 touched 状态
- 如何自定义下拉项,增加新增、删除功能?
label-select.component.html
<nz-select
*ngIf="isEditing"
[(ngModel)]="tagIds"
nzPlaceHolder="请输入标签"
nzShowSearch
nzAllowClear
nzMode="tags"
class="label-select width-100"
nzDropdownClassName="tag-dropdown"
(ngModelChange)="tageChange($event)"
>
<ng-container *ngFor="let option of tagOptions">
<nz-option
*ngIf="!tageLoading"
[nzLabel]="option.name"
[nzValue]="option.id"
nzCustomContent
>
<div class="d-flex justify-content-between align-items-center">
{{ option.name }}
<div>
<span
*ngIf="(tagIds || []).includes(option.id)"
nz-icon
nzType="check"
class="text-blue mr-sm"
></span>
<span
(click)="deleteClick(option)
nz-icon
nzType="delete"
[ngClass]="{
'text-grey': option.related,
'text-red': !option.related
}"
></span>
</div>
</div>
</nz-option>
</ng-container>
<nz-option nzDisabled nzCustomContent *ngIf="tageLoading">
<i nz-icon nzType="loading" class="loading-icon"></i> Loading Data...
</nz-option>
</nz-select>
<div *ngIf="!isEditing" class="d-inline-flex" style="flex-flow: row wrap">
<!-- textEllipsis是一个省略显示的指令,下次出了文章给出链接 -->
<nz-tag
textEllipsis
style="max-width: 100%"
class="mt-xs"
nzColor="blue"
*ngFor="let option of getTagListByTagIds(tagIds, tagOptions) ?? []"
>{{ option.name }}</nz-tag
>
</div>
label-select.component.ts
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { _HttpClient } from '@delon/theme';
import { deepCopy } from '@delon/util';
import { firstValueFrom } from 'rxjs';
@Component({
selector: 'label-select',
templateUrl: './label-select.component.html',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => LabelSelectComponent),
multi: true,
},
],
styles: [
`
.label-select {
color: #1890ff;
}
.label-select ::ng-deep .ant-select-selection-item-remove {
color: #1890ff;
}
::ng-deep .tag-dropdown .ant-select-item .ant-select-item-option-state {
display: none;
}
`,
],
})
export class LabelSelectComponent implements OnInit {
constructor(
public apiService: ApiService,
private http: _HttpClient,
) {}
@Input() isEditing: boolean = true;
@Input() tagType: string = 'svc';
tageLoading = false;
tagIds: any[] = [];
tagList: any[] = [];
tagOptions: any[] = [];
ngOnInit() {
this.getTagList();
}
getTagList(addName?: string) {
this.http
.get('/taginfo/getList', {
type: this.tagType,
})
.subscribe({
next: (res: any) => {
if (res.code === 200) {
this.tagList = res.data || [];
this.tagOptions = deepCopy(this.tagList);
if (addName && this.tagIds) {
const id = this.tagList.find((i) => i.name === addName)?.id;
id && this.tagIds.push(id);
}
} else {
console.log('可用小弹窗提示',res.message)
}
},
error: () => { console.log('接口错误',error) },
});
}
async tageChange(val) {
const addValue = (val || []).filter(
(i) => this.tagList.findIndex((j) => j.id === i) === -1,
);
const isExistValue = (val || []).filter(
(i) => this.tagList.findIndex((j) => j.id === i) !== -1,
);
this.onChange(this.tagIds);
this.onTouch();
if (!addValue) { return }
this.tageLoading = true;
const res = await firstValueFrom(
this.http.post(`/taginfo/add`, { type: this.tagType, name: addValue[0] }),
);
if (this.apiService.isSuccess(res)) {
this.tagIds = isExistValue ? [...isExistValue] : [];
this.getTagList(addValue[0]);
} else {
this.tagIds = isExistValue ? [...isExistValue] : [];
console.log(res.message)
}
this.tageLoading = false;
}
async deleteClick(option) {
const res = await firstValueFrom(
this.http.post(
`/taginfo/delete`,
{},
{ type: this.tagType, id: option.id },
),
);
if (res.code === 200) {
this.getTagList();
this.tagIds = this.tagIds.filter((i) => i !== option.id);
console.log('删除成功');
} else {
console.log(res.message)
}
}
getTagListByTagIds(tagIds, tagList) {
const arr = (tagIds ?? []).map((i) => {
return tagList.find((j) => j.id === i) ?? i;
});
return arr;
}
private onChange: (value: any) => void;
private onTouch: () => void;
writeValue(value: any): void {
this.tagIds = value;
}
registerOnChange(fn: (value: any) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void {
this.onTouch = fn;
}
setDisabledState?(isDisabled: boolean): void {
}
}