Как обновить div в Meteor без помощника? - PullRequest
0 голосов
/ 11 сентября 2018

Я могу без проблем обновить div с помощью JavaScript.Например, начальные значения для обоих div показаны на изображении слева, а значения после нажатия на кнопку тестирования показаны справа.

enter image description here

когда я нажимаю кнопку тестирования, счетчик увеличивается на 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 a = желтый был вверху (div b = серый внизу), и наоборот, когда счетчик неровный.

Для простотыв примере я использую 2 маленьких массива для каждого из возможных порядков (a, b) и (b, a), и вспомогательный 'order' вернет 1 из 2 случаев в зависимости от значения счетчика (x% 2 равно нулю илинет)

К сожалению, результат не тот, который я хочу и ожидал.Div изменяют свою позицию правильно, но их текст - нет.После первого щелчка счетчик меняется с 5 на 6, поэтому желтый div перемещается снизу вверх, но текст внутри элементов div неправильный, я ожидаю «новое значение a» в желтом div, которое переместилось вверх, но я получаювместо 'new value ba' (то же самое для других div, наоборот)

Также, когда я проверяю div в выводе консоли, я вижу странные результаты, кажется, что Метеор запутался в идентификаторах div?см. изображение ниже, первый div - это и a, и b, и желтый, и серый одновременно ...

strange console output, contradicting id's

Кто-нибудь знает, почемуэто?И как я могу это исправить?

Я знаю, что могу просто использовать помощников, чтобы получить правильный текст в правильном 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 + " ");
}

1 Ответ

0 голосов
/ 12 сентября 2018

С Blaze вы также должны явно импортировать файл .css, чтобы применить стили:

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

Затем вы можете легко использовать встроенный в шаблон jQuery, чтобы получить целевой div, где nouisliderбудет оказывать.

Рассмотрим следующий шаблон:

<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 в div с id range, нажав кнопку:

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's 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, которому ранее был назначен только идентификатор.Теперь есть флаг {{#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)
})

Теперь, если мы нажмем кнопку переключателя, хотим а) удалить все текущие ползунки с их событиями и т. д. и б) обновить данные 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 требуется
  • (+) отделяет состояние ползунка от значениясостояние
  • (-) при повторном рендеринге рендеринг может происходить более одного раза, пользователи не обращают на это внимания, но тратят ресурсы, что может стать проблемой для мобильных устройств.
  • (-) может стать чрезмернымсложный на больших шаблонах.Инкапсуляция шаблонов здесь очень важна.
...