ng-zorro-antd icon indicating copy to clipboard operation
ng-zorro-antd copied to clipboard

为 nz-upload 实现 ControlValueAccessor

Open s97712 opened this issue 5 years ago • 5 comments

What problem does this feature solve?

nz-upload 也属于表单控件吧,是否考虑为nz-upload实现ControlValueAccessor

What does the proposed API look like?

<nz-upload [(ngModel)]="file" required>

s97712 avatar Mar 09 '19 08:03 s97712

Translation of this issue:


Implementing ControlValueAccessor for nz-upload

What problem does this feature solve?

Nz-upload is also a form control, is it considered to implement ControlValueAccessor for nz-upload?

What does the proposed API look like?

<nz-upload [(ngModel)]="file" required>

ng-zorro-bot avatar Mar 09 '19 08:03 ng-zorro-bot

is there any progress?

hiwjd avatar Apr 22 '20 13:04 hiwjd

is there any update? How can I make nz-upload as a formControl field?

mengyaolin avatar Aug 18 '20 03:08 mengyaolin

@mengyaolin, You can use the nzChange event, like this:

<nz-upload (nzChange)="uploadChange($event)"></nz-upload>
uploadChange(ev: NzUploadChangeParam): void {
  if (ev.type === 'success') {
    this.form.patchValue({ file: ev.file.response.url })
  }
}

cipchk avatar Aug 20 '20 03:08 cipchk

Angular 的组件是很容易通过指令来扩展原有功能的 @mengyaolin

由于 upload 组件的值比较复杂,不建议在原有的组件上直接实现表单能力,推荐使用单独的指令来实现表单能力

这里提供一个单文件上传的表单例子(这个场景比较简单,方便理解),同理根据需要实现多文件上传的表单

用法

<nz-upload nzx [(ngModel)]="url"></nz-upload>

实现

import {
  Directive,
  forwardRef,
  Input,
  OnDestroy,
  OnInit,
  Self,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { NzUploadComponent } from 'ng-zorro-antd/upload';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

@Directive({
  // eslint-disable-next-line @angular-eslint/directive-selector
  selector: 'nz-upload[nzx]',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => NzUploadExtensionDirective),
      multi: true,
    },
  ],
})
export class NzUploadExtensionDirective
  implements OnInit, ControlValueAccessor, OnDestroy
{
  @Input()
  defaultFileName?: string;

  private destroy$$ = new Subject<void>();

  onChange?: (v?: string) => void;

  constructor(@Self() private uploadRef: NzUploadComponent) {}

  ngOnInit(): void {
    this.uploadRef.nzChange
      .pipe(takeUntil(this.destroy$$))
      .subscribe((info) => {
        if (info.type === 'removed') {
          this.onChange?.();

          return;
        }

        if (info.file.status === 'done') {
          this.onChange?.(info.file.response.url);
        }
      });
  }

  ngOnDestroy(): void {
    this.destroy$$.next();
    this.destroy$$.complete();
  }
  registerOnTouched(): void {
    /* 不关心 */
  }

  registerOnChange(fn: (v?: string) => void): void {
    this.onChange = fn;
  }

  writeValue(v: string): void {
    if (v) {
      this.uploadRef.nzFileList = [
        {
          uid: v,
          name: this.defaultFileName ?? v,
          status: 'done',
          url: v,
        },
      ];
    } else {
      this.uploadRef.nzFileList = [];
      this.uploadRef.ngOnChanges();
    }
  }

  setDisabledState(isDisabled: boolean): void {
    this.uploadRef.nzDisabled = isDisabled;
    this.uploadRef.ngOnChanges();
  }
}

maroon1 avatar Aug 01 '22 10:08 maroon1