I created a custom control to which I provide the value programmatically because it an image-editor based on cropper.js and the user "uploads" an image via an normal fileinput and I then use the File API to assign the image to my custom control.
here's the code (I removed some cropper.js specific code for readability)
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);
}
}
here's the template
<image-edit [hidden]="!photo.data" #photoData="ngModel" name="photoData" [(ngModel)]="photo.data" validateMinImageDimensions minWidth="300" minHeight="400">
</image-edit>
Unfortunately my control is never dirty or touched. I tried to extend AbstractForm and set dirty myself but it didnt work. It remains undirty and untouched.
Any ideas what I'm doing wrong?
EDIT
I removed the call to onTouch
and onChange
in writeValue
because according to @n00dl3 answer and comment (see there) it is not a good solution.
You never call this.onTouch()
in your component, so your input cannot be marked as touched neither dirty....
So to clarify, you need to manually call this.onTouch()
when you consider the data has been changed by the user (not by the model), so you should avoid calling onTouch
inside the writeValue
method.
Instead you should listen to the eventual events that your user may trigger when dealing with (without necessarily changing) the value you are handling (maybe just after a focus on a regular input
element).
I guess there is a way to know that the user has cropped the image inside your Cropper
library like passing a complete
callback, in the initialization options. You need to call the onTouch
method from this callback or whenever you think your input should be marked as touched (maybe clicking on the image, or even just hovering it).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With