Skip to content Skip to sidebar Skip to footer

How To Update Div In Meteor Without Helper?

I can I update a div using JavaScript with no problem. For example, the initial values for both divs are shown on the image on the left, and the values after clicking on the test b

Solution 1:

With Blaze you have to explicitly import the .css file as well in order to get the styles applied:

// no slider without cssimport'nouislider/distribute/nouislider.css'import noUiSlider from'nouislider'

Then you can easily use the Template's builtin jQuery to get a target div where nouislider will render.

Consider the following template:

<template name="MyTemplate">
    <div><divid="range"></div></div>
    {{#if values}}
        <div>
            <span>values: </span><span>{{values}}</span>
        </div>
    {{/if}}
    <button class="test">ShowSlider</button>
</template>

Now let's render a new nouislider into the div with id range by clicking the button:

Template.MyTemplate.events({
  'click .test': function (event, templateInstance) {
    createSliders(templateInstance)
  },
})

functioncreateSliders (templateInstance) {
  // get the target using the template's jQueryconst range = templateInstance.$('#range').get(0)
  noUiSlider.create(range, {
    start: [0, 100],
    range: {
      'min': [0],
      'max': [100]
    },
    connect: true
  })
}

Now you could also easily add some reactive data here (but avoid Session):

Template.MyTemplate.onCreated(function () {
  const instance = this
  instance.state = newReactiveDict()
  instance.state.set('values', null)
})

... and connect it with some data. The noUiSlider allows you to hook into it's update event, from where you can pass the values into the state:

functioncreateSliders (templateInstance) {
  // get slider, render slider...// ...
  range.noUiSlider.on('update', function (values, handle) {
    // update values, for instance use a reactive dict
    templateInstance.state.set('values', values)
  })
}

Render the value into the template using a helper:

Template.MyTemplate.helpers({
  values () {
    returnTemplate.instance().state.get('values')
  }
})

Import your own .css files to statically style the slider like so:

#range {
  width: 300px;
  margin: 14px;
}

or style it dynamically using jQuery's css.


UPDATE: Correct rendering on updated display list

The issue you described is correct and can be reproduced. However it can also be prevented, using Template.onRendered to control the point when rendering may occur.

I extended the Template to the following code:

<templatename="MyTemplate">
    {{#each sliders}}
        <divclass="range"><div><span>id:</span><span>{{this.id}}</span></div><divid="{{this.id}}">
                {{#if ready}}{{slider this}}{{/if}}
            </div>
            {{#with values this.id}}
                <div><span>values: </span><span>{{this}}</span></div>
            {{/with}}
        </div>
    {{/each}}

    <buttonclass="test">Switch Sliders</button></template>

Now look inside the target div, which previously had only an id assigned. Now there is a {{#if ready}} flag and a function, call to {{slider this}}.

Now in order to render only when the DOM has initially been rendered, we need Template.onRendered:

Template.MyTemplate.onRendered(function () {
  const instance = this
  instance.state.set('ready', true)
})

and add it to the (updated) helpers:

Template.MyTemplate.helpers({
  sliders () {
    return Template.instance().state.get('sliders')
  },
  values (sliderId) {
    return Template.instance().state.get('values')[sliderId]
  },
  slider (source) {
    createSliders(source.id, source.options, Template.instance())
  },
  ready() {
    return Template.instance().state.get('ready')
  }
})

Now we have some more issues here that need to be resolved. We only want to render if the switch changes but not if the values update. But we need the latest values in order to re-assign them as start position in the next render (otherwise the sliders would be set with the start values 0,100).

To do that we change the onCreated code a bit:

Template.MyTemplate.onCreated(function () {

  // initial slider statesconst sliders = [{
    id: 'slider-a',
    options: {
      start: [0, 100],
      range: {
        'min': [0],
        'max': [100]
      },
      connect: true
    }
  }, {
    id: 'slider-b',
    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)
})

Now if we press the switch button want a) delete all current sliders with their events etc. and b) update the sliders data to our new (reversed) state:

Template.MyTemplate.events({
  'click .test': function (event, templateInstance) {

    let sliders = templateInstance.state.get('sliders')
    const values = templateInstance.state.get('values')

    // remove current rendered sliders// and their events / prevent memory leak
    sliders.forEach(slider => {
      const target = templateInstance.$(`#${slider.id}`).get(0)
      if (target && target.noUiSlider) {
        target.noUiSlider.off()
        target.noUiSlider.destroy()
      }
    })

    // assign current values as// start values for the next newly rendered// sliders
    sliders = sliders.map(slider => {
      const currentValues = values[slider.id]
      if (currentValues) {
        slider.options.start = currentValues.map(n =>Number(n))
      }
      return slider
    }).reverse()

    templateInstance.state.set('sliders', sliders)
  }
})

Because we have multiple slider values to be updated separately, we also need to change some code in the createSlider function:

functioncreateSliders (sliderId, options, templateInstance) {
  const$target = $(`#${sliderId}`)const target = $target.get(0)

  //skip if slider is already in targetif ($target.hasClass('noUi-target')) {
    return
  }

  noUiSlider.create(target, options)

  target.noUiSlider.on('update', function (values, handle) {
    // update values by sliderIdconst valuesObj = templateInstance.state.get('values')
    valuesObj[sliderId] = values
    templateInstance.state.set('values', valuesObj)
  })
}

By using this approach you some advantages and some disadvantages.

  • (+) pattern can be used for many similar problems
  • (+) no autorun required
  • (+) separated slider state from value state
  • (-) rendering can occur more than once for a re-render, users wont notice but it wastes resources, which can be an issue on mobile.
  • (-) can become overly complex on larger templates. Encapsulation of Templates is very important here.

Post a Comment for "How To Update Div In Meteor Without Helper?"