【问题标题】:Angular 8 - Upload multiple array of images using form builderAngular 8 - 使用表单构建器上传多个图像数组
【发布时间】:2026-02-07 22:45:01
【问题描述】:

我有一个产品表单,其中包含产品名称、描述等不同字段。还有图像数组。

产品形式

产品名称产品描述

产品纯度产品商品

图片(添加)

复选框 1 图片 1

复选框 2 图片 2

复选框 3 图片 3。

....

我可以在我的数据库中保存产品名称和产品描述以及其他字段,但不知道如何上传这些图像。因为图像是在单击添加按钮时创建的,并且根据要求它可能有一个或多个图像。我使用表单构建器创建了表单。鉴于我的代码如下。

模板:

<form  class="kt-form kt-form--group-seperator-dashed" [formGroup]="mmgForm">
    <div class="kt-form__section kt-form__section--first">
        <div class="kt-form__group">
            <div class="row">
                <label class="col-lg-2 col-form-label">Product Name:</label>
                <div class="col-lg-4">
                    <mat-form-field class="example-full-width">
                        <input formControlName="prod_name" matInput placeholder="Enter product name" [ngClass]="{ 'is-invalid': submitted && mmgForm.controls['prod_name'].errors }">
                        <div *ngIf="submitted && mmgForm.controls['prod_name'].errors" class="invalid-feedback cls-formErr">
                            <div *ngIf="mmgForm.controls['prod_name'].errors.required">Product name is required</div>
                        </div>
                    </mat-form-field>
                </div>
                <label class="col-lg-2 col-form-label">Product description:</label>
                <div class="col-lg-4">
                    <mat-form-field class="example-full-width">
                        <textarea formControlName="prod_description" matInput placeholder="Enter product description" rows="5" [ngClass]="{ 'is-invalid': submitted && mmgForm.controls['prod_description'].errors }"></textarea>
                        <div *ngIf="submitted && mmgForm.controls['prod_description'].errors" class="invalid-feedback cls-formErr">
                            <div *ngIf="mmgForm.controls['prod_description'].errors.required">Product description is required</div>
                        </div>
                    </mat-form-field>
                </div>
            </div>
        </div>
        <div class="product_images">
            <div class="imageHeading">
                <p>
                    Images &nbsp; (<button mat-icon-button color="primary" matTooltip="Add product" (click) = addImage()><mat-icon>add_circle</mat-icon></button>)
                </p>
            </div>
            <div class="kt-form__group">
                <div class="row">
                    <div class="col-lg-1 imageLabel">#</div>
                    <div class="col-lg-2 imageLabel">Main Image</div>
                    <div class="col-lg-3 imageLabel">Choose Image</div>
                    <div class="col-lg-2 imageLabel">Image</div>
                    <div class="col-lg-2 imageLabel">Actions</div>
                </div>
            </div>
            <div class="imagesContainer">
                <div class="kt-form__group image-container container-1" *ngFor="let image of images; index as i">
                    <div class="row">
                        <div class="col-lg-1">{{ i+1 }}</div>
                        <div class="col-lg-2"><input type="checkbox" /></div>
                        <div class="col-lg-3"><input type="file" accept="image/*" (change)="imagePreview($event, image)" /></div>
                        <div class="col-lg-2"><img [src]="image.url.imgUrl" class="prod_image" /></div>
                        <div class="col-lg-2">
                            <button mat-icon-button color="warn" matTooltip="Delete Product" type="button" (click)="deleteImage(i)">
                                <mat-icon>delete</mat-icon>
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</form>

Ts 文件:

// Angular
import { Component, OnInit, ElementRef, ViewChild, ChangeDetectionStrategy, OnDestroy, ChangeDetectorRef } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
// Material
import { SelectionModel } from '@angular/cdk/collections';
import { MatPaginator, MatSort, MatSnackBar, MatDialog, MatRadioButton } from '@angular/material';
import { ProductManagementService } from '../../../../../core/e-commerce/_services/product-management.service';
import { ToastrService } from 'ngx-toastr';

