Ng2-file-upload - where to import module?


#1

I am trying to use ng2-file-upload in Ionic3. Not sure if this is Ionic 3 specific, I doubt it.

But the problem is importing the right module in the right place.
Here is the error: Uncaught (in promise): Error: Template parse errors: Can’t bind to ‘uploader’ since it isn’t a known property of ‘input’

Here is a link to a thread about it outside of Ionic:
GitHub

The supposed answer to this is to import the FileUploadModule in the parent module instead of in the grandparent.

Can someone explain how this would be in an Ionic environment?
Does it not get imported in the app.module.ts?
Does it not get imported in the page.ts?
Where would it go then?

Or any other advice to get ng2-file-upload working again.
This is intended for browser uploads.


#3

You have to import it in you yorpage.module.ts file.


#4

I should have mentioned I tried that.

import { FileUploadModule } from ‘ng2-file-upload/file-upload/file-upload.module’;
and import { FileUploadModule } from ‘ng2-file-upload/file-upload/file-upload’;


#5

You should import

import { FileSelectDirective } from 'ng2-file-upload';

You can read it here: http://valor-software.com/ng2-file-upload/

Switch to thy TypeScript tab


#6

Do you know this to work?
The GitHub link above mentions problems with the docs and others have found solutions.
However, I’m unclear how to do it in Ionic. At least not successfullly.

Using import { FileSelectDirective } from ‘ng2-file-upload’; has not worked for me.
Same errors:
Here is the error: Uncaught (in promise): Error: Template parse errors: Can’t bind to ‘uploader’ since it isn’t a known property of ‘input’

Or …property of ‘div’ depending on which you use.


#7

DeepLinks seems to be the culprit.

After removing DeepLinking, ng2-file-upload is working when I import FileUploader into the Page.

import { FileUploader} from 'ng2-file-upload';

Other than just avoiding DeepLinks until a future version - if anyone has any experience or ideas on how to get by this - I’m all ears.

In the meanwhile, although this may not be the optimal way to do this, I am DeepLinking other pages and importing any page with ng-file-upload being used in it.
Seems to be OK so far.


#8

I have seen that you are the only one who has achieved it, even in other posts you gave an example of how you did it, but it does not work for me.
Would you be so kind as to set a full example?


#9

I use IonicPage for lazy loading all pages except for a couple that utilize ng2-file-upload.

My UploadPage has a lot of walk-through form submission stuff going on. Here is what I think is all you need to to creat a file drop zone, display added images in slides and then upload all images with additional parameters to a server with authorization ( some redundancy in the authHeaders but you can see which works for you ).

This is for a PWA.

I am uploading images one at a time through a loop and passing extra data along with each.
There are methods for uploading multiple at once - not in this code though.

Good luck!

In app.module.ts:

import { FileUploadModule } from 'ng2-file-upload';

 imports: [
  FileUploadModule
]

In UploadPage.ts:


import { FileUploader, FileItem } from 'ng2-file-upload';

export class UploadPage {
    @ViewChild('imageSlider') imageSlider: Slides;

    uploader;
    public hasBaseDropZoneOver: boolean = false;
    public hasAnotherDropZoneOver: boolean = false;

    public fileOverBase(e: any): void {
        console.log("-fileOverBase-")
        this.hasBaseDropZoneOver = e;
    }

    public fileOverAnother(e: any): void {
        console.log("-fileOverAnother-")
        this.hasAnotherDropZoneOver = e;
    }

}

setupUploader() {
        this.uploader = new FileUploader({ authToken: 'Bearer ' + this.auth.getToken(), url: this.appURL + "/api/v1/upload_item_image", method: 'post' });

        let authHeader = new Headers({ "Content-Type": "application/json" });
        authHeader.append('Authorization', 'Bearer ' + this.auth.getToken());

        const uploadOptions = <FileUploadOptions>{ headers: authHeader };

        this.uploader.onBeforeUploadItem = (item: FileItem) => {
            console.log("----onBeforeUploadItem");

           //add additional parameters for the serverside
            this.uploader.options.additionalParameter = {
                name: item.file.name,
                section: "whatever",
                 userid = __this.auth.user.userid;
            };
        };

        this.uploader.onAfterAddingFile = (fileItem) => {
            console.log("JUST ADDED A FILE: " + fileItem._file.name);
            
            this.readLocalFile(String(this.uploader.queue.length - 1), fileItem);
            
            this.updateImagesInQueue();
        }
    }

    updateImagesInQueue() {
            let i = 0;
            for (let file of this.uploader.queue) {
                this.readLocalFile(String(i), file);
                i++;
            }
    }

    readLocalFile(id, ff) {
        var reader = new FileReader();
        reader.onload = readSuccess;
        function readSuccess(evt) {
            document.getElementById(id)["src"] = evt.target.result;
        };
        reader.readAsDataURL(ff._file);
    }

   uploadAllImages() {
        this.uploading = true;
        for (let file of this.uploader.queue) {
            this.uploadFile(file);
        }
    }

