Плюс и минус в каунте корзины MS2

Досточно простой вопрос, который мучает начинающих разработчиков. Решил поделиться решением, как сделать кнопки плюс и минус в корзине MS2. Что бы все было «по-фуншую»

Начнем c разметки. Я показываю на страндартной теме MS2. Вносим правки в чанк tpl.msCart

...
<td class="count">
<form method="post" class="ms2_form" role="form">
  <input type="hidden" name="key" value="{$product.key}"/>
  <div class="form-group">
    <div class="input-group input-group-sm qty">
      <span class="input-group-prepend">
      <span class="input-group-text minus input_count_action">-</span>
      </span>
      <input type="number" name="count" value="{$product.count}" class="form-control"/>
      <span class="input-group-append">
      <span class="input-group-text plus input_count_action">+</span>
      <span class="input-group-text">{'ms2_frontend_count_unit' | lexicon}</span>
      </span>
    </div>
      <button class="btn btn-sm" type="submit" name="ms2_action" value="cart/change">
      ↻</button>
  </div>
</form>
</td>
...


Далее подключим JS файл и подключим его последним.

{$_modx->regClientScript("assets/js/ms2.js")}

И далее напишем ms2.js

$(document).ready(function() {

  $('.qty').on('click', '.input_count_action', function(e) {

    var $input = $(this).closest("div").find('input');
    var count = parseInt($input.val());

    if ($(this).hasClass("plus")) {
      count = count + 1;
    } else if ($(this).hasClass("minus")) {
      count = count - 1;
    }
    
    $input.val(count);
    $input.change();
    return false;
  });

  $('div.count').click(function(e) {
    var v = $(this).parent().find('input#product_price').val(),
      k = $(this).parent().find('input[name="key"]').val();

    if (($(this).hasClass('minus') || $(this).hasClass('plus')) && v > 0) {

      $.post("", {
        ms2_action: 'cart/change',
        key: k,
        count: v
      }, function(response) {

        if (typeof response.success !== "undefined") {

          if (response.success) {
            miniShop2.Order.getcost();
            miniShop2.Cart.status(response.data);
          }
        }
      }, "json");

    }
  });

	miniShop2.Callbacks.Cart.change.before = function() {
		var $field = $(miniShop2.sendData.$form[0]).find(miniShop2.Cart.countInput);
		var count = +$field.val();
		if (count < 1) {
			$field.val('1');
			miniShop2.Message.error('Ошибка! Нельзя ставить кол-во меньше 1!');
			return false;
		}
		return true;
	}
});
И получаем результат.



Решение от @mngatoff

<script>
$(document)
    .on('click touchend', countButton, function (e) { // где countButton - кнопки плюс и минус
        e.preventDefault();
        var $container = $(this).closest('.ms2_form'),
        $count = $container.find('[name="count"]'),
        num = $count.val();
        if (isNaN(num) === false) { // страховочка от, например, пустого поля
            num = parseInt(num, 10);
            switch ($(this).data('ms2-count')) { // соответственно, у кнопок должен быть атрибут data-ms2-count="plus или minus"
                case 'plus':
                    num = num + 1;
                    $count.val(num);
                    break;
                case 'minus':
                    if (num <= 1) return;
                    num = num - 1;
                    $count.val(num);
                    break;
            }
        } else {
            return false;
        }
        $count.trigger('change'); // инициализируем отправку на сервер.
    })
    .on('change keypress keyup', '.ms2_form [name="count"]', function() {
        if ($(this).val().match(/\D/)) {
            this.value = $(this).val().replace(/\D/g,''); // следим на лету, чтобы в поле были только цифры
        }
        if (parseInt($(this).val(), 10) < 1) {
            this.value = 1; // следим на лету, чтобы в поле было не меньше единицы
        }
    });
</script>
Ну и обязательно ставим плюсы первому комментарию)
Олег Щавелев
02 декабря 2019, 02:08
modx.pro
14
2 990
+8
Поблагодарить автора Отправить деньги

Комментарии: 13

mngatoff
02 декабря 2019, 02:23
6
+7
достаточно триггернуть событие change на input:count, и запрос отправится стандартными средствами минишопа.

я вот так делаю (немного вырвано из контекста, но принцип ясен):
<script>
$(document)
    .on('click touchend', countButton, function (e) { // где countButton - кнопки плюс и минус
        e.preventDefault();
        var $container = $(this).closest('.ms2_form'),
        $count = $container.find('[name="count"]'),
        num = $count.val();
        if (isNaN(num) === false) { // страховочка от, например, пустого поля
            num = parseInt(num, 10);
            switch ($(this).data('ms2-count')) { // соответственно, у кнопок должен быть атрибут data-ms2-count="plus или minus"
                case 'plus':
                    num = num + 1;
                    $count.val(num);
                    break;
                case 'minus':
                    if (num <= 1) return;
                    num = num - 1;
                    $count.val(num);
                    break;
            }
        } else {
            return false;
        }
        $count.trigger('change'); // инициализируем отправку на сервер.
    })
    .on('change keypress keyup', '.ms2_form [name="count"]', function() {
        if ($(this).val().match(/\D/)) {
            this.value = $(this).val().replace(/\D/g,''); // следим на лету, чтобы в поле были только цифры
        }
        if (parseInt($(this).val(), 10) < 1) {
            this.value = 1; // следим на лету, чтобы в поле было не меньше единицы
        }
    });