@Component({
  selector: 'kt-product-edit',
  templateUrl: './product-edit.component.html',
  styleUrls: ['./product-edit.component.scss'],
})
export class ProductEditComponent implements OnInit {

    mmgForm : any;
    fileData: File = null;
    previewUrl : any = "/assets/media/images/noimage.jpg"; 
    images : any = [];

    constructor(
    private products: ProductManagementService,
    private router: Router,
    private route: ActivatedRoute,
    private toastr: ToastrService,
    private cdr: ChangeDetectorRef,
    private FB: FormBuilder,
    ) {

    }

  ngOnInit() {
    this.createForm();
    this.addImage();
  }

  createForm() {
      this.mmgForm = this.FB.group({
      prod_name: ['', Validators.required],
      prod_description: ['', Validators.required]
    });
  }

  /**
     * Form Submit
     */
    submit() {
        if (this.mmgForm.invalid) {
          console.log(this.mmgForm);
          return;
        }
        const controls = this.mmgForm.controls;

        var form_values = {
          prod_name: controls.prod_name.value,
          prod_description: controls.prod_description.value,
        }
        this.products
          .createProduct(JSON.stringify(form_values)).subscribe(  //Calling Service
        (data) => {
          console.log(data);
        },
        error => {

        }
      );
    }

  imagePreview(fileInput: any, image) {
    this.fileData = <File>fileInput.target.files[0];
    // Show preview 
    var mimeType = this.fileData.type;
    if (mimeType.match(/image\/*/) == null) {
      return;
    }
    var reader = new FileReader();      
    reader.readAsDataURL(this.fileData); 
    reader.onload = (_event) => { 
    image.url.imgUrl = reader.result; 
    this.cdr.markForCheck();
    }
  }

  addImage(){
    let url = {imgUrl : this.previewUrl}
    this.images.push({url});
  }
  deleteImage(index: number) {
    this.images.splice(index, 1)
  }
}

【问题讨论】:

  • 我认为你应该澄清你的问题,这很难理解!
  • @Max 我已经编辑了我的问题。基本上它是一个包含图像数组的表单,不知道如何使用 angular 将这些图像上传到服务器。
  • 你的图片是文件还是字节码?

标签: javascript angular typescript angular8


【解决方案1】:

我建议为此创建一个FileUploadService

服务方法:

uploadFiles(Array<File> files): Observable<FileUploadResponse> {
    const url = `your-upload-url`;

    const formData: FormData = new FormData();

    for(File file in files) {
        formData.append('file', file, file.name);
    }

    return this.http.post<FileUploadResponse>(url, formData /*{headers if necessary}*/);
}

你的组件.ts:

//[..]
Array<File> files = [];

onFileInputEvent(event: any) {
  for(let i = 0; i < event.srcElement.files.length; i++) {
    this.fileName = event.srcElement.files[0].name;
    this.files.push(event.srcElement.files[0]);
  }
}

uploadFiles(){
  this.fileUploadService.uploadFile(this.file).subscribe(res => { // do something}, err =>{});
}

你的component.html:

<!-- [..] -->
<input id="fileInput" placeholder="Images" type="file" (change)="onFileInputEvent($event)" accept=".jpg">

编辑,输入更美观

<div class="file-input">
    <input id="fileInput" placeholder="CSV-Datei" type="file" class="hidden" (change)="onFileInputEvent($event)" accept=".csv">
    <label for="fileInput" class="mat-raised-button">Add images</label>
    <mat-chip-list *ngIf="fileName">
        <mat-chip [removable]="true" (removed)="remove()">
            {{fileName}}
        <mat-icon matChipRemove>cancel</mat-icon>
    </mat-chip>
</mat-chip-list>

【讨论】: