郑州做供暖的公司网站,做网站的怎么认证微博,怎么样做网站赚钱吗,学习网站模板在 Angular 应用中#xff0c;我们有两种方式来实现表单绑定——“模板驱动表单”与“响应式表单”。这两种方式通常能够很好的处理大部分的情况#xff0c;但是对于一些特殊的表单控件#xff0c;例如input[typedatetime]、input[typefile]#xff0c;我们需要重写默认的表… 在 Angular 应用中我们有两种方式来实现表单绑定——“模板驱动表单”与“响应式表单”。这两种方式通常能够很好的处理大部分的情况但是对于一些特殊的表单控件例如input[typedatetime]、input[typefile]我们需要重写默认的表单绑定方式让我们绑定的变量不再仅仅只是一个字符串而是一个 Date 或者 File 对象。为了达成这一目的我们需要自定义表单控件的 ControlValueAccessor。 ControlValueAccessor 接口是 Angular Forms API 与 DOM 之间的桥梁通过提供不同的 ControlValueAccessor我们就可以使用统一的 Angular Forms API 来操作不同的 HTML 表单元素。 在我们使用 ngModel 或者 formControl 的时候这两个 Directive 会向 Angular 的依赖注入容器申请实现了 ControlValueAccessor 接口的对象这是一种典型的面向接口编程的设计。例如如果我们需要为 input[typefile] 提供一个用来绑定 File 对象的 ControlValueAccessor只需要在依赖注入容器中提供一个 FileControlValueAccessor 的实现就可以了。不过我们并不想覆盖其他类型 input 元素的 ControlValueAccessor因为那样肯定会对已有代码造成大范围的破坏。所以在这里我们需要使用 Angular 的分层注入能力——在 ElementInjector 中提供 FileControlValueAccessor。关于 ElementInjector 更多的内容请看这里 a-curios-case-of-the-host-decorator-and-element-injectors-in-angular。 下面演示的两个 Directive 您都可以在这里查看在线演示。 首先让我们来创建一个 Directive这个指令将会选中 input[typefile][appInputFile] 元素这样我们就可以有选择的为文件选择器的 ElementInjector 定义新的 Provider。 Directive({selector: input[typefile][inputFile], // 1providers: [{provide: NG_VALUE_ACCESSOR, // 2useExisting: forwardRef(() InputFileDirective), // 3multi: true // 4}]
})
export class InputFileDirective implements ControlValueAccessor, OnInit, OnDestroy {// 当文件选择器选择的文件发生改变时调用的回调函数onChange: (any) any;// 当文件选择器选择的被操作后调用的回调函数onTouched: () any;// 监听宿主元素的 change 事件HostListener(change, [$event.target.files]) onElChange (files: FileList) {this.onChange(files);};// 监听宿主元素的 blur 事件HostListener(blur, []) onElTouched () {this.onTouched();};constructor(private el: ElementRefHTMLInputElement) { // 5}ngOnInit(): void {this.el.nativeElement.addEventListener(change, this.listener);}// 来自 ControlValueAccessor 接口用来设置元素的值writeValue(obj: any): void {this.el.nativeElement.value obj;}// 来自 ControlValueAccessor 接口用来将一个函数注册为 onChange 回调函数registerOnChange(fn: any): void {this.onChange fn;}// 来自 ControlValueAccessor 接口用来将一个函数注册为 onTouched 回调函数registerOnTouched(fn: any): void {this.onTouched fn;}// 来自 ControlValueAccessor 接口设置表单元素是否启用setDisabledState?(isDisabled: boolean): void {this.el.nativeElement.disabled isDisabled;}} 上面的代码片段中你可以看到有几处类似 // 1 的注释这是我用来在下面的文章中引用该行代码的标记语法借鉴自 ASCIIDoc 通过定义一个复合的选择器我们可以有选择的对 input[typefile] 重写 ControlValueAccessorControlValueAccessor 的注入 token 是一个常量 —— NG_VALUE_ACCESSOR由于 Directive 的定义在这行代码的下面所以需要使用 forwardRef 来引用这个依赖的实现。这里需要将 multiple 设置为 true因为 Angular 默认的 ControlValueAccessor 就是提供了多个实现的。在解析依赖的时候Angular 会优先选择我们自定义的实现。为了代码更加简单我在这里选择了不利于服务端渲染的 ElementRef.nativeElement 来读取原生 HTML 元素的属性如果你对服务端渲染有需求你应该使用 Renderer2 来读写元素的属性。有了这个 Directive我们就可以在 Angular Forms 中绑定 File 对象了 input typefile [(ngModel)]foo.files inputFile / Date 类型的数据也是日常开发中比较头疼的一个地方因为在 JSON 中Date 类型往往会被序列化为字符串而在前端代码中我们又需要将其反序列化为 Date 对象最终在页面上展示的时候我们又需要按照产品需求再将其序列化为制定格式的字符串。现在有了 ControlValueAccessor 的帮助我们就可以实现让 input[typedatetime] 与 Date 对象进行双向绑定的功能同时还能够定制 Date 对象在输入框中的显示格式。 Directive({// tslint:disable-next-line:directive-selectorselector: input[typedatetime][valueAsDate],providers: [{provide: NG_VALUE_ACCESSOR,useExisting: forwardRef(() DateValueDirective),multi: true}]
})
export class DateValueDirective implements ControlValueAccessor {/*** See https://date-fns.org/v2.0.0-alpha.25/docs/format* 自定义日期展示格式* type {string}* memberof DateValueDirective*/// tslint:disable-next-line:no-input-renameInput(valueAsDate) format: string;private dateValue: Date;HostListener(input, [$event.target.value]) onChange (_: any) { };HostListener(blur, []) onTouched () { };get element() { return this.elementRef.nativeElement; }constructor(private elementRef: ElementRef,private renderer: Renderer2 // 1) { }parseDate(str: string) {return parseDate(str, this.format, new Date(), { awareOfUnicodeTokens: true });}formatDate(date: Date) {return formatDate(date, this.format, { awareOfUnicodeTokens: true });}/*** 设置组件的值的时候先把新的值存到一个成员变量中然后再把新的值格式化为 string*/writeValue(date: Date): void {this.dateValue date;this.renderer.setProperty(this.element, value, this.formatDate(date));}/*** 在 input 元素值发生变化的时候先尝试把变化后的值转换成 Date 对象* 如果转换失败那么依然使用之前的值* 否则将新的值传递给回调函数*/registerOnChange(fn: any): void {const onChange (value: string) {const date this.parseDate(value);if (isValidDate(date)) {this.dateValue date;fn(date);} else {fn(this.dateValue);}};this.onChange onChange;}registerOnTouched(fn: any): void {this.onTouched fn;}setDisabledState?(isDisabled: boolean): void {this.renderer.setProperty(this.element, disabled, isDisabled);}
} 这里演示了使用 Renderer2 来读写元素属性的操作整个指令的内容仍然非常简单但是却能够为我们的日常开发带来不小的便利使用了这个指令后我们就可以非常容易的为 Date 对象进行双向绑定。 input typedatetime valueAsDateM/d/yyyy h:mm:ss a [(ngModel)]foo.date 转载于:https://www.cnblogs.com/JacZhu/p/10087447.html