uploadFile(file) {
        file.upload();
        this.uploader.onCompleteItem = (item: any, response: any, status: any, headers: any) => {
            console.log("A ITEM UPLOADED SUCCESSFULLY");
            var response_json = JSON.parse(response);
            console.log("--uploader.getNotUploadedItems().length: " + this.uploader.getNotUploadedItems().length)
            if (this.uploader.getNotUploadedItems().length < 1) {
                console.log("All Image have been uploaded");
            }
        };
    }

In UploadPage.html:

<input id="camera-upload" type="file" class="camerafile" accept="image/*" ng2FileSelect [uploader]="uploader" multiple>
                                <label for="camera-upload" class="custom-file-upload">
                                    <i></i>Select Images</label>

<div *ngIf="uploader&&!uploader_closed" ng2FileDrop [ngClass]="{'nv-file-over': hasBaseDropZoneOver}" (fileOver)="fileOverBase($event)"
                            [uploader]="uploader" class="well my-drop-zone">
                            <p *ngIf="uploader.queue.length<1">Drop Image Files Here</p>
                            <ion-slides #imageSlider class="imageSlider" spaceBetween="-50" *ngIf="!uploader_closed" (ionSlideDidChange)="slideChanged()">
                                <ion-slide class="imageSlide" *ngFor="let item of uploader.queue; let i = index">
                                    <img class="image" id="{{i}}">
                                    <br>
                                    <button id="remove_btn" ion-button small icon-right *ngIf="!item.isSuccess" (click)="item.remove()" color="danger">Remove
                                        <ion-icon name="trash"></ion-icon>
                                    </button>
                                </ion-slide>
                            </ion-slides>
                        </div>

In UploadPage.scss:

.inputfile {
        display: none!important;
        z-index: 10!important;
    }
    .custom-file-upload {
        border: 2px solid #ccc;
        background-color: bisque;
        border-radius: 15px!important;
        display: inline-block;
        padding: 6px 12px;
        cursor: pointer;
        z-index: 10!important;
        width: 120px;
        text-align: center!important;
    }

#10

thanks for the answer, my problem was that I did not know where to indicate the php that kept the file. but I have already solved it. In the end I got it to work exactly as I wanted, I put the code to help the next try (the code is something different from yours).
in console: ionic g page upload
upload.html

<ion-header>

  <ion-navbar>
    <ion-title>upload</ion-title>
  </ion-navbar>

</ion-header>


<ion-content padding>
  <ion-grid>
    <ion-row>
        <ion-col width-40 [ngStyle]="{'background-color': 'Bisque'}">
          <h3>Seleccionar Imagenes</h3>
          <div width="200px">Indique Carpeta:<br><input class="cajaTexto" type="text" id="carpeta" [(ngModel)]="carpeta" /></div>
            <input type="file" class="invisible" id="seleccion" ng2FileSelect [uploader]="uploader" multiple />
            <button ion-button small icon-right [ngStyle]="{'background-color': '#D2691E'}" (click)="openFileDialog1()">Seleccione Imagenes</button>
        </ion-col>
        <ion-col width-60 *ngIf="uploader?.queue?.length >0">
            <div *ngIf="uploader?.queue?.length >1">
              <h3>Lista de Archivos</h3>
              <p>Archivos Listados: {{ uploader?.queue?.length }}</p>
            </div>
            <table class="table">
                <tbody>
                    <tr *ngFor="let item of uploader.queue">
                        <td><strong>{{ item?.file?.name }}</strong></td>
                        <td *ngIf="uploader.isHTML5" nowrap>{{ item?.file?.size/1024/1024 | number:'.2' }} MB</td>
                        <td *ngIf="uploader.isHTML5">
                            <progress id="progressbar" max="100" value="{{ item.progress }}"> </progress>
                            <div id="progressbarlabel">{{ item.progress }} %</div>
                        </td>
                        <td class="text-center">
                            <span *ngIf="item.isCancel"><ion-icon class="iconorojo" name="close-circle"></ion-icon></span>
                            <span *ngIf="item.isError"><ion-icon class="iconorojo" name="close-circle"></ion-icon></span>
                        </td>
                        <td nowrap>
                            <button ion-button small icon-right *ngIf="!item.isSuccess" (click)="item.upload()" [disabled]="item.isReady || item.isUploading || item.isSuccess">Subir<ion-icon name="cloud-upload"></ion-icon></button>
                            <!-- <button ion-button icon-right *ngIf="!item.isSuccess" (click)="item.remove()" [ngStyle]="{'background-color': '#D2691E'}">Cancel<ion-icon name="close-circle"></ion-icon></button> -->
                            <button ion-button small icon-right *ngIf="!item.isSuccess" (click)="item.remove()" color="danger">Limpiar<ion-icon name="trash"></ion-icon></button>
                            <span *ngIf="item.isSuccess"><ion-icon class="iconoverde" name="checkmark-circle-outline"></ion-icon></span>
                        </td>
                    </tr>
                </tbody>
            </table>
            <div>
                <div>
                    Progreso Global: {{ uploader.progress }} %
                    <progress id="progressbar" class="progreso" max="100" value="{{ uploader.progress }}"> </progress>
                    
                </div>
                <div *ngIf="uploader?.queue?.length >1">
                  <button ion-button small icon-right (click)="uploader.uploadAll()" [disabled]="!uploader.getNotUploadedItems().length">Subir Todo<ion-icon name="cloud-upload"></ion-icon></button>
                  <!-- <button ion-button icon-right (click)="uploader.cancelAll()" [ngStyle]="{'background-color': '#D2691E'}" [disabled]="!uploader.isUploading">Cancel all<ion-icon name="close-circle"></ion-icon></button> -->
                  <button ion-button small icon-right color="danger" (click)="uploader.clearQueue()" [disabled]="!uploader.queue.length">Limpiar Todo<ion-icon name="trash"></ion-icon></button>
                  <!-- <button type="button" class="btn btn-warning small " (click)="uploader.cancelAll()" [disabled]="!uploader.isUploading"></button> 
                  <button type="button" class="btn btn-danger small " (click)="uploader.clearQueue()" [disabled]="!uploader.queue.length"></button>-->
                </div>
            </div>
        </ion-col>
    </ion-row>
