Bootstrap multiselect selectAll не работает с Knockout.js, если не используется setTimeout - PullRequest
0 голосов
/ 19 сентября 2019

Очевидно, что Bootstrap multiselect имеет ограничение при использовании jQuery multiselect() с Knockout.js, так что если выпадающий список множественного выбора изменяется кодом во время события Knockout (событие click, в следующем примере), то код isnне применяется.

Следующий пример демонстрирует это:

  1. Сначала нажмите кнопку слева.Вы увидите, что, хотя параметры созданы, они не выбраны.Вам нужно еще один клик, чтобы сделать их выбранными.
  2. Затем нажмите кнопку справа.Вы увидите, что параметры созданы и выбраны.Я использовал время ожидания 1000 мс, но оно работает только с временем ожидания 1 мс.

Мой вопрос: есть ли лучший способ, чем время ожидания, заставить selectAll() работать?

var selectorVM = function () {
	var self = this;
  self.available = ko.observableArray([]);
  self.selected = ko.observableArray([]);
  self.init = function () {
    self.initOptions();
    self.selectAll();
  };
  self.initWithTimeout = function () {
    self.initOptions();
    self.selectAllWithTimeout();
  };
  self.initOptions = function () {
    self.available([]);
    self.available([
      { name: "option 1", value: 1}, 
      { name: "option 2", value: 2}
    ]);  
  };
  self.selectAll = function () {
      var $selector = $("#selector");
      $selector.multiselect('selectAll', false);
      $selector.multiselect('updateButtonText');
  };
  self.selectAllWithTimeout = function () {
    setTimeout(self.selectAll, 1000);
  };
}
var selectorVM = new selectorVM();
ko.applyBindings(selectorVM);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-multiselect/0.9.15/js/bootstrap-multiselect.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-multiselect/0.9.15/css/bootstrap-multiselect.css" rel="stylesheet"/>

<div> 
  <button class="btn btn-default" data-bind="click: init">Click to init dropdown, no timeout</button>
  <button class="btn btn-default" data-bind="click: initWithTimeout">Click to init dropdown, with timeout</button>
</div>
<div>
  <select id="selector" 
        class="form-control"
        multiple="multiple" 
        data-bind="options: available,
                   optionsText: 'name',
                   optionsValue: 'value',
                   selectedOptions: selected,
                   multiselect: { includeSelectAllOption: true }">
  </select>    
</div>

1 Ответ

0 голосов
/ 19 сентября 2019

Ваша проблема связана с порядком выполнения кода.Встроенный обработчик привязки KO options и плагин jQuery Multiselect модифицируют DOM, и обработчик options, вероятно, выполняет после функции множественного выбора, поэтому при построении меню множественного выбора нетпока нет никаких вариантов для этого подобрать.Когда вы используете setTimeout, даже с тайм-аутом в 1 мс, функция помещается в конец стека вызовов и будет выполняться после завершения текущего цикла событий (подробнее см. здесь ), так что теперьвыполняется после завершения привязки options.Ergo, это работает.

Однако, это не очень красиво.Никогда не стоит выполнять манипуляции с DOM внутри модели представления.Вот почему настраиваемые обработчики привязки - это вещь;легко взаимодействовать с элементами DOM и поддерживать чистоту моделей представлений. Ваш код ссылается на обработчик привязки multiselect, но я не вижу его в вашем посте.Если мы создадим его (это не сложно), мы увидим, что мы можем заставить меню работать так, как ожидалось. Ваша первоначальная проблема заключалась в том, что вы использовали прилагаемый обработчик привязки, а также манипулировали DOM вручную.Вы должны просто придерживаться обработчика привязки, и он будет работать нормально.Тайм-ауты не нужны.

С такими фреймворками, как KO, в качестве общего практического правила вам всегда нужно только манипулировать данными в вашей модели представления.KO должен сделать тяжелую работу по обновлению пользовательского интерфейса.Поэтому, если вы хотите выбрать все элементы, подумайте о том, как это работает: существует наблюдаемый массив с именем selected, в котором хранятся идентификаторы выбранных элементов.Поэтому, если вы хотите выбрать все элементы, вам нужно только просмотреть все доступные элементы и отправить идентификаторы в выбранный массив.KO позаботится об обновлении интерфейса для вас.Смотрите обновленный фрагмент кода.Я создал отдельную кнопку, которая вызывает функцию selectAll, но вы, конечно, можете просто вызвать selectAll в вашей функции инициализации, если хотите.

var selectorVM = function () {
  var self = this;
  
  self.available = ko.observableArray([]);
  self.selected = ko.observableArray([]);
  
  self.init = function () {
    self.initOptions();
  };
  
  self.initOptions = function () {
    self.available([
      { name: "option 1", value: 1 }, 
      { name: "option 2", value: 2 }
    ]);  
  };

  self.selectAll = function () {
    self.available().forEach(function (opt) {
      if (self.selected.indexOf(opt.value) === -1) {
        self.selected.push(opt.value);
      }
    });
  }
}

var selectorVM = new selectorVM();
ko.applyBindings(selectorVM);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-multiselect/0.9.15/js/bootstrap-multiselect.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-multiselect/0.9.15/css/bootstrap-multiselect.css" rel="stylesheet"/>

<div> 
  <button class="btn btn-default" data-bind="click: init">Click to init dropdown, no timeout</button>
  <button class="btn btn-default" data-bind="click: selectAll">Select all</button>
</div>
<div>
  <select id="selector" 
        class="form-control"
        multiple="multiple" 
        data-bind="options: available,
                   optionsText: 'name',
                   optionsValue: 'value',
                   selectedOptions: selected,
                   multiselect: { includeSelectAllOption: true }">
  </select>    
</div>

<p>Selected options in observable array:</p>
<ul data-bind="foreach: selected"><li data-bind="text: $data"></li></ul>
...