Progress binding is not updating while uploading

Hi all,

I have a very weird problem, I hope some of you know the answer to my problem. I am using FileTransfer to upload image from a collection of images. While uploading im trying to update my progress bar. Although the uploading is going as it should (see screenshot of the console.logs) it does not update the view with the progress percentage.

But get this, when I add a input bound to progress like this <ion-input [(ngModel)]="progress" type="text"></ion-input> and enter something in the input the bindings trigger and they update my progress bar.

Also when I add a function setProgress like this

setProgress = () : void => {
     this.progress = 90; 
}

and bind it to a button

<div class="buttons">
    <button dark (click)="setProgress ()" class="btn">
        setPercentage
    </button> 
</div>  

it also updates the proggress and the progressbar in my view. And the finished binding doesnt work either, it should show my button when it is done uploading.

I would love to get some help on this, its bugging the hell out of me.

My full .ts file here

import {Page, NavController, NavParams,} from 'ionic-angular';
import {Collection} from '../../models/Collection';
import {CollectionItem} from '../../models/CollectionItem';
import {Plugins} from '../../services/plugins.service';
import {Api} from '../../services/api.service';
import {Common} from '../../services/common.service';
import {CollectionListPage} from '../collection_list/collection_list';

@Page({
  templateUrl: 'build/pages/uploading/uploading.html',
})
export class UploadingPage {
    
    collection: Collection;
    uploading: boolean = false;
    progress: number = 0;
    finished: boolean = false;
    current: number = 1;
    total: number;
    collectionId: number;
    
    constructor(private nav: NavController, 
                private navParams: NavParams,
                private api: Api,
                private common: Common,
                private plugins: Plugins) {  
                    
        this.collection = this.navParams.get("collection"); 
        this.total = this.collection.items.length;        
        this.createCollection().then(response => {
            this.upload(this.collection.items[0]);
        }); 
    }
    
    done = () : void => {
        this.nav.setRoot(CollectionListPage);    
    }
    
    success = (result: any) : void => {          
        console.log("Uploading success", this.current);
        this.current++;      
        if(this.current <= this.total) {    
            console.log("Uploading next one: ", this.current);        
            this.progress = 0;
            this.upload(this.collection.items[this.current - 1]);
        } else {
            console.log("Upload finished: ", this.current);   
            this.finished = true;
        }
    }
            
    failed = (err: any) : void => {
        alert(err);
        console.log(err);
        console.log(err.error);
    }
    
    onProgress = (progressEvent: ProgressEvent) : void => {
        if (progressEvent.lengthComputable) {
            this.progress = Math.round((progressEvent.loaded / progressEvent.total) * 100);
            console.log("Progress: ", this.progress);
        }
    }
    
    createCollection = () : Promise<void> => {
        return this.api.create.collection(this.collection).then(id =>  { 
                this.collectionId = id;
            }
        );
    }
    
    upload = (item: CollectionItem) : void => {
        item.collectionId = this.collectionId;
        
            var ft = new FileTransfer();            
            var filename = "image_" + this.current + ".jpg" ;            
            var options = new FileUploadOptions();
            
            options.fileKey = "file";
            options.fileName = filename;
            options.mimeType = "image/jpeg"
            options.chunkedMode = false;
            options.headers = { 
                'Content-Type' : undefined
            }
            options.params = {
                collectionId: item.collectionId,
                type: item.type,
                fileName: filename
            };     
            
            ft.onprogress = this.onProgress;  
            ft.upload(item.imageUrl, "http://api.com/collections/save", this.success, this.failed, options);   
    }
}

My full view here:

<ion-content class="uploading"> 
    <h3>Image{{current}} of{{total}}</h3>
    <progress id="progressbar" max="100" value="{{ progress }}"> </progress>
    <div id="progressbarlabel">{{ progress }} %</div>        
    <div class="buttons">
        <button [hidden]="!finished" dark (click)="done()" class="upload-button">
            Done
        </button> 
    </div>  
</ion-content>
4 Likes

Hmm :thinking:

So <div id="progressbarlabel">{{ progress }} %</div> is updating?

Shot in the dark, have you tried this

 <progress id="progressbar" max="100" [value]="progress"> </progress>

Since progress is a dynamic value, using the [] notation tells angular that this value needs to be updated.

Hi, thanks for your reply.

Sadly, your suggestion doesnt work either.

No, nothing is updating while uploading. Not the {{progress}}, not the {{total}}, nothing, even the [hidden]="!finished" does not update.

Like mentioned, when I add <ion-input [(ngModel)]="progress" type="text"></ion-input> to the view. And then change the value of progress through the input, {{progress}} does update. And when I add a function that updates progress like this.progress = 90 and bind it to a button it also updates. But not when its uploading :frowning:

progress should have a value. I am logging this.process through the line console.log("Progress: ", this.progress); in the onProgress function. See the screenshot in my OP for the results.

Maybe you could provide a minimal demo for me to look at?

