Reputation: 615
I am performing some actions at the start of my ember app and want to show a twitter bootstrap progress bar while each step completes.
The problem is that I think all of the calls to update the progress bar are being bounced to one call at the end of the run loop.
The progress bar only gets updated to 100% when everything has finished.
I do not know enough about the ember run loop to know how to update the bar at each step.
Do I use Ember.run.next() ??
EDIT:
So I am updating the progress bar with:
$('#api_progress_bar .bar').width("#{amount}%")
and amount gets passed into the function
The steps that are taken in between the above line are basically creating some seed data Ember models, nothing out of the ordinary.
But what is happening is that I think all the calls to set width are being bounced down to one call...
I have noticed before that css mods like this sometimes do not work until control is passed back to the system...
EDIT 2: I have added a jsbin to illustrate the problem. What happens is that the view only gets updated when all the code in the action completes, which is not what I want. I want to update the progress bar in discreet steps.
Anyone any ideas how to do this?
Upvotes: 3
Views: 1691
Reputation: 606
Take a look at:
github: https://github.com/ember-addons/bootstrap-for-ember demo: http://ember-addons.github.io/bootstrap-for-ember
It supports bootstrap progress bar component that can be easily bound to the controller without any wiring code required.
Upvotes: 2
Reputation: 5075
This may depend on how fast the progress tasks are completing. I am currently doing this in a ProgressView
using a corresponding model with a progress
property that is updated.
When the model changes the progress bar updates as the view is bound to this model. Here's how I am doing this. The actual value in the model is updated with events from other pieces, here a setInterval is used.
The this.set('progress')
calls are collapsed into a single call with the last value set by the Ember runloop. This is an important performance optimization, that helps prevents bindings from firing only once for a given property per runloop.
To force the progress
calls to be queued you would need to wrap it in some sort of interval, or use Ember.run.scheduleOnce
. ie:- increment a counter of steps immediately, then count down slower with the interval or scheduleOnce
.
Here's an example ProgressStepper
. You specify the totalSteps
at instantiation time, and call next
when a step completes. The progress bar can be bound to it's progress
property. It uses scheduleOnce
, you can tick slower with setInterval of say 100ms.
App.ProgressStepper = Em.Object.extend({
totalSteps: 10,
currentStep: 0,
progress: 0,
complete: Ember.K(),
pending: 0,
isOnRunloop: false,
next: function() {
this.set('pending', this.get('pending') + 1);
if (!this.get('isOnRunLoop')) {
this.set('isOnRunLoop', true);
this.enqueue();
}
},
enqueue: function() {
// ticking on the runloop gives better performance
// as it doesn't use additional timers
Ember.run.scheduleOnce('afterRender', this, 'onNext');
// use this is if you want to tick slower
//setTimeout(this.onNext.bind(this), 100);
},
onNext: function() {
this.nextStep();
if (this.hasPending()) {
this.enqueue();
} else {
this.set('isOnRunLoop', false);
}
},
hasPending: function() {
return this.get('pending') > 0;
},
nextStep: function() {
this.set('pending', this.get('pending') - 1);
var currentStep = this.get('currentStep');
var totalSteps = this.get('totalSteps');
currentStep++;
if (currentStep <= totalSteps) {
this.set('currentStep', currentStep);
this.updateProgress();
if (currentStep == totalSteps) {
Ember.run.scheduleOnce('afterRender', this, 'complete');
}
}
},
updateProgress: function() {
var progress = this.get('currentStep') / this.get('totalSteps') * 100;
this.set('progress', progress);
}
});
Here's an updated jsbin.
Upvotes: 2