如何使用 formBuilder 在 angular2 中为新的嵌套 JSON 数据插入新图像?

How to insert new image for new nested JSON data in angular2 with formBuilder?

我想做的是使用 formBuilder 创建一个包含图像的 JSON 数据以保存到 firebase 数据库中,但首先我需要在主 JSON 数据中创建嵌套数组数据包含每个数组的图像,我需要每个数组数据插入新图像

我的 JSON 数据中嵌套了 formArray :

{
  "nomor_transaksi": "",
  "returDetails": [
    {
      "kd_brg": "",
      "qty": "",
      "pengaduan": "",
      "kd_lokasi": "",
      "picture": ""
    },
    {
      "kd_brg": "",
      "qty": "",
      "pengaduan": "",
      "kd_lokasi": "",
      "picture": ""
    }
  ]
}

这是我的 html 代码:

<form [formGroup]="datareturForm" (ngSubmit)="submitForm()">

      <div class="form-group">
        <label for="nomor_transaksi">Nomor Transaksi</label>
        <input #notrans type="text" placeholder="nomor transaksi" formControlName="nomor_transaksi"/>
        <p *ngIf="datareturForm.controls.nomor_transaksi.errors">Pelan2 bro, max 10 char ya</p>
      </div><br/>

      <!-- details mulai disini-->

      <div class="form-group">
        <div formArrayName="returDetails">
          <p>List Barang : </p>
          <div *ngFor="let retur of returDetails.controls; let i=index" [formGroupName]="i">
            <label for="kd_brg">Kode Barang</label>
            <input type="text" placeholder="Kode Barang" formControlName="kd_brg"/>
            <!--<p *ngIf="returDetails.controls.kd_brg.errors">Pelan2 bro, max 10 char ya</p>-->

            <label for="qty">Qty</label>
            <input type="text" placeholder="Qty" formControlName="qty"/>
            <!--<p *ngIf="returDetails.controls.kd_brg.errors">Pelan2 bro, max 10 char ya</p>-->

            <label for="pengaduan">Pengaduan</label>
            <input type="text" placeholder="Pengaduan" formControlName="pengaduan"/>
            <!--<p *ngIf="returDetails.controls.kd_brg.errors">Pelan2 bro, max 10 char ya</p>-->

            <label for="kd_lokasi">Kode Lokasi</label>
            <input type="text" placeholder="Kode Barang" formControlName="kd_lokasi"/>
            <!--<p *ngIf="returDetails.controls.kd_brg.errors">Pelan2 bro, max 10 char ya</p>-->

            <label for="gambar">Gambar</label>
            <input type="file" (change)="onChange($event)" />

            <label for="preview">Preview</label>
            <img [src]="fileupload">
          </div>
        </div>
      </div><br/>

      <button type="submit">Save Contact</button>
      <button type="button" (click)="deleteRetur(notrans.value)">Delete</button>
      <br/>

    </form>
<button (click)="add()">Add Detail</button>

每次单击 "Add Detail" 按钮,它都会为新行数据创建新的数组 JSON,我将 "add detail" 按钮放在窗体之外,因为如果我将它放在表单,它会认为我点击并提交。

