How to include a file upload control in an Angular2 reactive form?
Asked Answered
U

3

28

For some weird reason, there are just no tutorials or code samples online showing how to use Angular2 Reactive forms with anything more than simple input or select dropdowns.

I need to create a form to let users select their avatar. (Image file)

The following doesn't work. (i.e. The Avatar property never shows any value changes.)

profile.component.html:

               <form [formGroup]="profileForm" novalidate>
                 
                        <div class="row">
                            <div class="col-md-4 ">
                                <img src="{{imgUrl}}uploads/avatars/{{getUserAvatar}}" style="width:150px; height:150px;float:left;border-radius:50%;margin-right:25px;margin-left:10px;">

                                <div class="form-group">
                                    <label>Update Profile Image</label>
                                    <input class="form-control" type="file" formControlName="avatar">
                                </div>
                            </div>
                            <div class="col-md-8 ">
                                <div class="form-group">
                                    <label >Firstname:
                                        <input class="form-control" formControlName="firstname">
                                    </label>
                                </div>
                                <div class="form-group">
                                    <label >Lastname:
                                        <input class="form-control" formControlName="lastname">
                                    </label>
                                </div>
                                <div class="form-group">
                                    <label >Email:
                                        <input class="form-control" formControlName="email">
                                    </label>
                                </div>
                                <div class="form-group">
                                    <label >Password:
                                        <input class="form-control" type="password" formControlName="password">
                                    </label>
                                </div>
                            </div>
                        </div>
                 
                </form>
                <p>Form value: {{ profileForm.value | json }}</p>
                <p>Form status: {{ profileForm.status | json }}</p>

profile.component.ts:

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup,  Validators } from '@angular/forms';
import {Config} from '../../services/config.service';
import {AuthService} from '../../services/auth.service';
import {User} from '../../models/user.model';

@Component({
  selector: 'app-profile',
  templateUrl: './profile.component.html',
  styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
  
  authUser:User;

  profileForm : FormGroup; 

  constructor(private authService:AuthService, private fb: FormBuilder) {}
          
  createForm() {
    this.profileForm = this.fb.group({
      firstname:  [this.authUser.firstname, Validators.required ],
      lastname: [this.authUser.lastname, Validators.required ],
      email: [this.authUser.email, Validators.required ],
      avatar: [this.authUser.avatar, Validators.required ],
      password:['xxxxxx', Validators.minLength(4)] 
    });
  }
  ngOnInit() {
    this.authUser = this.authService.getAuthUser();

    this.createForm();
  } 
Unknown answered 17/4, 2017 at 2:45 Comment(1)
Are you all the other values?Pyretotherapy
U
14

Simple answer can be found here. https://devblog.dymel.pl/2016/09/02/upload-file-image-angular2-aspnetcore/

The HTML

    <input #fileInput type="file"/>
    <button (click)="addFile()">Add</button>

Component.ts

@ViewChild("fileInput") fileInput;

addFile(): void {
let fi = this.fileInput.nativeElement;
if (fi.files && fi.files[0]) {
    let fileToUpload = fi.files[0];
    this.uploadService
        .upload(fileToUpload)
        .subscribe(res => {
            console.log(res);
        });
    }
}

The service.ts

upload(fileToUpload: any) {
    let input = new FormData();
    input.append("file", fileToUpload);

    return this.http.post("/api/uploadFile", input);
}
Unknown answered 18/4, 2017 at 20:15 Comment(6)
It works, but it would be nice if the input could participate in the form validation mechanism.Horologe
why you need to put an input type file and a button to add file , the input file itself gives you a button to click on it and add fileVasquez
@JoeSleiman the question is for reactive form. So there are probably other fields and not only the file upload field. In this case is better to validate the form on submit, instead on client side upload, because the <input is not part of the reactive form and cannot be validated as required. At least this is my logic.Trouper
Agreed. This answer circumvents the whole Reactive From concept. With reactive, you should not be accessing the template from the component (ie @ViewChild)Misbegotten
looks like there isn't (and isn't going to be) any support for <input type="file"> in Angular: github.com/angular/angular.io/issues/3466 Odd...Skit
@JoeSleiman Why should the image be sent (posted) separately and not within the form. I'm trying to post the whole form along with the two images. This way the processing (on the server) would be harder.Brittneybrittni
A
11

I'm a bit late to this, but for anyone else that may come here looking for the same solution - This is my file input accessor that can be used with Reactive or Template-driven forms. Demo here.

There's some optional validation provided with it that can be used to check image dimensions and file size, extension, type, which is disabled by default.

npm i file-input-accessor and add the module to your AppModule imports:

import {BrowserModule} from '@angular/platform-browser';
import {FileInputAccessorModule} from "file-input-accessor";

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        FileInputAccessorModule
    ],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule {}

Then use your file input like any other input:

<!--Reactive Forms-->
<input type="file" multiple [formControl]="someFileControl" />
<input type="file" multiple formControlName="someFileControl" />

<!--Template-driven-->
<input type="file" name="file-input" multiple [(ngModel)]="fileList" />

You can subscribe to valueChanges property just like any other reactive control.

Astylar answered 18/1, 2018 at 14:7 Comment(1)
I'd like to see how this could be used with an actual file upload to the server. Currently looking through this to see how I could accomplish that.Septum
A
6

You can use the below method to upload image in any type of form.

Expose one change method to your control.

<input class="form-control" type="file" name="avatar" (change)="imageUpload($event)">
<img [src]="imageUrl" />

Add below logic in your class.

 // Declare the variable. 
  imageUrl: any;

   //method definition in your class 
    imageUpload(e) {
        let reader = new FileReader();
        //get the selected file from event
        let file = e.target.files[0];
        reader.onloadend = () => {
          //Assign the result to variable for setting the src of image element
          this.imageUrl = reader.result;
        }
        reader.readAsDataURL(file);
      }
    }

Once the image is uploaded you can use the this.imageUrl to update your form model. For uploading the image or file to server you can take the reference from the below link.

How to upload file in Angular2

Let me know if this solution is working for you.

Artwork answered 17/4, 2017 at 11:16 Comment(4)
Have been trying to make this work for 2 days now and i can confirm that it can't... Http can now send files but this is not the way to do it. (See devblog.dymel.pl/2016/09/02/…)Unknown
how to validate input type file for 2 mb for example i won't let the user to click submit if the file more than 2mb . can you please give me an exampleVasquez
@JoeSleiman You can have a check before assigning the reader.result to imageUrl whether the file.size > 1048576*2 If yes then assign the reader.result else show relevant error message to user.Artwork
@RahulRai please refer to this link to know my issue : #43782498Vasquez

© 2022 - 2024 — McMap. All rights reserved.