Issue
I'm building a mobile app using ionic and angular. I created a service to delegate the download of a file. This service creates an observable wich is observed by my component when a file is being downloaded. The component receives the events as expected, I can see the data in the console, but the template view does not update until the download is finished. Strangely enough if I try to toggle the app menu the view gets updates.
Here is the relevant code:
SERVICE
import {Injectable} from '@angular/core';
import {FileTransfer, FileTransferObject} from "@ionic-native/file-transfer";
import {BehaviorSubject} from "rxjs/BehaviorSubject";
@Injectable()
export class DataRetriever {
protected fileTransfer: FileTransferObject = this.transfer.create();
protected _progressSource = new BehaviorSubject<number>(0);
protected progressStep = 0;
progress$ = this._progressSource.asObservable();
/**
* Trigger the progress observable
* @param {ProgressEvent} progressEvent
*/
listener(progressEvent:ProgressEvent) {
if (progressEvent.lengthComputable) {
let progress = Math.round((progressEvent.loaded / progressEvent.total)*100);
if(progress > this.progressStep){
console.log("Download progress " + progress + "%");
this.progressStep++;
this._progressSource.next(progress);
}
} else {
this._progressSource.next(1);
}
}
/**
* Perform sync data download
* @returns {Promise<boolean>}
*/
public doSync() {
this.fileTransfer.onProgress((event) => this.listener(event));
return this.getLastUpdate().then(updated_at => {
if(updated_at === 0){
let filename = 'first_sync.zip';
return this.downloadFirstSync().then(()=>{
return this.file.checkFile(this.file.dataDirectory,filename);
}).then((exists) => {
console.log("Success? " + (exists ? "Y" : "N"));
}).catch(error=>{
console.log(JSON.stringify(error,null,2))
});
}
});
}
/**
* Download first sync file
*/
downloadFirstSync() {
const url = this.api.getApiURL('first_sync');
let filename = 'first_sync.zip';
return this.fileTransfer
.download(url, this.file.dataDirectory + filename,true,Api.getApiHeader(this.token))
.then((entry) => {
console.log('Download complete: ' + entry.toURL());
return Promise.resolve();
}).catch(error => {
console.log(JSON.stringify(error,null,2));
return Promise.reject(error);
});
}
}
COMPONENT
import {Component} from '@angular/core';
import {DataRetriever} from "../../providers/data-retriever/data-retriever";
import {Subscription} from "rxjs/Subscription";
@Component({
selector: 'page-data-retrieve',
templateUrl: 'data-retrieve.html'
})
export class DataRetrievePage {
public updated_at:number = 0;
public progressStatus = 0;
protected progressSubscription: Subscription;
constructor(public navCtrl: NavController,
public dataRetriever:DataRetriever) {
}
ionViewDidLoad() {
this.progressSubscription = this.dataRetriever.progress$.subscribe(
amount => {
console.log('Update received ' + amount);
this.progressStatus = amount;
},error => {
console.log(error);
});
this.dataRetriever.getLastUpdate().then(result=>{
this.updated_at = result;
});
console.log('ionViewDidLoad DataRetrievePage');
}
doSync(){
this.dataRetriever.doSync();
}
ngOnDestroy() {
this.progressSubscription.unsubscribe();
}
}
LOG From the console while "doSync" is running:
Download progress 1% data-retrieve.ts:34
Update recebido 1 data-retriever.ts:59
Download progress 2% data-retrieve.ts:34
Update recebido 2 data-retriever.ts:59
Download progress 3% data-retrieve.ts:34
Update recebido 3 data-retriever.ts:59
Download progress 4% data-retrieve.ts:34
VIEW
<progress-bar [progress]="progressStatus"></progress-bar>
<div class="progress-outer">
<div class="progress-inner" [style.width]="progress + '%'">
{{ progress }}%
</div>
</div>
Solution
After a lot of research and lost hair I've came upon a solution.
The main reason for the issue seens to be that Angular watches for user events and refresh views when needed. If the event is system generated, views are not update. That's why you need to inform angular that a refresh is needed.
So I added the following code to my component:
this.downloadProgressSubscription = this.dataRetriever.progress$.subscribe(
(info:ProgressContainer) => {
console.log(JSON.stringify(info,null,2));
this.downloadProgress = info;
this.ref.detectChanges(); // <- Relevant line
},error => {
console.log(error);
});
Answered By - Arthur Samarcos
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.