浅析Angular19 自定义表单控件

下面我将为你详细讲解“浅析Angular19自定义表单控件”的完整攻略。如果您是Angular开发者,那么您一定知道表单是Web应用程序中至关重要的一部分。Angular提供了很多内置的表单控件,例如文本框、下拉框、单选框等。但是,在某些情况下,内置控件可能无法满足我们的需求。因此,我们需要自定义表单控件。下面是自定义表单控件的完整攻略:

1. 创建自定义表单控件

创建自定义表单控件需要创建一个组件,并实现controlvalueaccessor接口。接口是一个用于控制表单控件值的方法集合。

控件本身应该继承AbstractControl类以实现表单校验。使用@HostBinding绑定DOM元素。具体的示例如下:

import { Component, Input, Output, forwardRef, EventEmitter, OnInit, 
HostBinding } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, AbstractControl } 
from '@angular/forms';

@Component({
  selector: 'app-custom-control',
  template: '<input [(ngModel)]="value">',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CustomControlComponent),
      multi: true
    }
  ]
})
export class CustomControlComponent implements ControlValueAccessor {
  @Input() placeholder: string;
  @Output() customValueChange = new EventEmitter();
  value: any;

  onTouched: () => void = () => { };
  onChange: (_: any) => void = () => { };

  writeValue(value: any) {
    this.value = value
  }
  registerOnChange(fn: any) {
    this.onChange = fn;
  }
  registerOnTouched(fn: any) {
    this.onTouched = fn;
  }

  onValueChange() {
    this.customValueChange.emit(this.value);
    this.onChange(this.value);
    this.onTouched();
  }

  setDisabledState(isDisabled: boolean) {
    // 写你自己的代码
  }
}

上面代码中,CustomControlComponent 组件是一个基本的输入框,它通过实现ControlValueAccessor接口来控制表单控件的值,通过监听输入框的变化,通知 onChange,onTouched两个回调函数。注意控件值的读取和写入都需要手动实现。

在父组件中,您可以像使用内置表单控件一样使用自定义表单控件:

<app-custom-control [(ngModel)]="value"></app-custom-control>

2. 自定义表单控件实现表单验证

在 Angular 中,表单验证由 Validators 类提供的一些内置验证器完成。同样地,我们也可以自定义表单验证器。

下面是一个自定义表单验证器的示例。该验证器将最小长度设置为4。

import { AbstractControl } from '@angular/forms';

export function minLengthValidator(minLength: number) {
  return (control: AbstractControl) => {
    const value = control.value;
    return value && value.length >= minLength ? null : {
      mininumLength: {
        expectedLength: minLength,
        actualLength: value ? value.length : 0
      }
    };
  };
}

上面的自定义表单验证器接受一个最小长度值作为参数,返回一个验证函数。valid()方法返回null表示验证通过,否则返回一个包含验证失败信息的对象。您可以像下面这样使用自定义表单验证器:

form = this.fb.group({
  customControl: ['', [minLengthValidator(4)]]
});

示例1:自定义单选框控件

如下是一个可以用来展示生肖列表的自定义单选框控件示例:

import { Component, Input, Output, forwardRef, EventEmitter, OnInit, HostBinding } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, AbstractControl } from '@angular/forms';

@Component({
  selector: 'app-custom-checkbox',
  template: `
    <div *ngFor="let item of data">
        <input 
          type="radio"
          [checked]="item.value === value" 
          (blur)="onTouched()"
          [name]="name"
          [value]="item.value"
          (change)="writeValue(item.value)">
          {{item.text}}
    </div>
    `,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CustomRadioComponent),
      multi: true
    }
  ]
})
export class CustomRadioComponent implements ControlValueAccessor {
  @Input() data: any[];
  @Input() name: string;
  @Output() customValueChange = new EventEmitter();
  value: any;

  onTouched: () => void = () => {};
  onChange: (_: any) => void = () => {};