</ion-grid>
</ion-content>

upload.module.ts

import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { UploadPage } from './upload';
import { FileSelectDirective } from 'ng2-file-upload';
@NgModule({
  declarations: [
    UploadPage,
    FileSelectDirective,
  ],
  imports: [
    
    IonicPageModule.forChild(UploadPage),
  ],
  exports:[
    UploadPage
  ]
})
export class UploadPageModule {}

upload.ts

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { FileUploader } from 'ng2-file-upload';
const URL = 'https://example.com/php/probar.php';

@IonicPage()
@Component({
  selector: 'page-upload',
  templateUrl: 'upload.html',
})
export class UploadPage {
  uploader:FileUploader;
  carpeta="";
  indice=0;
  
  constructor(public navCtrl: NavController, public navParams: NavParams) {
    this.uploader = new FileUploader({
      url: URL, // url del php que trata el fichero subido
      method: 'POST',
      removeAfterUpload: true, // lo quita de la lista una vez su subida fue correcta
      queueLimit: 100 // limite de archivos que se pueden añadir a la lista, si el numero de archivos seleccionados es superior se cogen los x primeros.
    });
    
    
    this.uploader.onBuildItemForm = (fileItem: any, form: any) => { 
      form.append('id' , this.indice); 
      form.append('fotonum' , 'n1'); 
      form.append('carpeta' , this.carpeta); 
      this.indice = this.indice + 1;
    };
    // Función que salta cada vez que añadimos un nuevo archivo, aqui podemos controlar que no se suban archivos demasiado grandes
    this.uploader.onAfterAddingFile = function(fileItem) {
          if (fileItem.file.size > 2048000) {
            let prompt = this.alertCtrl.create({
              title: 'Error de archivo',
              message: "El archivo que está intentando subir sobrepasa el limite de tamaño de 2mb, seleccione otro archivo.",
              buttons: [
                {
                  text: 'Aceptar',
                  handler: data => {
                    fileItem.cancel();
                    fileItem.remove();
                  }
                }
              ]
            });
            prompt.present();
          }
      console.info('onAfterAddingFile', fileItem);
  };
    // Función que salta cuando se han añadido todos los archivos seleccionados.
    this.uploader.onAfterAddingAll = function(addedFileItems) {
      console.info('onAfterAddingAll');
    };
    // Función que se ejecuta antes de subir un archivo. Aquí añadimos datos adicionales necesarios para renombrar el
    // archivo como nosotros queremos.
    this.uploader.onBeforeUploadItem = function(item) {
      console.info('onBeforeUploadItem');
    };
    // Barra de progreso que indica la subida.
    this.uploader.onProgressItem = function(fileItem, progress) {
      console.info('onProgressItem');
    };
    // Función que se ejecuta cuando la barra de progreso llega a su fin.
    this.uploader.onProgressAll = function(progress) {
      console.info('onProgressAll');
    };
    // Función que salta si la subida del archivo se produjo con éxito en nuestro caso guardamos los índices de las
    // imágenes para almacenarlos posteriormente en la bdd.
    this.uploader.onSuccessItem = function(fileItem, response, status, headers) {
      console.info('onSuccessItem', fileItem, response, status, headers);

    };
    // Función que salta si se produce un error en la subida.
    this.uploader.onErrorItem = function(fileItem, response, status, headers) {
      console.info('onErrorItem', fileItem, response, status, headers);
    };
    // Función que se ejecuta si se cancela la subida.
    this.uploader.onCancelItem = function(fileItem, response, status, headers) {
      console.info('onCancelItem', fileItem, response, status, headers);
    };

    this.uploader.onCompleteItem = function(fileItem, response, status, headers) {
      console.info('onCompleteItem');
    };
    this.uploader.onCompleteAll = function() {
      console.info('onCompleteAll');
    };

  }

