Angular 2,自定义控件永远不会被标记为脏

Ari*_*ael 7 angular2-forms angular2-template angular

我创建了一个自定义控件,我以编程方式为其提供值,因为它是一个基于cropper.js 的图像编辑器,用户通过普通文件输入“上传”图像,然后我使用文件 API 将图像分配给我的自定义控件.

这是代码(为了可读性,我删除了一些cropper.js特定的代码)

import {
   Component,
   OnInit,
   ViewChild,
   ElementRef,
   ViewEncapsulation,
   forwardRef
} from '@angular/core';

import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
import * as Cropper from 'cropperjs';
import { Photo } from '../entities/photo';

@Component({
   selector: 'image-edit',
   templateUrl: 'image-edit.component.html',
   styleUrls: [
       'image-edit.component.scss',
       '../../../node_modules/cropperjs/dist/cropper.min.css'
],
encapsulation: ViewEncapsulation.None,
providers: [
    {
        provide: NG_VALUE_ACCESSOR,
        useExisting: forwardRef(() => ImageEditComponent),
        multi: true
    }
   ]
})

export class ImageEditComponent implements OnInit, ControlValueAccessor {

    get value() {
        return this.cropperImage.nativeElement.src;
    }

    set value(v: string) {
        this.cropperImage.nativeElement.src = v;
    }

    public onTouch: () => void;
    public onChange: (_: any) => void;

    @ViewChild('cropperImage')
    private cropperImage: ElementRef;

    private isDisabled: boolean;

    // cropper.js
    private cropper: Cropper;
    private cropperOptions: Cropper.CropperOptions = {};

    public ngOnInit(): void {

        // init cropper.js
    }

    public writeValue(obj: any): void {

        if (obj) {
            this.value = obj;
            this.initializeCropperOrReplaceImage();
        }
    }

    public registerOnChange(fn: any): void {
        this.onChange = fn;
    }

    public registerOnTouched(fn: any): void {
        this.onTouch = fn;
    }

    public setDisabledState(isDisabled: boolean): void {
       this.isDisabled = isDisabled;
    }

    private initializeCropperOrReplaceImage(): void {

        if (!this.isCropperInitialized) {
            this.cropper = new Cropper(this.cropperImage.nativeElement, this.cropperOptions);
            this.isCropperInitialized = true;
        }
        else {
            this.cropper.replace(this.cropperImage.nativeElement.src);
        }

        URL.revokeObjectURL(this.cropperImage.nativeElement.src);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是模板

<image-edit [hidden]="!photo.data" #photoData="ngModel" name="photoData" [(ngModel)]="photo.data" validateMinImageDimensions minWidth="300" minHeight="400">
 </image-edit>
Run Code Online (Sandbox Code Playgroud)

不幸的是,我的控制从来没有脏过或接触过。我试图扩展 AbstractForm 并自己设置脏,但它没有用。它仍然不脏,没有受到影响。

任何想法我做错了什么?

编辑

我删除了对onTouchonChangein的调用,writeValue因为根据@n00dl3 的回答和评论(参见那里),这不是一个好的解决方案。

n00*_*dl3 7

您永远不会调用this.onTouch()您的组件,因此您的输入不能被标记为未触碰或脏....

所以澄清一下,this.onTouch()当您认为数据已被用户(而不是模型)更改时,您需要手动调用,因此您应该避免onTouchwriteValue方法内部调用。

相反,您应该监听您的用户在处理(不一定更改)您正在处理的值(可能只是在关注常规input元素之后)时可能触发的最终事件。

我想有一种方法可以知道用户在初始化选项中Cropper像传递complete回调一样裁剪了库中的图像。您需要onTouch从此回调中调用该方法,或者在您认为应将输入标记为已触摸时调用该方法(可能单击图像,甚至只是将其悬停)。