【问题标题】:How to update div in Meteor without helper?如何在没有帮助的情况下更新 Meteor 中的 div?
【发布时间】:2018-09-11 20:38:55
【问题描述】:

我可以毫无问题地使用 JavaScript 更新 div。例如,两个div的初始值显示在左边的图像中,点击测试按钮后的值显示在右边。

当我单击测试按钮时,计数器会增加 1,并且 div 的值会更改如下:

var ela = document.getElementById("a");
var elb = document.getElementById("b");
$("#" + ela.id).html("new value " + ela.id + " ");
$("#" + elb.id).html("new value " + elb.id + " ");

到目前为止一切顺利,但现在我想更改 div 的顺序:当计数器是偶数时,我希望 div a = 黄色位于顶部(div b = 底部为灰色),另一个计数器不均匀时的方法。

为了简单起见,我为每个可能的订单 (a,b) 和 (b,a) 使用了 2 个小数组,并且助手“订单”将根据计数器值 (x %2 是否为零)

不幸的是,结果不是我想要的和期望的。 div 正确地改变了位置,但它们的文本不是。第一次单击后,计数器从 5 变为 6,因此黄色 div 从下到上移动,但 div 内的文本是错误的,我希望黄色 div 中的“新值 a”向上移动,但我得到了'new value b a' 代替(其他 div 也一样)

另外,当我检查控制台输出中的 div 时,我看到了奇怪的结果,Meteor 似乎对 div id 感到困惑?看下图,第一个div既是a又是b,同时是黄色和灰色……

有人知道这是为什么吗?我该如何解决?

我知道我可以使用助手在正确的 div 中获取正确的文本。但我的最终目标不是更改 div 中的文本,而是我想使用 nouislider 创建一个滑块,如下所示:

var noUiSlider = require('nouislider');
var sliderHtmlElement = document.getElementById("a");
var options = {
    start: [0, 100],
    range: {
        'min': [0],
        'max': [100]
    }
};
noUiSlider.create(sliderHtmlElement, options);

我的完整测试代码:

<template name="MyTemplate">
	{{x}}
	<br>
	<br>
	{{#each order}}
		<div class="{{label}}" id="{{label}}" 
        style="background-color: {{color}};">
            start value {{label}}
        </div>
		<br>
	{{/each}}
	<button class="test">test</button>
</template>

var order1;
var order2;

Template.MyTemplate.onCreated(function() {
	Session.set("x", 5);
	
	var or0 = [];
	or0["label"] = "a";
	or0["color"] = "yellow";

	var or1 = [];
	or1["label"] = "b";
	or1["color"] = "grey";

	order1 = [];
	order1[0] = or0;
	order1[1] = or1;
	
	order2 = [];
	order2[0] = or1;
	order2[1] = or0;
});

Template.MyTemplate.events({
	'click .test': function(event) {
		var varx = Session.get("x") + 1;
		Session.set("x", varx);
		createSliders();
	}
});

Template.MyTemplate.helpers({
	x: function() {
		return Session.get("x");
	},
	order: function() {
		if (Session.get("x") % 2 === 0) {
			return order1;
		} else {
			return order2;
		}
	}
});

function createSliders() {
	var ela = document.getElementById("a");
	var elb = document.getElementById("b");
	console.log(ela);
	console.log(elb);
	$("#" + ela.id).html("new value " + ela.id + " ");
	$("#" + elb.id).html("new value " + elb.id + " ");
}

【问题讨论】:

  • 所以如果我做对了,你的主要问题是在没有助手的情况下在 div 目标内创建 nouislider?
  • 感谢您的回复。我的主要目标是创建一个根据某些参数上下移动的滑块列表。我创建滑块没有问题,但我无法上下移动它们。因此,我创建了没有滑块的简单示例,因为 javascript 生成的 div 值也是如此:我可以创建它们,但如果没有奇怪的结果,我无法上下移动它(参见第二张图片,显示非常奇怪的控制台输出,div 都是黄色的和灰色)

标签: javascript meteor nouislider


【解决方案1】:

使用 Blaze,您还必须显式导入 .css 文件才能应用样式:

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

然后您可以轻松地使用模板的内置 jQuery 来获取 nouislider 将呈现的目标 div。

考虑以下模板:

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

现在让我们通过单击按钮将一个新的 nouislider 渲染到 id 为 range 的 div 中:

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

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

现在您还可以在此处轻松添加一些反应性数据(但避免使用Session):

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

...并将它与一些数据连接起来。 noUiSlider 允许您挂钩到它的更新事件,您可以从那里将值传递到状态:

function createSliders (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)
  })
}

