l33z3r
l33z3r

Reputation: 615

Ember Run Loop and Bootstrap Progress Bar

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

Answers (2)

asaf000
asaf000

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

Darshan Sawardekar
Darshan Sawardekar

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.

Edit: after question updated with jsbin

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

Related Questions