This is very much related to my first question about nousliders here: How to update div in Meteor without helper? (this title was not well chosen, because it was not about avoiding helpers)
The answer provided by Jankapunkt works very well, for example I can have 4 sliders, and reordering works without loosing slider states like min/max like this:
now I want some of the elements to be non-sliders, for example change 1 to a dropdown:
but when I click the switch button, 1 slider dissapears (the one that moves to the dropdown spot), and I get an error in console:
Exception in template helper: Error: noUiSlider (11.1.0): create requires a single element, got: undefined
I don't understand why adding if/else makes any difference ... the slider helper is waiting for ready {{#if ready}}...{{/if}} so it should work ? anyone understand why it doesn't ? and how to fix it ?
template onCreated looks now like this:
Template.MyTemplate.onCreated(function() {
const sliders = [{
id: 'slider-a',
prio: 1,
type: "NumberRange",
options: {
start: [0, 100],
range: {
'min': [0],
'max': [100]
},
connect: true
}
}, {
id: 'slider-b',
prio: 2,
type: "NumberRange",
options: {
start: [0, 100],
range: {
'min': [0],
'max': [100]
},
connect: true
}
}, {
id: 'dropdown-c',
prio: 3,
type: "Dropdown"
}, {
id: 'slider-d',
prio: 4,
type: "NumberRange",
options: {
start: [0, 100],
range: {
'min': [0],
'max': [100]
},
connect: true
}
}, ]
const instance = this
instance.state = new ReactiveDict()
instance.state.set('values', {}) // mapping values by sliderId
instance.state.set('sliders', sliders)
})
and template now looks like this, there is an if else statement to show Dropdown or NumberRange (slider):
<template name="MyTemplate">
{{#each sliders}}
<div class="range">
<div>
<span>id:</span>
<span>{{this.id}}</span>
</div>
{{#if $eq type 'Dropdown'}}
<select id="{{this.id}}" style="width: 200px;">
<option value="">a</option>
<option value="">b</option>
<option value="">c</option>
</select>
{{else if $eq type 'NumberRange'}}
<div id="{{this.id}}">
{{#if ready}}{{slider this}}{{/if}}
</div>
{{/if}}
{{#with values this.id}}
<div>
<span>values: </span>
<span>{{this}}</span>
</div>
{{/with}}
</div>
{{/each}}
<button class="test">Switch Sliders</button>
</template>
First of all you should be aware of the situation:
noUiSlider
on the dropdown when hitting the switch button, causing the error you described above.Therefore I want to give you some advice on restructuring your code first.
1. Renaming variables
You can use your IDE's refactoring functionality to easily rename all your variable names. If you don't have such functionality in your IDE / editor I highly suggest you to start your search engine to get one.
Since you have more input types than sliders, you should use a more generic name, like
inputs
which indicates a broader range of possible types.There should also be a
value
entry on your dropdown, do be able to restore the last selection state when re-rendering:Now you also have to rename your helpers and the template helper calls:
2. Handle multiple input types on switch event
You also should rename your variables in the switch event. Furhtermore, you need to handle different input types here. Dropdowns have no
.noUiSlider
property and they also receive not an array but a string variable as value:3. Correct rendering / update display list
Now comes the problem of mixing Blaze rendering with classic DOM updates: until this point you will run into an error. This is mainly because now our
createSliders
function will expect adiv
element with a certain id at the place where the dropdown has been rendered before the switch has been pressed. It won't be there because the Blaze render invalidation will not be finished at this point.Fixing this using
autorun
inonCreated
oronRendered
will easily increase complexity or even messes up your code. A simpler solution is to use a short timeout here:4. Bonus: saving state of dropdown
In order to save the state of the dropdown, you need to hook into it's
change
event. You therefore need to assign it a class to map the event independently of theid
:For which now you can create an event:
Now you have saved your current dropdown value but in order to restore it in the next render, you need to extend the
options
in the html:This should now also display the last selected state of the dropdown.
Summary
setTimeout
solution should be the last approach to be used when other approaches are even less feasible.