Reputation: 1001
This is most probably a trivial problem. In my javascript I have a function that does a lot of actions and I want to update the view with the progress. I have a self.message = ko.observable()
which I continuously update during the function execution.
This is bound to a div
element on my view. I would expect the view to be updated every time the ko.observable
is updated.
However, this does not happen. It only shows the last value of "message" when the function has completed execution.
How do I show progress update message using knockoutjs
?
The code is as follows:
in js file
self.message = ko.observable("");
self.myfunction = function()
{
self.message("start function");
....
self.message("end function");
}
in the view
<div data-bind="text: vm.message"></div>
Thanks Martin
Upvotes: 1
Views: 345
Reputation: 3000
The reason you're only seein the last value of self.message, is that your long-running JavaScript function probably executes synchronously. Your web app is working in a single thread, so the UI won't update until your JavaScript is done executing.
It seems from your description that your long running task is done in steps already, since in between you have moments where you (try to) update the UI. The easiest trick to give the UI a chance to redraw is to use setTimeout with a timeout of 1 ms. This will give the UI a chance to catch up.
Here's some ugly example code:
self.myfunction = function()
{
self.message("start function");
setTimeout(function () {
subTask1();
self.message("subtask1 complete");
setTimeout(function () {
subTask2();
self.message("subtask2 complete");
setTimeout(function () {
subTask3();
self.message("function complete");
}, 1);
}, 1);
}, 1);
}
etc. You get the idea. You probably want to refactor this away into helper functions, and even better, leverage promises to chain the code. The whole point is that you need to create time in between your heavy-load task to update the UI.
Here's some untested code that will run an array of tasks in batches. You can pass the amount of tasks you want to run in each batch. I haven't tested the code, but it should be pretty accurate. I've set up a few observables to use in the UI. the observable 'progress' will show a percentage, which you can use for a progress bar, for example.
var done = ko.observable(0),
total = ko.observable(0),
progress = ko.computed(function () {
return Math.round(done() / total()) + '%';
});
function executeBatch(tasks, amount, startAt) {
// If startAt is not set, start at the first task
startAt = startAt || 0;
// Stop condition: all tasks are done
if (tasks.length - 1 < startAt) {
return;
}
// Execute the amount of tasks you want to run in 1 batch, or less if there aren't enough tasks left.
for (var i = 0; i < Math.min(startAt + amount, tasks.length); i++) {
doTask(tasks[i]);
}
// Update the UI
done(startAt + amount);
// Give the UI time to update, then recursively call this function to continue executing tasks
setTimeout(function () {
executeBatch(tasks, amount, startAt + amount);
}, 1);
}
function executeAllTasks(tasks) {
// Reset the observables
done(0);
total(tasks.length);
// Start doing the work, 10 tasks at a time
executeBatch(tasks, 10);
}
Upvotes: 1