5

I have the code below:

import { Component, OnInit, ElementRef } from '@angular/core';
import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'

@Component({
  selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  showSort: boolean = false;
  form: FormGroup;
  name: FormControl;
  sortSelf: FormControl;
  sortItem: FormArray;
  locationItems: FormArray;

  constructor(private _fb: FormBuilder, private _elementRef: ElementRef ) {
  }

  ngOnInit(): void {
    this.sortItem = this._fb.array([this.initSort()]);

    this.form = this._fb.group({
      sortItem: this.sortItem
    });
  }

  initSort() {
    return this._fb.group({
      locationPicture: new FormControl('', []),
      locationItems: this._fb.array([
        this.initSortItems()
      ])
    })
  }

  initSortItems() {
    return this._fb.group({
      itemPicture: new FormControl('', [])
    })
  }

  addSort() {
    this.sortItem.push(this.initSort());
  }

  addSortLocationItem(i?: number, t?: number) {
    const control: FormArray = <FormArray> this.sortItem.at(i).get('locationItems');
    control.push(this.initSortItems());
  }

  showImage(event, level?: string, i?: number, t?: number){
    let file = event.target.files[0];
    if(level === 'locationPicture'){
      (<FormArray>this.sortItem.at(i)).controls['locationPicture'].setValue(file, {onlySelf: true});
    }else{
      (<FormArray>this.sortItem.at(i).get('locationItems')).at(t).get('itemPicture').setValue(file, {onlySelf: true});
    }
  }

  next(value, valid: boolean){
    console.log(`this is value ${JSON.stringify(value, null, 4)}`);
  }
 }

Mark Up:

<div class="col-md-8 col-md-offset-2">
  <form class="" action="index.html" method="post" [formGroup]="form" (ngSubmit)="next(form.value, form.valid)">
    <div class="form-group">

      <div formArrayName="sortItem" class="">
        <div class="top-buffer-small">
          <a (click)="addSort()" style="cursor: default">
            Add Sort Location +
          </a>
        </div>
        <div *ngFor="let sortLocation of sortItem.controls; let i=index">

            <div [formGroupName]="i" class="">
              <div class="form-group">
                  <label>Location Picture</label>
                  <input type="file" formControlName="locationPicture" class="form-control locationPicture" accept="image/*" capture="camera" (change)="showImage($event, 'locationPicture', i, t)">
                  <!-- <input type="file" formControlName="locationPicture" (change)="showImage($event)" /> -->
                  <img src="" alt="" class="location-image" width="80" height="80">
                  <small class="form-text text-muted" [hidden]="sortItem.controls[i].controls.locationPicture.valid">
                      Picture is required
                  </small>
              </div>

                <div formArrayName="locationItems" class="col-md-10 col-md-offset-1">
                  <div class="row">
                    <a (click)="addSortLocationItem(i,t)" style="cursor: default">
                      Add Item +
                    </a>
                  </div>
                  <!-- <div class="from-group" *ngFor="let eachItem of form.controls.sortItem.controls[i].controls.locationItems.controls; let t=index"> -->
                  <div class="form-group" *ngFor="let eachItem of sortLocation.get('locationItems').controls; let t=index">

                      <div [formGroupName]="t">
                        <div class="form-group">
                            <label>Item Picture</label>
                            <input type="file" formControlName="itemPicture" class="form-control itemPicture" capture="camera" (change)="showImage($event, 'itemPicture', i, t)">
                            <img src="" alt="" class="item-image" width="80" height="80">
                            <small class="form-text text-muted" [hidden]="sortItem.controls[i].controls.locationItems.controls[t].controls.itemPicture.valid">
                                Picture is required
                            </small>
                         </div>
                      </div>
                    </div>
                </div>
                <div class="clearfix"></div>
            </div>
        </div>
      </div>
    </div>
    <div class="text-center">
      <button type="submit" name="button" class="btn btn-primary" [disabled]="form.invalid">Submit</button>
    </div>
  </form>
</div>

I am trying to update the form within the showImage method by updating the formcontrol values. However, I cannot get the form to update with the picture. When I try to upload it on the 'locationPicture' and 'itemPicture', I get the following error:

Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.

Please any help with this will be great

2
  • I don't think you can set the value of an <input type="file"> because the value represents a local file on the user's filesystem (not just a string). Why do you want to set this value anyway? Commented Jan 28, 2017 at 16:05
  • ah that make sense now. iIwas doing a json stringify of my form on submit and i noticed my locationPicture and itemPicture where empty but I just read you cant see file objects using JSON stringify so I think I am good. Thanks. Commented Jan 28, 2017 at 16:09

1 Answer 1

1

I also got this problem, but we can deal with file and other form field separately.

This is my code, I want to post a FormData Object to Django REST API, this formData contains both strings and image, also with user_name and password. I created a component - upload-image-form

This is what I want to post, an Image class(I define it by myself)

// images.ts
export class Image {

    constructor(
        public id: number,
        public created: string,  // set by back end
        public userId: number,
        public fileUrl: string,
        public owner: string,
        public des?: string,
        public localImage?: File,
    ) { }

}

This is my component

// upload-image-form.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { NgForm } from '@angular/forms/forms';
import { FormControl, FormGroup, FormBuilder } from '@angular/forms';

import { Image } from '../image';
import { User } from '../user';
import { ImageService } from '../image.service';


@Component({
    selector: 'app-upload-image-form',
    templateUrl: './upload-image-form.component.html',
    styleUrls: ['./upload-image-form.component.css']
})
export class UploadImageFormComponent implements OnInit {

    @Input() user: User;  // this value comes from parent component user-detail's tempolate
    private file: File;
    private formData: FormData = new FormData();
    private imageForm: FormGroup;
    private submitted = false;
    private imageUrl = 'http://192.168.201.211:8024/images/';
    private password: string;

    constructor(
        private imageService: ImageService,
        private fb: FormBuilder,
    ) { }

    ngOnInit() {
        console.log(this.user);
        this.createFrom(this.user);  // create form here, so we can get this.user's value
    }

    createFrom(user: User) {
        // I didn't put file field in this form
        this.imageForm = this.fb.group({
            id: 1,
            created: '20170825',
            userId: user.id,
            fileUrl: 'http://images.fineartamerica.com/images-medium-large-5/mt-shuksan-picture-lake-dennis-romano.jpg',
            owner: user.username,
            des: '',
            pw: '',
        })
        console.log(user);
    }

    // https://stackoverflow.com/a/41465502/2803344
    // get a file object from form input
    onChange(event: EventTarget) {
        let eventObj: MSInputMethodContext = <MSInputMethodContext> event;
        let target: HTMLInputElement = <HTMLInputElement> eventObj.target;
        let files: FileList = target.files;
        this.file = files[0];
    }

    onSubmit() {
        // deal with string fields and file separately
        this.submitted = true;
        console.log(this.file);  // file is here, captured by onChange()
        console.log(this.imageForm.value);  // other fields are here, captured by formGroup

        this.formData.append('localImage', this.file, this.file.name);
        for (let item in this.imageForm.value) {
            console.log(item)
            if (item !== 'pw') {
                this.formData.append(item, this.imageForm.value[item]);
            }
            else {
                this.password = this.imageForm.value[item];
            }

        }

        // console.log('###here is the total form data');
        console.log(this.formData);
        console.log(this.formData.get('fileUrl'));
        console.log(this.user.username);
        this.imageService.post(this.formData, this.user.username, this.password)
                         .then(res =>{
                           console.log(res);
                         });
    }

    onClick(form: FormGroup) {
        form.reset({
            userId: this.user.id,
            owner: this.user.username,
            created: '20170825',
            fileUrl: 'http://www.fujifilm.com.sg/Products/digital_cameras/x/fujifilm_x_t1/sample_images/img/index/ff_x_t1_001.JPG',
        })
        this.submitted=false;
        console.log(form.value);
    }

}

This is my template

<!-- upload-image-form.component.html -->
<div [hidden]="submitted" *ngIf="user">

    <h2>Upload New Image</h2>

    <form [formGroup]="imageForm" (ngSubmit)="onSubmit()" >
        <div class="form-group">
            <label for="fileUrl">File Url</label>
            <input type="url" class="form-control" id="fileUrl" 
            formControlName="fileUrl" required>
            <div [hidden]="imageForm.get('fileUrl').valid || imageForm.get('fileUrl').pristine" 
                  class="alert alert-danger">
              File Url is required
            </div>
            <p>{{imageForm.get('fileUrl').valid | json}}</p>
            <p>{{imageForm.get('fileUrl').value | json}}</p>
        </div>
        <!-- upload an image 
        don't define this field in formGroup-->
        <div class="form-group">
            <label for="localImage">Local File</label>
            <input type="file" class="form-control" id="localImage"
            (change)="onChange($event)" accept=".jpg, .png" >
        </div>
        <div class="form-group">
            <label for="des">Description</label>
            <input type="text" class="form-control" id="des" 
            formControlName="des">
        </div>
        <div class="form-group">
            <label for="userId">User ID</label>
            <input type="text" class="form-control" id="userId" 
            formControlName="userId" readonly>
            <p>{{imageForm.get('userId').value | json}}</p>
        </div>
        <div class="form-group">
            <label for="owner">Owner</label>
            <input type="text" class="form-control" id="owner"
            formControlName="owner" readonly>
        </div>
        <!-- input user's password -->
        <div class="form-group">
            <label for="pw">password</label>
            <input type="password" class="form-control" id="pw"
            formControlName="pw" required>
        </div>
        <button type="submit" class="btn btn-success" [disabled]="!imageForm.valid">Submit</button>
    </form>
</div>

At last, this is my image service

// image.service.ts
import { Injectable } from '@angular/core';
import { Headers, Http, RequestOptions } from "@angular/http";

import 'rxjs/add/operator/toPromise';

import { Image } from "./image";
import { User } from './user';

@Injectable()
export class ImageService {

    private imageUrl = 'http://192.168.201.211:8024/images/';
    //set headers for authorization, https://stackoverflow.com/a/34465070/2803344
    createAuthorizationHeader(headers: Headers, name: string, pw: string) {
        headers.append('Authorization', 'Basic ' +
          btoa(`${name}:${pw}`)); 
    }

    createOptions(name: string, pw: string) {
        let headers = new Headers();
        this.createAuthorizationHeader(headers, name, pw);
        // headers.append('Content-Type', 'application/json');  // without this
        // headers.append('Content-Type', 'multipart/form-data');  // without this
        let options = new RequestOptions({ headers: headers });
        return options;
    }

    constructor(private http: Http) { }

    getImageById(id: number): Promise<Image> {
        const url = `${this.imageUrl}`;
        console.log(url);
        let headers = new Headers();
        let token = 'token';
        headers.append('X-Auth-Token', token);
        return this.http.get(url, {headers: headers})
                        .toPromise()
                        // .then(res => res.json().data as Image)
                        .then(res => console.log(res))
                        .catch(this.handleError);
    }

    getImageByUrl(url: string): Promise<Image> {
        return this.http.get(url)
                      .toPromise()
                      .then(res => res.json() as Image)
                      // .then(res => console.log(res))
                      .catch(this.handleError);
    }

    post(formData: FormData, user: string, pw: string): Promise<Image> {
        let options = this.createOptions(user, pw);
        console.log('we will have a post!');
        console.log(formData.get('localImage'));
        console.log(formData.get('fileUrl'));
        console.log(formData.get('des'));
        return this.http.post(this.imageUrl, formData, options)
                        .toPromise()
                        .then(res => res.json() as Image)
                        .catch(this.handleError);
    }

    private handleError(error: any): Promise<any> {
        console.error('An error occurred', error); // for demo purposes only
        return Promise.reject(error.message || error);
    }
}

You can find the whole project in github, https://github.com/OnlyBelter/image-sharing-system2.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.