如何在没有助手的情况下更新Meteor中的div? [英] How to update div in Meteor without helper?

查看:92
本文介绍了如何在没有助手的情况下更新Meteor中的div?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我可以使用JavaScript毫无问题地更新div.例如,两个div的初始值都显示在左侧的图像中,而单击测试"按钮后的值则显示在右侧.

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 button are shown on the right.

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

when I click the test button then the counter is increased by 1 and the values for the divs are changed like this:

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 =灰色在底部),而另一个计数器不平衡时会消失.

So far so good, but now I would like to change the order of the divs: when the counter is even I want div a = yellow to be at the top (div b = grey at the bottom), and the other way around when the counter is uneven.

为简单起见,在示例中,我对每个可能的命令(a,b)和(b,a)使用2个小数组,并且一个助手命令"将根据计数器值(x)返回2种情况中的1种%2是否为零)

For simplicity in the example I use 2 small arrays for each of the possible orders (a,b) and (b,a) and a helper 'order' will return 1 of the 2 cases depending on the counter value (x%2 is zero or not)

不幸的是,结果不是我想要和期望的. div正确地更改了位置,但是它们的文本却没有.第一次单击后,计数器从5变为6,因此黄色div从下到上移动,但是div内的文本是错误的,我希望黄色div中向上移动的是新值a",但我得到了取而代之的是新价值ba"(反之,与其他div相同)

Unfortunately, the result is not what I want and expected. The divs are changing position correctly, but their text is not. After the first click the counter goes from 5 to 6, so the yellow div is moving from bottom to top, but the text inside the divs is wrong, I expect 'new value a' in the yellow div that moved up, but I get 'new value b a' instead (same for other div the other way around)

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

Also when I inspect the divs in the console output I'm seeing strange results, Meteor seems to be confused about div ids? see the below image, the first div is both a and b, and yellow and grey at the same time ...

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

Does anybody know why this is? And how can I fix it?

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

I know I can just use helpers to get the correct text in the correct div. But my end goal is not to change the text inside divs, but instead I want to create a slider using nouislider like this:

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 + " ");
}

推荐答案

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

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

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

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

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

请考虑以下模板:

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

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

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

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

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

...并将其与一些数据连接. noUiSlider允许您挂接到它的update事件,您可以在其中将值传递到状态:

... 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:

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

使用助手将值渲染到模板中

Render the value into the template using a helper:

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

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

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

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

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

or style it dynamically using jQuery's css.


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

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:

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

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

现在,为了仅在最初渲染DOM时进行渲染,我们需要Template.onRendered:

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

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

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

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

To do that we change the onCreated code a bit:

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数据更新为新的(反转)状态:

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

由于我们有多个滑块值需要单独更新,因此我们还需要在createSlider函数中更改一些代码:

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

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

通过这种方法,您将拥有一些优点和缺点.

By using this approach you some advantages and some disadvantages.

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

这篇关于如何在没有助手的情况下更新Meteor中的div?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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