0

hello I want to validate an input file html type image just accept 'png', 'jpg', 'jpeg', 'gif' and not be greater than 2mb until I can make it possible only that it does not take me this.firstFormGroup.controls.image .status if you enter any other file is activated as valid Status.

 handleFileInput(event) {

  const file = event.target.files[0];
  const typeFile = file.type.split('/');
  const filemb: number = file.size / 1000000;
  if (filemb <= 2 && (typeFile[1] === 'png' || typeFile[1] === 'jpg' || typeFile[1] === 'jpeg' || typeFile[1] === 'gif') ) {
        const filePath = `${this.rutaImg}/${file.name}`;
        const fileRef = this.storage.ref(filePath);
        const task = this.storage.upload(filePath, file);
        this.uploadPercent = task.percentageChanges();
        task.snapshotChanges().pipe(
            finalize(() => {
              this.downloadURL = fileRef.getDownloadURL();
              this.downloadURL.subscribe( url => {
                if ( url ) {
                  this.urlImg = url;
                  console.log(this.urlImg);
                  this.validateImage = true;
                  console.log(this.validateImage );
                  return {
                    isError: this.validateImage
                };
                }
             });
            })
         )
        .subscribe();

  } else {
    this.validateImage = false;
  }
}

html code

    <div>
        <input formControlName="image"  (change)="handleFileInput($event)" type="file"  id="file" class="inputfile" accept="image/*"  required/>
        <label for="file"><mdb-icon icon="camera"></mdb-icon> Insertar Imagen &nbsp;
          <progress  style="margin-top: -10px; " *ngIf="uploadPercent" max="100" [value]="(uploadPercent | async)"></progress>
       </label>
 </div>

FormGroup

this.firstFormGroup = this.fb.group({
      ruc: ['', rucValidator],
      razon_social: ['', nameValidator],
      nameBussines: ['', nameValidator],
      phone: ['', phoneValidator],
      describe: ['', describeValidator],
      plan: ['', Validators.required],
      category: ['', Validators.required],
      image: ['', this.validateImage]
    });
Vision
  • 41
  • 1
  • 7
  • can you create a stackblitz demo? – KiraAG Jan 09 '19 at 04:26
  • @KiraAG hi I'm trying to make it work I can not adapt the library https://stackblitz.com/edit/angular-889iz4?file=src%2Fapp%2Fapp.component.ts – Vision Jan 09 '19 at 13:08
  • Actually on other note, your approach is valid only. What is the issue you are facing with this approach? – KiraAG Jan 10 '19 at 05:44
  • @KiraAG the validation is correct only that the this.firstFormGroup is not taking what the validation returns for it is always valid whatever it does in the function handleFileInput () – Vision Jan 10 '19 at 12:48

3 Answers3

1

You have to create a custom FormControl, I will show you how to create it(online example) or you can use the npm module (FileInputAccessor)


Steps for creating FormControl and upload the valid file

  1. Implements ControlValueAccessor
  2. Added custom validation rules
    • Register for custom validation rules
  3. Listen to change events in your reactive forms
    • Upload the valid file to your storage

1.Implements ControlValueAccessor

image-formcontrol.component

import { Component, OnInit, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { ImageFormcontrol } from './image-formcontrol';
@Component({
  selector: 'app-image-formcontrol',
  templateUrl: './image-formcontrol.component.html',
  styleUrls: ['./image-formcontrol.component.css'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => ImageFormcontrolComponent),
      multi: true
    }
  ]
})
export class ImageFormcontrolComponent implements OnInit, ControlValueAccessor {

  _registerOnChangeFn;
  _registerOnTouchedFn;

  constructor() { }

  ngOnInit() {
  }

  handleFileInput(event: Event) {

    const file = (event.target as HTMLInputElement).files[0];

    this.writeValue({
      file
    });
  }

  writeValue(value: ImageFormcontrol.Value) {
    if (this._registerOnTouchedFn) {
      this._registerOnTouchedFn();
    }

    if (this._registerOnChangeFn) {
      // update value and validity
      this._registerOnChangeFn(value);
    }
  }

  registerOnChange(fn) {
    this._registerOnChangeFn = fn;
  }

  registerOnTouched(fn) {
    this._registerOnTouchedFn = fn;
  }

}
<input (change)="handleFileInput($event)"  type="file"  id="file" class="inputfile" accept="image/*" />

2.Added custom validation rules

image-formcontrol-validator

  export function allowedTypes(pattern: string[]): ValidatorFn {
    return (control: AbstractControl): { [key: string]: any } | null => {
      const value = control.value as ImageFormcontrol.Value;

      const valid = null;
      const isUndefined = !value || !value.file;
      if (isUndefined) {
        return valid;
      }

      const allowed = new RegExp(`(${pattern.join('|')})`, 'ig').test(value.file.type);
      if (allowed) {
        return valid;
      } else {
        const invalid = { 'allowedTypes': { value: control.value } };
        return invalid;
      }
    };
  }

--

2-1.Register for custom validation rules

app.component ( you can change it to your component, just for demo purposes )