Or even a small git repo ?

Since it requires cordova I guess a code pen will not work. So I went ahead and created a repo for you to check.

REPO => deleted

Just take some pictures with the camera and press start uploading. The view does not update, but if you check the console with chrome inspect the logs show progress is being made. The images will be uploaded to a test API of mine so dont worry about that. Iā€™ve tested it on an android samsung galaxy S7 edge with android 6.0.1.

I hope you can help me because this shit is messing me up :cry:

Hmm, so what Iā€™m guess is that the file transfer happens outside of angularā€™s zones, so it doesnā€™t know how to update the bindings.

At the moment, the file transfer plugin isnā€™t in ionic-native, but thereā€™s an issue open for it.

You could wait for that to get added, or you make a PR with the wrapper in place.

Hmm. Any idea when ā€˜soonā€™ is? :slight_smile: Days? Weeks? Months?

I guess that most probably the problem that youā€™re observing is caused by this:

however you might also check if something like this will work:

            ft.onprogress = e => this.onProgress(e);

I already had a similar problem and it was caused by the fact that the value of this inside the handler was set to the global Window object instead of the object itself.

@iignatov lā€™ll try that tonight when I come home. Am I correct if I say the e is an event object? If so what event is it?

If you look at my onProgress function, it already recieves an event.

onProgress = (progressEvent: ProgressEvent) : void => {
        if (progressEvent.lengthComputable) {
            this.progress = Math.round((progressEvent.loaded / progressEvent.total) * 100);
            console.log("Progress: ", this.progress);
        }
    }

@dtaalbers Yes, e is the event object. Itā€™s of type ProgressEvent. You can also write it this way:

            ft.onprogress = (e: ProgressEvent) => this.onProgress(e);

I get it thanks :slight_smile:

fyi; I donā€™t have the problem of a missing event. My current code as in

onProgress = (progressEvent: ProgressEvent) : void => { ... }

has an event there. I have no problem accesssing it.

Could your suggestion give the current scope (as in this) to the onProgress function? Because that sounds nice :stuck_out_tongue:

If the issue that youā€™re observing is caused by the (unexpected) value of this then the change that I suggested might resolve it. I created a simple (maybe too oversimplified) test to illustrate this. You can check it out in this demo environment.

Sadly I canā€™t get it work. Probably have to wait until this plugin goes ionic native. For now I used filthy jquery to update the DOM elements.

Thanks for the help boys!

if you need update DOM you can use native js. No need to clutter up the code.
However, you can read about zone.js with Angular 2 for the DOM forcing update. Google it.

1 Like

Thank you for the hint. Iā€™ve managed to get it to work without JQuery and or native js. Iā€™ve updated my example (with the ionic-native wrapper) here :slight_smile:

1 Like

Hi all, i solved case with progress bar for Transfer.upload, with synchronouse upload of lots of files and for each file own progressbar.

Some structure for files to upload:
export interface processedFile {
path?:string, //dir of files lib
size?:number, //size of file
file?:string,
progress?:string, //% of upload
done?:boolean //flag with responce from server about md5 checksum is ok
}
export interface processedFiles extends Array{} //array of files to upload

public processedFiles:processedFiles = [] //array of files to upload

this.uploadTo(this.processedFiles[i], userData) //start to upload file of files array

onProgress(item, progress) { //item - here will be item of files array and progress digit
    item.progress = progress; //
}

uploadTo(item:processedFile, userData: AccountData) {
    return new Promise((resolve, reject) => {
        let fdata: Url = {file:item.file, path:item.path}; // url model
        let fileTransfer = new Transfer();
        fileTransfer.onProgress((progressEvent: any ) => {
            if (progressEvent.lengthComputable) {
                let progress = Math.round((progressEvent.loaded / progressEvent.total) * 100);
                this.onProgress(item, progress) //**here trick, which allow progress to get visibility**
            }
        });
        fileTransfer.upload(this.ops.getFilenameFromURL(fdata), this.vars.premoveexpertpl, {
            fileName: fdata.file,
            params: {f: "upload", u: userData.username, p: userData.password}
        }, true).then((ret) => {
            let res: FileUploadResult = <FileUploadResult>ret;
            let url:Url = this.ops.getFileNameFromPath(res.headers['file']);

//ā€¦some not interesting code

        }, (err) => {

//ā€¦some not interesting code
})
})
}

I fixed this by surrounding my progress function with zone.run:

import {NgZone} from '@angular/core';

  constructor(
    ...
    public _zone: NgZone) {
  }
      fileTransfer.onProgress((e) => {
        this._zone.run(() =>             
            myObj.uploadPercentage = (e.lengthComputable) ?  Math.floor(e.loaded / e.total * 100) : -1;          
        });
      });
6 Likes

Thanks, this works for me

Why onProgress not show in IOS ? :worried:

For anyone interested, I have a full upload example in this example app. You can download the repo and try it on your own phone!

1 Like