  writeValue(value: any) {
    this.value = value;
    this.customValueChange.emit(this.value);
    this.onTouched();
  }

  registerOnChange(fn: any) {
    this.onChange = fn;
  }
  registerOnTouched(fn: any) {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean) {
    // 写你自己的代码
  }
}

上述代码中,我们使用了两个模板变量:data 和 name,其中 data 表示一个数组,包含每个单选项的值和显示文本。name 表示单选框的名称。每个单选项都会生成一个单选框元素,并绑定各种属性。

示例2:自定义上传文件组件

下面是一个自定义的上传文件组件,该组件可以允许用户上传多个文件并在上传时展示进度条:

import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core';

@Component({
  selector: 'app-file-upload',
  templateUrl: './file-upload.component.html',
  styleUrls: ['./file-upload.component.scss']
})
export class FileUploadComponent implements OnInit {

  @Input() files: any = [];
  @Input() multiple: boolean = false;
  @Input() label: string = '选择文件';
  @Input() uploadUrl: string = '';
  @Input() uploadFieldName: string = 'file';
  @Input() maxSize?: number = null;
  @Input() accept: string = '*';
  @Output() done: EventEmitter<any> = new EventEmitter<any>();
  @Output() progress: EventEmitter<number> = new EventEmitter<number>();
  @Output() error: EventEmitter<any> = new EventEmitter<any>();
  @Output() removed: EventEmitter<any> = new EventEmitter<any>();
  @Output() added: EventEmitter<any> = new EventEmitter<any>();

  pendingFiles: any[] = [];

  ngOnInit(): void {
  }

  onChange(event) {
    this.pendingFiles = Array.from(event.target.files || []);
    for (let i = 0; i < this.pendingFiles.length; i++) {
      if (this.validate(this.pendingFiles[i])) {
        this.added.emit(this.pendingFiles[i]);
      } else {
        this.pendingFiles.splice(i, 1);
        i--;
      }
    }
  }

  upload() {
    const files = this.pendingFiles.slice(0);
    this.pendingFiles = [];
    for (let i = 0; i < files.length; i++) {
      const file = files[i];
      const xhr = new XMLHttpRequest();
      const formData = new FormData()
      formData.append(this.uploadFieldName, file, file.name)
      xhr.onload = () => {
        if (xhr.status === 200) {
          this.done.emit(xhr.responseText);
        } else {
          this.error.emit(xhr.responseText);
        }
        const progress = ((i + 1) / files.length) * 100;
        this.progress.emit(progress);
      }
      xhr.onerror = () => {
        this.error.emit(xhr.responseText);
      }
      xhr.upload.onprogress = (e) => {
        const progress = ((e.loaded / e.total) * 100);
        this.progress.emit(progress);
      }
      xhr.upload.onabort = () => {
        this.removed.emit(file);
      }
      xhr.open('POST', this.uploadUrl, true)
      xhr.send(formData)
    }
  }

  remove(file) {
    const index = this.pendingFiles.indexOf(file);
    if (index !== -1) {
      this.pendingFiles.splice(index, 1);
      this.removed.emit(file);
    }
  }

  validate(file): boolean {
    if (this.maxSize && file.size / 1024 > this.maxSize) {
      this.error.emit('文件超出最大大小');
      return false;
    }
    if (this.accept !== '*' && !new RegExp(this.accept).test(file.type)) {
      this.error.emit('文件类型不允许');
      return false;
    }
    return true;
  }
}

上述代码中,我们使用了一个文件选择器元素来允许用户选择文件。当文件选择器的值发生变化时,我们将选中的文件保存在数组变量pendingFiles中,并校验文件类型和大小。然后我们根据服务器API将文件上传到服务端,并在上传完成后通知使用此组件的父组件以显示上传进度。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:浅析Angular19 自定义表单控件 - Python技术站

(0)
上一篇 2023年6月25日
下一篇 2023年6月25日