</script>
    mngatoff
    02 декабря 2019, 02:25
    1
    0
    плюс этого варианта еще и в том, что его можно без изменений использовать в карточке товара в форме добавления в корзину
      Олег Щавелев
      03 декабря 2019, 10:32
      0
      Cпасибо за решение. Добавил как альтернативное в тело статьи.
      SEQUEL.ONE
      04 сентября 2020, 01:03
      0
      .on('click touchend', countButton, function (e) { // где countButton — кнопки плюс и минус
      Подскажите пожалуйста, что нужно вписать в функцию countButton, у меня в консоли вылазиет ошибка:

      Uncaught ReferenceError: countButton is not defined
      а при нажатии на сами кнопки количество не меняется.
        Владимир
        04 сентября 2020, 08:41
        1
        +1
        У вас в коде нет «countButton», вот она и не может его найти. Попробуйте кнопкам прописать класс countButton, а в функции написать: .on('click touchend', '.countButton', function (e) {…
          SEQUEL.ONE
          05 сентября 2020, 01:54
          0
          Спасибо. Заработало =)
      Баха Волков
      02 декабря 2019, 10:32
      +2
      Действительно, вот эта часть вашего кода:

      $('div.count').click(function(e) {
          var v = $(this).parent().find('input#product_price').val(),
            k = $(this).parent().find('input[name="key"]').val();
      
          if (($(this).hasClass('minus') || $(this).hasClass('plus')) && v > 0) {
      
            $.post("", {
              ms2_action: 'cart/change',
              key: k,
              count: v
            }, function(response) {
      
              if (typeof response.success !== "undefined") {
      
                if (response.success) {
                  miniShop2.Order.getcost();
                  miniShop2.Cart.status(response.data);
                }
              }
            }, "json");
      
          }
        });

      ну очень не нужна

      Обратите внимание, что на изменение input[name=count] повешена функция которая отправляет форму.

      @mngatoff как раз и предлагает воспользоваться методом trigger или на худой конец можно было бы самостоятельно сабмитить форму
        Баха Волков
        02 декабря 2019, 10:46
        +3
        И еще позвольте немного покритиковать, сюдя по этой вашей строчке:

        var v = $(this).parent().find('input#product_price').val(),

        у каждого товара в корзине есть input с id=«product_price»… Транслируя готовые решения или пошаговые инструкции по хорошему нужно осознавать некую ответственность.

        Идентификатор в коде документа должен быть в единственном экземпляре
      Павел Гвоздь
      03 декабря 2019, 08:40
      0
      Соглашусь с комментариями выше и прошу исправить решение в посте в соответствии с ними! Ибо потом производятся сайты, созданные на копипасте (без мозгов), которые приходится корректировать мне или другим адекватным разработчикам.

      Ну и хорошо бы вставлять код в пост не как попало, а хоть как-то его корректируя. Зачем в первом блоке с HTML кодом эти табы вначале каждой строки?
        Олег Щавелев
        03 декабря 2019, 10:43
        0
        Ну и хорошо бы вставлять код в пост не как попало, а хоть как-то его корректируя. 
        Зачем в первом блоке с HTML кодом эти табы вначале каждой строки?
        Каюсь) Поправил.
          Олег Щавелев
          03 декабря 2019, 10:45
          0
          Соглашусь с комментариями выше и прошу исправить решение в посте в соответствии с ними!
          Обязательно вникну и поправлю основываясь на комментариях выше.
            Олег Захаров
            14 апреля 2022, 12:13
            0
            Вот что странно иногда возникают ситуации когда код @mngatoff не срабатывает — у меня миникорзина выпадает через меню dropdown в выпадающий список, Bootstrap 5 — возникает какая-то ошибка и событие change на поле input ну ни в какую не хочет срабатывать. Плюс на сайте может быть несколько корзин.
            И тут пригождается кусок кода ms2.js в ТС (только не удаляйте) — прием как отправить ms2_action: 'cart/change' оказался для меня лично очень полезен.
            Да не по стандарту, но главное что работает. Может я не адекватный программист, но я уже не знаю как заставить сработать стандартным «правильным» способом $count.trigger('change'), т.к. возникает конфликт с Bootstrap 5 — кстати в нем по умолчанию отключили jQuery — интересно когда коды js minishop перепишут без использования jQuery?..
            На другом сайте с Bootstrap 3 все работает нормально.
          Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
          13