 openFileDialog1 = function() {
   
    if (this.carpeta.length < 5) {
        alert("El nombre de carpeta no ha sido especificado o no se trata de un nombre valido.");
    } else {
        document.getElementById('seleccion').click();
    }
};
  ionViewDidLoad() {
    console.log('ionViewDidLoad UploadPage');
  }

}

upload.scss

page-upload {
    .my-drop-zone {border: dotted 3px blue;height: 100px;width: 100%;}
    .nv-file-over {border: dotted 3px red;}
    /* Default class applied to drop zones on over */
    .another-file-over-class {border: dotted 3px green;}
    html,body {height: 100%;}
    .iconorojo {padding-left:10px;color:red;font-size: 20px;}
    .iconoverde {padding-left:10px;color:green;font-size: 20px;}
    .invisible{visibility:  hidden;  position: absolute;}
    input.cajaTexto {
        height: 28px;
        width: 100%;
        border-style: solid;
        border-width: 1px;
        border-color: #e6b400;
        border-radius: 5px;
        padding-left: 10px;
        margin-top: 6px;
    }
    .cajaTexto {min-width: 0;}
    
    progress[value]::-webkit-progress-value {
        background-image:
             -webkit-linear-gradient(-45deg,transparent 33%, rgba(0, 0, 0, .1) 33%, rgba(0,0, 0, .1) 66%, transparent 66%),
             -webkit-linear-gradient(top, rgba(255, 255, 255, .25), rgba(0, 0, 0, .25)),
             -webkit-linear-gradient(left, #09c, #f44);
        border-radius: 5px; 
        background-size: 35px 20px, 100% 100%, 100% 100%;
      }
      
    progress[value]::-moz-progress-bar { 
        background-image:
          -moz-linear-gradient(135deg, transparent 33%, rgba(0, 0, 0, 0.1) 33%, rgba(0, 0, 0, 0.1) 66%, transparent 66% ),
          -moz-linear-gradient(top, rgba(255, 255, 255, 0.25), rgba(0, 0, 0, 0.25)),
          -moz-linear-gradient(left, #09c, #f44);
        border-radius: 5px; 
        background-size: 35px 20px, 100% 100%, 100% 100%; 
      }
    .progreso{
        border-radius: 5px;
        -moz-border-radius: 5px;
        -webkit-border-radius: 5px;
        border: 1px solid #bbbaba;
        background-color: rgb(236, 235, 234);
        width:100%;
    }
}

Sorry for the comments in Spanish, it’s a complete example with some design


#11

The only problem with this is that it does not make use of the Drop Zone for drag and drop.

This has been a problem for me for a long time and your question got me to revisit it.
I now add the FileUploadModule to the module.ts and include it in the imports.
FileSelectDirective is part of FileUploadModule so it no longer needs to be imported.
(FileSelectDirective must be removed from all other page modules or it will break during build --prod)

With the following, a drop zone can now be added to a div in a lazy loaded IonicPage.

import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { UploadPage } from './upload';
//import { FileSelectDirective } from 'ng2-file-upload';
import { FileUploadModule } from 'ng2-file-upload';
@NgModule({
  declarations: [
    UploadPage,
    //FileSelectDirective,
  ],
  imports: [
    FileUploadModule,
    IonicPageModule.forChild(UploadPage),
  ],
  exports:[
    UploadPage
  ]
})
export class UploadPageModule {}

#12

is correct in production mode failed, with your correction everything is fine.
thanks!!!


#13

Now another problem …
it only works for me the first time, if I leave the page that uploads the files and I go back to make a new upload, it does not give errors but the selected files do not appear.


#14

I answer to myself, if I call the page with this.navCtrl.push it does not work, however if I call it with this.navCtrl.setRoot if it works