nz-select表单组件封装--实现增加自定下拉项,并进行新增、删除操作

141 阅读2分钟
问题拆解
  • 如何封装表单控件?
    • 通过onChange(官方api),将新的值通知给父表单控件或模型更新
    • onTouch 用于标记表单控件已被触摸(即用户曾与控件交互过),通常用于设置控件的 touched 状态
  • 如何自定义下拉项,增加新增、删除功能?
    • 官方组件有提供api,这个暂时不展开了
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); $event.stopPropagation()"
            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 {
    // Implement if your component needs to support disabled state
  }
}