这是我的打字稿代码:

  datareturForm: FormGroup;
  returDetails: FormArray;
  items: FirebaseListObservable<any[]>;
  fileupload: any;

  ngOnInit(){

    this.datareturForm = this.formBuilder.group({
      nomor_transaksi: ['', Validators.maxLength(20)],
      returDetails: this.bikinArray()
    })

  }

  bikinArray(): FormArray {
    this.returDetails = this.formBuilder.array([
      this.buildGroup()
    ]);
    return this.returDetails;
  }

  buildGroup(): FormGroup {
    return this.formBuilder.group({
      kd_brg:'',
      qty:'',
      pengaduan:'',
      kd_lokasi:'',
      gambar:''
    });
  }

  add() {
    this.returDetails.push(this.buildGroup());
  }

    onChange(input): void {



          // Create an img element and add the image file data to it
          var img = document.createElement("img");
          // var img:any = new Image();
          img.src = window.URL.createObjectURL(input.target.files[0]);

          // Create a FileReader
          var reader: any, target: EventTarget;
          reader = new FileReader();
          // var that = this;
          // Add an event listener to deal with the file when the reader is complete
          reader.addEventListener("load", (event) => {
              // Get the event.target.result from the reader (base64 of the image)
              img.src = event.target.result;

              // Resize the image
              var resized_img = this.resize(img,input.target.files[0].type);
              // that.cropper.setImage(img);
              // Push the img src (base64 string) into our array that we display in our html template
              this.fileupload=resized_img;


              // this.file=dataUrl;
              // var imgBlob = this.dataURItoBlob(this.file);
              // this.fileupload = imgBlob

          }, false);

          reader.readAsDataURL(input.target.files[0]);
        }

        resize (img, type, MAX_WIDTH:number = 700, MAX_HEIGHT:number = 700){

          var canvas = document.createElement("canvas");

          // console.log("Size Before: " + img.src.length + " bytes");

          var width = img.width;
          var height = img.height;

          if (width > height) {
              if (width > MAX_WIDTH) {
                  height *= MAX_WIDTH / width;
                  width = MAX_WIDTH;
              }
          } else {
              if (height > MAX_HEIGHT) {
                  width *= MAX_HEIGHT / height;
                  height = MAX_HEIGHT;
              }
          }
          canvas.width = width;
          canvas.height = height;
          var ctx = canvas.getContext("2d");

          ctx.drawImage(img, 0, 0, width, height);

          var dataUrl = canvas.toDataURL(type);  
          // IMPORTANT: 'jpeg' NOT 'jpg'
          // console.log("Size After:  " + dataUrl.length  + " bytes");
          return dataUrl
        }

每次我点击添加详细信息按钮,它都会创建新的嵌套数组,但图像不断循环相同的图像,我想添加新图像:

看到它循环播放同一张图片,当我替换其中一张数组图片时,其他图片也不断更改同一张图片。我需要每一行都是不同的图像,我该怎么做?

=============================================

更新

我刚刚更新了我的代码,就像@Fabio Antunes 的回答一样,我得到的是这个错误:

error_handler.js:45 EXCEPTION: Cannot set property 'picture' of undefinedErrorHandler.handleError @ error_handler.js:45next @ application_ref.js:273schedulerFn @ async.js:82SafeSubscriber.__tryOrUnsub @ Subscriber.js:223SafeSubscriber.next @ Subscriber.js:172Subscriber._next @ Subscriber.js:125Subscriber.next @ Subscriber.js:89Subject.next @ Subject.js:55EventEmitter.emit @ async.js:74onError @ ng_zone.js:120onHandleError @ ng_zone_impl.js:64ZoneDelegate.handleError @ zone.js:207Zone.runTask @ zone.js:139ZoneTask.invoke @ zone.js:304 error_handler.js:50 ORIGINAL STACKTRACE:ErrorHandler.handleError @ error_handler.js:50next @ application_ref.js:273schedulerFn @ async.js:82SafeSubscriber.__tryOrUnsub @ Subscriber.js:223SafeSubscriber.next @ Subscriber.js:172Subscriber._next @ Subscriber.js:125Subscriber.next @ Subscriber.js:89Subject.next @ Subject.js:55EventEmitter.emit @ async.js:74onError @ ng_zone.js:120onHandleError @ ng_zone_impl.js:64ZoneDelegate.handleError @ zone.js:207Zone.runTask @ zone.js:139ZoneTask.invoke @ zone.js:304 error_handler.js:51 TypeError: Cannot set property 'picture' of undefined at FileReader. (app.component.ts:115) at ZoneDelegate.invokeTask (zone.js:236) at Object.onInvokeTask (ng_zone_impl.js:34) at ZoneDelegate.invokeTask (zone.js:235) at Zone.runTask (zone.js:136) at FileReader.ZoneTask.invoke (zone.js:304)ErrorHandler.handleError @ error_handler.js:51next @ application_ref.js:273schedulerFn @ async.js:82SafeSubscriber.__tryOrUnsub @ Subscriber.js:223SafeSubscriber.next @ Subscriber.js:172Subscriber._next @ Subscriber.js:125Subscriber.next @ Subscriber.js:89Subject.next @ Subject.js:55EventEmitter.emit @ async.js:74onError @ ng_zone.js:120onHandleError @ ng_zone_impl.js:64ZoneDelegate.handleError @ zone.js:207Zone.runTask @ zone.js:139ZoneTask.invoke @ zone.js:304 app.component.ts:115 Uncaught TypeError: Cannot set property 'picture' of undefined(…)

