Search code examples
arraysjsonformsangularformbuilder

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


what i trying to do is create a JSON data with image in it with formBuilder to save into firebase database, but first i need to create nested array data inside the main JSON data which is contain image for each array, i need each array data inserted with new image

My JSON data with nested formArray in it :

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

Here is my html code :

<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>

each i click "Add Detail" button, it will create new Array JSON for the new row data, i put the "add detail" button outside the form because if i put it in the form, it the form will think i click and do submit.

Here is my typescript code :

  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
        }

and each i click the add detail button it will create new nested array but the image keep looping the same image, i want to add new image: Screenshot

see it's loop the same image and when i replace one of the array image, the others keep changing the same image too. I need each row is different image, how do i do this?

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

UPDATE

I just updated my code just like @Fabio Antunes answer, what i got is this error :

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(…)

Here is the looks : Preview

i think it doesn't bind to my json tree right? how to bind it? i do add my html code like this :

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

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

but nothing happened, still i got the same error, i trying to google about formBuilder/reactive form handling it doens't show a result, need help here, dunno what to search anymore


Solution

  • since i don't think there is any bind function in formBuilder / reactive form for like formControlName="" in i did inject the JSON manual from my app.component.ts here is what i did :

    in my app.component.html i add

        <label for="gambar">Gambar</label>
        <input type="file" (change)="onChange($event,i)" />
    
        <label for="preview">Preview</label>
        <img [src]="retur.value.picture">
    

    notice the i can't access the json value with just dunno why, i do try and error alot with help from my friend lol

    then below is my 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)
                      });
                  }
    
            }
    
        }
    

    sorry for many comment in indonesia language, don't mind it, it's my own notes for my self. as you guys can see in there i add returDetails.picture in onChange function, look at this line this.returDetails.controls[index].value.picture=resized_img;

    there i add the resized_img into json variable, so here is the json gonna be like this :

    enter image description here

    as you can see before the image i choose is not added to the json variable, but now i added it, and if you have question in your mind about "hasilImgBlob" its a image data BLOB that i add into JSON variable, but the result just {} still i dunno why, but when i push it into firebase, it saved hola! i can't see whats in {} i just can debug the object, hope this little code can help the other. and don't mind the JSON variable that i skipped, it's not i don't wanna write it, but it's the same variable just like the first one "nomor_transaksi". i skipped it becuase it's too many

    note : this code i use to push into firebase database and the image is saved into firebase storage. this code still haven't met my satisfaction since the BLOB data is saved in json database file, but this topic is not about this. it's better saved the url for the firebase storage since i already saved it into firebase storage