使用帮助器将值呈现到模板中:

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

导入您自己的 .css 文件以静态设置滑块的样式,如下所示:

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

或使用 jQuery 的 css 动态设置样式。


更新:在更新的显示列表上正确呈现

您描述的问题是正确的,可以重现。但是也可以防止它发生,使用Template.onRendered 控制可能发生渲染的点。

我将模板扩展为以下代码:

<template name="MyTemplate">
    {{#each sliders}}
        <div class="range">
            <div>
                <span>id:</span>
                <span>{{this.id}}</span>
            </div>
            <div id="{{this.id}}">
                {{#if ready}}{{slider this}}{{/if}}
            </div>
            {{#with values this.id}}
                <div>
                <span>values: </span>
                <span>{{this}}</span>
                </div>
            {{/with}}
        </div>
    {{/each}}

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

现在查看目标 div 内部,它之前只分配了一个 id。现在有一个{{#if ready}} 标志和一个函数,调用{{slider this}}

现在为了只在 DOM 初始渲染时渲染,我们需要Template.onRendered

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

并将其添加到(更新的)助手中:

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

现在我们还有一些问题需要解决。我们只想在开关发生变化时渲染,而不是在值更新时渲染。但是我们需要最新的值,以便在下一次渲染中将它们重新分配为起始位置(否则滑块将设置为起始值 0,100)。

为此,我们稍微更改onCreated 代码:

Template.MyTemplate.onCreated(function () {

  // initial slider states
  const 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)
})

现在,如果我们按下开关按钮,想要 a) 删除所有当前滑块及其事件等 b) 将 sliders 数据更新为我们的新(反转)状态:

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

因为我们有多个滑块值需要分别更新,所以我们还需要更改createSlider函数中的一些代码:

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

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

  noUiSlider.create(target, options)

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

通过使用这种方法,您有一些优点和一些缺点。

  • (+) 模式可用于许多类似的问题
  • (+) 不需要autorun
  • (+) 将滑块状态与值状态分开
  • (-) 重新渲染可能会发生多次渲染,用户不会注意到,但这会浪费资源,这在移动设备上可能是个问题。
  • (-) 在较大的模板上可能会变得过于复杂。模板的封装在这里非常重要。

【讨论】:

  • 好的,现在在您的模板中放置第二个滑块,该滑块具有不同的最小最大值,以便您识别它们。然后尝试切换由一些反应元素触发的位置。正是在这里出现了奇怪的结果,div 似乎可以正确切换,但内容(在本例中为滑块)没有切换。您可以在没有滑块的情况下观察到相同的效果,请参阅我原来的问题中的示例。
  • 好的,我知道你的问题了。会检查的。
  • 感谢您提供了很好的答案,这正是我正在寻找的解决方案。我自己永远不会想到这一点 (1.) 将 createSlider() 函数放在帮助程序中 (2.) 这个帮助程序正在等待运行,因为 ready var (3) ready var 仅在结束时设置为 true onRender 模板,以便 div 已准备好并正确加载,可用于滑块。我还学会了更好地使用 Template.instance(),还学会了 +1 来检查滑块是否已经存在(我在创建滑块时正在尝试/捕获,但我喜欢你检查类 'noUi-target'更好)
  • 顺便说一句,我在这里问了第二个问题stackoverflow.com/questions/52340006/…,它与 nouislider 列表大致相同,但这次我尝试将非 nouislider 元素添加到列表中,但失败了...... :(跨度>
猜你喜欢
  • 1970-01-01
  • 2011-09-30
  • 1970-01-01
  • 2021-02-11
  • 1970-01-01
  • 2013-07-26
  • 2012-11-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多