Picture of Marissa Preciado
Blockslider _isComplete
by Marissa Preciado - Friday, 3 January 2020, 6:51 PM
 

I'm trying to add a "complete" class to the tabs on blockslider when the corresponding blocks are completed. I'm using adapt-contrib-blockslider). I've added this to the js file:

  this.listenTo(Adapt.blocks, 'change:_isComplete', this.setComplete, this);

setComplete: function () {
this.$('.blockslider-tab.active').addClass('complete');
},

as well as an {{#if attributes._isComplete}} handler to the hbs file. It works except if more than one blockslider is active on a page, it then marks all the active tabs as complete. I'm totally new to js so if anyone can help with this, it would be greatly appreciated.

 

Picture of Matt Leathes
Re: Blockslider _isComplete
by Matt Leathes - Monday, 6 January 2020, 12:00 PM
 

it's this line that's the problem:

this.listenTo(Adapt.blocks, 'change:_isComplete', this.setComplete, this);

as it's saying 'any time ANY block's _isComplete attribute changes, execute this.setComplete'.

You need to be more specific than that; i.e. you only want to be listening to the _isComplete change event for blocks associated with the current instance of the blockSlider extension.

Luckily, it looks like there's already a function to give you that list - getAvailableBlocks - so you should be able to change the above code to:

this.listenTo(this.getAvailableBlocks(), 'change:_isComplete', this.setComplete);

Your setComplete function also makes the assumption that the block that's just been completed will always be the 'active' block - which probably will be the case the majority of the time but you should still try and avoid making assumptions like that as they can lead to unexpected behaviour (i.e. bugs!)

to be more specific and avoid that sort of problem, look up the _id of the block that just got set to completed and use that to construct a more specific selector:

setComplete: function(blockModel) {
  var blockId = blockModel.get('_id');
  this.$('.blockslider-tab-' + blockId).addClass('complete');
},

Hope this helps.

Picture of Marissa Preciado
Re: Blockslider _isComplete
by Marissa Preciado - Monday, 6 January 2020, 3:42 PM
 

Thank you for your reply. This is very helpful. I figured it was something like that going on but with my limited knowledge, I wasn't sure how to fix it. 

However, I'm having issues using:

this.listenTo(this.getAvailableBlocks(), 'change:_isComplete', this.setComplete);

It doesn't seem like setComplete ever fires. If I replace it with:

this.listenTo(Adapt.blocks, 'change:_isComplete', this.setComplete, this);

It seems to work and only the one tab gets marked complete. Would using this along with the _id lookup cause any potential bugs?

Picture of Matt Leathes
Re: Blockslider _isComplete
by Matt Leathes - Monday, 6 January 2020, 4:50 PM
 

Ah OK - probably because getAvailableBlocks returns an Array of Backbone Models and not a Backbone Collection. You could try either this:

this.listenTo(new Backbone.Collection(this.getAvailableBlocks()), 'change:_isComplete', this.setComplete);

or this:

this.getAvailableBlocks().forEach(function(block) {
  this.listenTo(block, 'change:_isComplete', this.setComplete);
}.bind(this));

Equally, if you modified setComplete to do the _id lookup, your suggested change would also work - it's just a bit less efficient as it'll run any time ANY block on the page completes - rather than just the blocks inside the current blockSlider.

Note that the final this in your suggested change is redundant. listenTo only takes three arguments, not four - so it should be written like so:

this.listenTo(Adapt.blocks, 'change:_isComplete', this.setComplete);

Not that leaving it in would cause any problems, it'll simply be ignored.

Picture of Marissa Preciado
Re: Blockslider _isComplete
by Marissa Preciado - Monday, 6 January 2020, 8:13 PM
 

Ah, That works perfectly! Thank you so much for your help!

Picture of Oliver Foster
Re: Blockslider _isComplete
by Oliver Foster - Wednesday, 8 January 2020, 11:35 AM
 

This is slightly tangential but possibly relevant.


There is another alternative to listening on a collection of block models or on an individual block model for the change:_isComplete event.

Example:

this.listenTo(Adapt.course, "bubble:change:_isComplete", this.onAnyModelComplete);


onAnyModelComplete: function(event) {

  // event.type === "bubble:change:_isComplete";

  // event.target === source adapt model;

  // event.value === _isComplete value true/false};

}

 

Both change:_isComplete and change:_isInteractionComplete bubble by default for all models which extend the AdaptModel.

References:


AdaptModel

ModelEvent

Bubbling Events

Bubbling Attachment

Bubbling Handlers