相关文章

  • Java Web开发防止多用户重复登录的完美解决方案

    Java Web开发防止多用户重复登录的完美解决方案 在 Java Web 开发中,通常需要考虑如何防止多用户重复登录的问题。为了避免这种情况的发生,我们可以采用以下方法来解决。 1. 使用 Session 实现用户登录控制 Session 是 Web 应用程序中的一种状态管理技术,用于在服务器端存储用户会话数据。通过使用 Session,我们可以轻松实现用…

    other 2023年6月26日
    00
  • 【mybatis】mybatis中update更新操作 null字段不更新 有…

    【mybatis】mybatis中update更新操作 null字段不更新 有哪些解决方案? 在Mybatis中,我们经常需要使用update语句来更新数据库中的数据,但是在数据更新时,有时候我们并不想更新所有字段,比如某些字段的值为null,我们不希望将null值更新到数据库中。那么,在Mybatis中,该如何实现null字段不更新的功能呢?本文将为大家介…

    其他 2023年3月28日
    00
  • fastframework快速开发框架

    以下是详细讲解“fastframework快速开发框架的完整攻略”的标准Markdown格式文本: fastframework快速开发框架的完整攻略 fastframework是一个基于Java语言的快速开发框架,可以帮助开发人员快速构建Web应用程序。本文将介绍fastframework的基本概念、使用方法和两个示例说明。 1. fastframework…

    other 2023年5月10日
    00
  • 浅析java 的 static 关键字用法

    当我们在使用Java语言时,不可避免地会遇到static关键字,它可以用来修饰变量、方法、代码块和内部类,使用得当可以起到很好的作用。接下来,就带您深入了解static关键字的用法吧! 什么是static? 在Java里,static是一个关键字,它表明一个成员变量、方法或属性是静态的。 static关键字的用法 1. 静态变量 静态变量在类被加载时就已经分…

    other 2023年6月27日
    00
  • SharePoint 2007图文开发教程(1) 简介,安装,配置及创建Web应用程序

    SharePoint 2007图文开发教程(1) 简介,安装,配置及创建Web应用程序 简介 本教程将详细介绍如何使用SharePoint 2007进行图文开发,包括环境配置、Web应用程序的创建和基本的开发操作等内容。 安装 下载并安装SharePoint 2007软件包; 安装IIS和ASP.NET相关组件; 安装Microsoft SQL Server…

    other 2023年6月25日
    00
  • win10中怎么修改IP地址?win10重新设置IP

    当你需要在Windows 10中修改IP地址或重新设置IP时,可以按照以下步骤进行操作: 打开网络和Internet设置:点击任务栏上的网络图标,然后选择“网络和Internet设置”。 进入网络设置:在“网络和Internet设置”窗口中,点击左侧的“更改适配器选项”。 打开网络连接属性:在“更改适配器选项”窗口中,找到你要修改IP地址的网络连接,右键点击…

    other 2023年7月30日
    00
  • Java内部类的全限定名规律代码示例

    当我们在Java中定义了一个内部类时,它的全限定名是由外部类的全限定名和内部类的名称组成的,中间使用一个美元符号”$”分隔。下面是关于Java内部类全限定名规律的详细攻略,包含两个示例说明。 示例1:成员内部类的全限定名 // 外部类 package com.example; public class OuterClass { // 成员内部类 public…

    other 2023年6月28日
    00
  • 七猫免费小说怎么查看版本号?七猫免费小说查看版本号技巧

    七猫免费小说查看版本号攻略 七猫免费小说是一款受欢迎的小说阅读应用程序。如果你想查看七猫免费小说的版本号,可以按照以下步骤进行操作: 步骤一:打开七猫免费小说应用 首先,你需要打开七猫免费小说应用程序。你可以在手机的应用列表中找到它,并点击打开。 步骤二:进入设置页面 一旦你打开了七猫免费小说应用,你需要找到设置页面。通常,设置页面可以通过点击应用的主界面右…

    other 2023年8月3日
    00
合作推广
合作推广
分享本页
返回顶部