在某个事件中移动它们时,nouisliders会消失 [英] nouisliders dissapear when moving them on some event

查看:98
本文介绍了在某个事件中移动它们时,nouisliders会消失的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这与我在这里有关nousliders的第一个问题非常相关: (没有很好的选择这个标题的原因,因为它与避免使用助手无关)如何在Meteor中更新div?

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)

Jankapunkt提供的答案非常有效,例如,我可以有4个滑块,并且重新排序的工作不会丢失像这样的最小/最大滑块状态:

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:

现在我希望某些元素不滑动,例如将1更改为下拉列表:

now I want some of the elements to be non-sliders, for example change 1 to a dropdown:

但是,当我单击切换按钮时,消失了1个滑块(移动到下拉点的那个),并且在控制台中出现错误:

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

我不明白为什么添加if/else会有什么不同...滑块助手正在等待{{#if ready}} ... {{/if}}准备就绪,因此应该可以正常工作吗?有人知道为什么不这样做吗?以及如何解决?

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现在看起来像这样:

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)
})

模板现在看起来像这样,有一个if else语句来显示Dropdown或NumberRange(滑块):

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:

  • 您正在将经典ui渲染(滑块)与Blaze渲染(下拉列表)混合在一起.这将给您带来很多设计问题,并且下面的解决方案更多是使用Blaze API而不是干净的方法来解决问题.
  • 由于组件不再只是滑块,因此您应该重命名变量.否则,外国人很难解码变量的上下文.
  • 您的下拉列表当前未保存任何值,切换按钮将重置下拉列表.
  • 按下开关按钮时,您无法破坏下拉菜单中的noUiSlider,从而导致上述错误.
  • You are mixing classic ui rendering (the sliders) with Blaze rendering (the dropdown). This will bring you a lot of design problems and the solution below is more a hack than a clean appraoch using Blaze's API
  • Since your components are not only sliders anymore, you should rename your variables. Otherwise it is hard for a foreign person to decode your variable's context.
  • Your dropdown has currently no values saved, switching the button resets the dropdown.
  • You can't destroy a 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.

您可以使用IDE的重构功能轻松重命名所有变量名.如果您的IDE/编辑器中没有这种功能,我强烈建议您启动搜索引擎以获取一个功能.

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.

由于输入类型多于滑块,因此应使用更通用的名称,例如inputs,它表示可能的类型范围更广.

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.

下拉菜单中还应该包含一个value条目,以便在重新渲染时能够恢复上一个选择状态:

There should also be a value entry on your dropdown, do be able to restore the last selection state when re-rendering:

Template.MyTemplate.onCreated(function () {

  const inputs = [{
    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',
    value: '', // default none
  }, {
    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('inputs', inputs)
})

现在,您还必须重命名您的助手和模板助手调用:

Now you also have to rename your helpers and the template helper calls:

<template name="MyTemplate">
    {{#each inputs}}
    ...
    {{/each}}
</template>

Template.MyTemplate.helpers({
  inputs () {
    return Template.instance().state.get('inputs')
  },
  ...
})

2.处理切换事件时的多种输入类型

您还应该在switch事件中重命名变量.此外,您需要在此处处理不同的输入类型.下拉列表没有.noUiSlider属性,并且它们也不接收数组,而是一个字符串变量作为值:

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:

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

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

  // remove current rendered inputs
  // and their events / prevent memory leak
  inputs.forEach(input => {

    if (input.type === 'Dropdown') {
      // nothing to manually remove
      // Blaze handles this for you
      return
    }

    if (input.type === 'NumberRange') {
      const target = templateInstance.$(`#${input.id}`).get(0)
      if (target && target.noUiSlider) {
        target.noUiSlider.off()
        target.noUiSlider.destroy()
      }
    }
  })

  // assign current values as
  // start values for the next newly rendered
  // inputs
  inputs = inputs.map(input => {
    const currentValues = values[input.id]

    if (!currentValues) {
      return input
    }

    if (input.type === 'Dropdown') {
      input.value = currentValues
    }

    if (input.type === 'NumberRange') {
      input.options.start = currentValues.map(n => Number(n))
    }

    return input
  }).reverse()

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

3.正确渲染/更新显示列表

现在出现了将Blaze渲染与经典DOM更新混合在一起的问题:到现在为止,您将遇到错误.这主要是因为 now 我们的createSliders函数将在按下开关之前 处出现具有特定ID的div元素.它不会在那里,因为此时Blaze渲染无效将无法完成.

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 a div 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.

使用onCreatedonRendered中的autorun修复此问题将很容易增加复杂性,甚至使您的代码混乱.一个更简单的解决方案是在此处使用较短的超时时间:

Fixing this using autorun in onCreated or onRendered will easily increase complexity or even messes up your code. A simpler solution is to use a short timeout here:

Template.MyTemplate.helpers({
  // ...
  slider (source) {
    const instance = Template.instance()
    setTimeout(()=> {
      createSliders(source.id, source.options, instance)
    }, 50)
  },
  // ...
})

4.奖励:下拉菜单的保存状态

为了保存下拉菜单的状态,您需要挂钩到它的change事件.因此,您需要为它分配一个类来独立于id:

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 the id:

 <select id="{{this.id}}" class="dropdown" style="width: 200px;">...</select>

您现在可以为其创建事件:

For which now you can create an event:

'change .dropdown'(event, templateInstance) {
  const $target = templateInstance.$(event.currentTarget)
  const value = $target.val()
  const targetId = $target.attr('id')
  const valuesObj = templateInstance.state.get('values')
  valuesObj[targetId] = value
  templateInstance.state.set('values', valuesObj)
}

现在,您已经保存了当前的下拉值,但是为了在下一个渲染中恢复它,您需要在html中扩展options:

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:

<select id="{{this.id}}" class="dropdown" style="width: 200px;">
    <option value="a" selected="{{#if $eq this.value 'a'}}selected{{/if}}">a</option>
    <option value="b" selected="{{#if $eq this.value 'b'}}selected{{/if}}">b</option>
    <option value="c" selected="{{#if $eq this.value 'c'}}selected{{/if}}">c</option>
</select>

这现在还应该显示下拉菜单的最后选择状态.

This should now also display the last selected state of the dropdown.

  • 您可以使用此模式来包含更多输入组件.
  • 请注意,将Blaze渲染与传统DOM操作混合使用会大大增加代码的复杂性.同样适用于许多其他渲染系统/库/框架.
  • 当其他方法不太可行时,setTimeout解决方案应该是最后使用的方法.
  • 变量和方法命名应始终代表其上下文.如果上下文更改->重命名/重构变量和方法.
  • 请下次再次在此处发布完整的代码.您的其他帖子可能会被更新或删除,并且此处不再提供完整的代码,其他人很难找到一个好的解决方案
  • You can use this pattern to include even more input components.
  • Be aware, that mixing Blaze rendering with traditional DOM manipulations can increase the complexity of your code a lot. Same applies for many other rendering systems / libraries / frameworks out there.
  • The setTimeout solution should be the last approach to be used when other approaches are even less feasible.
  • Variable and method naming should always represent their context. If the context changes -> rename / refactor variables and methods.
  • Please next time post the full code here again. Your other post might get updated or deleted and the full code won't be accessible here anymore, making it hard for others to find a good solution.

这篇关于在某个事件中移动它们时,nouisliders会消失的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