export class AppComponent {

  firstFormGroup: FormGroup;

  constructor(private fb: FormBuilder) { }

  ngOnInit() {
    const imageFormControl = new FormControl('', ImageFormcontrolValidator.allowedTypes(['jpeg']));

    this.firstFormGroup = this.fb.group({
      image: imageFormControl
    });
  }
}

3. Listen to change events in your reactive forms

  • Upload the valid file to your storage

export class AppComponent {

  firstFormGroup: FormGroup;

  constructor(private fb: FormBuilder) { }

  ngOnInit() {
    const imageFormControl = new FormControl('', ImageFormcontrolValidator.allowedTypes(['jpeg']));

    this.firstFormGroup = this.fb.group({
      image: imageFormControl
    });

    imageFormControl.statusChanges.subscribe((status) => {
      switch (status) {
        case 'VALID':
          // Upload the valid file to your storage
          const value = imageFormControl.value as ImageFormcontrol.Value;
          this.upload(value.file);
          break;
      }
    });
  }

  private upload(file: File) {
    console.log('upload', file);

    /* your custom logic

    const filePath = `${this.rutaImg}/${file.name}`;
        const fileRef = this.storage.ref(filePath);
        const task = this.storage.upload(filePath, file);
        this.uploadPercent = task.percentageChanges();
        task.snapshotChanges().pipe(
            finalize(() => {
              this.downloadURL = fileRef.getDownloadURL();
              this.downloadURL.subscribe( url => {
                if ( url ) {
                  this.urlImg = url;
                  console.log(this.urlImg);
                  this.validateImage = true;
                  console.log(this.validateImage );
                  return {
                    isError: this.validateImage
                };
                }
             });
            })
         )
        .subscribe();

      */
  }
}
Chunbin Li
  • 2,196
  • 1
  • 17
  • 31
  • hello thanks for the help, I'm trying to adapt it to my logic but I can not do it when loading the image `writeValue (value: ImageFormcontrol.Value)` `this._registerOnTouchedFn, this._registerOnChangeFn`, I do not understand its functionality, apart from it you never activate `validateImage.statusChanges.subscribe ((status)` – Vision Jan 14 '19 at 15:43
  • `registeronchange` function : [Registers a callback function that is called when the control's value changes in the UI](https://angular.io/api/forms/ControlValueAccessor#registeronchange) – Chunbin Li Jan 15 '19 at 01:35
  • You can fork my [example](https://stackblitz.com/edit/angular-mpmhu9?file=src%2Fformcontrol%2Fimage-formcontrol%2Fimage-formcontrol.component.ts). Add your logic to the example and describe what you want to do and what you tried and what problems you encountered,hope it can help you :) – Chunbin Li Jan 15 '19 at 01:37
0

I have a stackblitz that could help you or help anybody. Is not perfect, but after reading and research a lot. I make this only for validate file extensions.

https://angular-fileupload-custom-validator-reactive-form.stackblitz.io

I resume here:

  1. Create a Custom Validator for your FormGroup or Form Builder.

  2. Add it to your component and define array of allowed extension files.

  3. Return invalid or valid state to your input form.

0

Custom validation for required files for input type of file

requiredFileType.ts

import { AbstractControl, FormControl } from "@angular/forms";
export function requiredFileType(type: string[]) {
  return function(control: FormControl) {
    // return (control: AbstractControl): { [key: string]: any } | null => {
    const file = control.value;
    const valid = null;
    if (file) {
      // console.log(file);
      var path = file.replace(/^.*[\\\/]/, "");
      //   var el_down = path
      //     .split("\\")
      //     .pop()
      //     .split("/")
      //     .pop();

      const extension = path.split(".")[1].toUpperCase();
      console.log(extension + "extension" + type.length);
      var existValue: boolean = false;
      for (var i = 0; i < type.length; i++) {
        let typeFile = type[i].toUpperCase();
        if (typeFile === extension.toUpperCase()) {
          console.log("type" + typeFile);
          existValue = true;
        }
      }
      if (existValue == true) {
        return null;
      } else {
        return {
          requiredFileType: true
        };
      }
      return null;
    }
    return null;
  };
}

component.ts

 this.addSlot
        .get("FileUpload")
        .setValidators([
          Validators.required,
          requiredFileType(["jpg", "png", "txt"])
        ]);

html

<div class="col-sm-5">
          <input
            type="file"
            formControlName="FileUpload"
            [class.is-invalid]="FileUpload.invalid && uploadPanel"
            class="form-control"
            (click)="handleFileInput($event.target.files)"
            #inputTypeFile
          />

          <!-- <button onclick="document.getElementById('abc').click()">
            choose file
          </button> -->
          <div *ngIf="FileUpload.invalid && uploadPanel">
            <small *ngIf="FileUpload.errors?.required" class="text-danger"
              >File is required</small
            >
            <small
              *ngIf="FileUpload.errors?.requiredFileType"
              class="text-danger"
              >File is required</small
            >
          </div>
        </div>
Mayank Sudden
  • 205
  • 1
  • 5
  • 11