外观如下:

我认为它不会绑定到我的 json 树,对吗?如何绑定呢? 我确实添加了我的 html 代码,如下所示:

    <label for="gambar">Gambar</label>
    <input type="file" (change)="onChange($event,i)" formControlName="picture"/>

    <label for="preview">Preview</label>
    <img [src]="retur.picture">

但什么也没发生,我仍然遇到同样的错误,我试图 google 关于 formBuilder/reactive 表单处理它没有显示结果,在这里需要帮助,不知道要搜索什么

因为我认为 formBuilder / 反应形式中没有任何绑定函数,例如 formControlName="" 我确实从我的 app.component.ts 中注入了 JSON 手册,这就是我所做的:

在我的 app.component.html 中添加

    <label for="gambar">Gambar</label>
    <input type="file" (change)="onChange($event,i)" />

    <label for="preview">Preview</label>
    <img [src]="retur.value.picture">

请注意,我无法访问 json 值,只是不知道为什么,在我朋友的帮助下,我尝试了很多错误,哈哈

下面是我的 app.component.ts :

  ngOnInit(){

    this.datareturForm = this.formBuilder.group({
      nomor_transaksi: ['', Validators.maxLength(20)],
      returDetails: this.bikinArray()
    })

  }

  bikinArray(): FormArray {
    this.returDetails = this.formBuilder.array([
      this.buildGroup()
    ]);
    return this.returDetails;
  }

  buildGroup(): FormGroup {
    return this.formBuilder.group({
      kd_brg:'',
      qty:'',
      pengaduan:'',
      kd_lokasi:'',
      namafile:'',
      picture: '',
      hasilImgBlob: ''
    });
  }

      add() {
        this.returDetails.push(this.buildGroup());
      }

onChange(input, index): void {

      // Create an img element and add the image file data to it
      var img = document.createElement("img");
      // var img:any = new Image();
      img.src = window.URL.createObjectURL(input.target.files[0]);

      //ambil nama file
      this.returDetails.controls[index].value.namafile=input.target.files[0].name;
      // console.log(this.namafile);

      // Create a FileReader
      var reader: any, target: EventTarget;
      reader = new FileReader();
      // var that = this;
      // Add an event listener to deal with the file when the reader is complete
      reader.addEventListener("load", (event) => {
          // Get the event.target.result from the reader (base64 of the image)
          img.src = event.target.result;

          // Resize the image
          var resized_img = this.resize(img,input.target.files[0].type);
          // that.cropper.setImage(img);
          // Push the img src (base64 string) into our array that we display in our html template
          this.fileupload=resized_img;

          this.returDetails.controls[index].value.picture=resized_img;

          var imgBlob = this.dataURItoBlob(this.fileupload);
          this.returDetails.controls[index].value.hasilImgBlob=imgBlob

          //ini kasi nama baru aja, terus nanti yg ini yg di save
          // this.hasilImgBlob[] = imgBlob
          //indexing gambar
          // this.returDetails[index].picture = resized_img;
          // console.info(index);

          // this.file=dataUrl;

      }, false);

      reader.readAsDataURL(input.target.files[0]);
    }

    //you need this function to convert the dataURI
    dataURItoBlob(dataURI) {
      var binary = atob(dataURI.split(',')[1]);
      var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
      var array = [];
      for (var i = 0; i < binary.length; i++) {
        array.push(binary.charCodeAt(i));
      }
      return new Blob([new Uint8Array(array)], {
        type: mimeString
      });
    }

    resize (img, type, MAX_WIDTH:number = 700, MAX_HEIGHT:number = 700){

      var canvas = document.createElement("canvas");

      // console.log("Size Before: " + img.src.length + " bytes");

      var width = img.width;
      var height = img.height;

      if (width > height) {
          if (width > MAX_WIDTH) {
              height *= MAX_WIDTH / width;
              width = MAX_WIDTH;
          }
      } else {
          if (height > MAX_HEIGHT) {
              width *= MAX_HEIGHT / height;
              height = MAX_HEIGHT;
          }
      }
      canvas.width = width;
      canvas.height = height;
      var ctx = canvas.getContext("2d");

      ctx.drawImage(img, 0, 0, width, height);

      var dataUrl = canvas.toDataURL(type);  
      // IMPORTANT: 'jpeg' NOT 'jpg'
      // console.log("Size After:  " + dataUrl.length  + " bytes");
      return dataUrl
    }


    save():void{
        if(this.fileupload==undefined){
            //ini bagian, save tanpa image ke storage
              //this.items.push(this.profileData);
        }else{
              event.preventDefault();
              //loop si retur.control
              for(var i = 0; i < this.returDetails.length ; i++){

                // ini dapetin nama file image
                let namaFileEdited:string= new Date().getTime()+'-'+this.returDetails.controls[i].value.namafile;

                let storageRef: any = firebase.storage().ref();
                //ini lokasi storage u folder n nama nya apa
                let path: string = 'images/' + namaFileEdited;
                //ini fungsi, save ke storage, (image nya)

                 let uploadTask: any = storageRef.child(path).put(this.returDetails.controls[i].value.hasilImgBlob);

                  //ini progress nya
                  uploadTask.on('state_changed', (snapshot)=> {
                    var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
                      console.log('Upload is ' + progress + '% done');
                      switch (snapshot.state) {
                        case firebase.storage.TaskState.PAUSED: // or 'paused'
                          console.log('Upload is paused');
                          break;
                        case firebase.storage.TaskState.RUNNING: // or 'running'
                          console.log('Upload is running');
                          break;
                      }
                  }, (error) => {
                    //ini klo error
                    console.info('dafuq error bro');
                  }, () =>{
                    //ini completed
                    //ini ambil nama file, masukin ke json profiledata
                    this.datareturForm['namafile']=namaFileEdited;
                    //ini ambil link gambar di storage, masukin ke json profile data
                    this.datareturForm['picture']=uploadTask.snapshot.downloadURL;

                     //save ke database
                    this.items.push(this.datareturForm.value)
                  });
              }

        }

    }

抱歉有很多印度尼西亚语的评论,请不要介意,这是我自己的笔记。正如你们看到的那样,我在 onChange 函数中添加了 returDetails.picture,看看这一行 this.returDetails.controls[index].value.picture=resized_img;

我将 resized_img 添加到 json 变量中,所以这里的 json 会像这样:

如您所见,之前我选择的图像未添加到 json 变量,但现在我添加了它,如果您对 "hasilImgBlob" 它的图像数据有疑问我添加到 JSON 变量中的 BLOB,但结果只是 {} 我仍然不知道为什么,但是当我将它推入 firebase 时,它​​保存了 hola!我看不到 {} 中的内容我只能调试对象,希望这段小代码可以帮助其他人。不要介意我跳过的 JSON 变量,不是我不想写它,而是它和第一个 "nomor_transaksi" 一样是同一个变量。我跳过它因为它太多了

注意:我使用此代码推送到 firebase 数据库并将图像保存到 firebase 存储中。由于 BLOB 数据保存在 json 数据库文件中,因此这段代码仍然没有让我满意,但本主题与此无关。最好将 url 保存到 firebase 存储中,因为我已经将它保存到 firebase 存储中