jQuery - Изменить выпадающий список на множественный выбор на основе другого выбора внутри формы, в которой есть клонированные строки - PullRequest
1 голос
/ 26 января 2020

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

В моем конкретном примере c у меня есть два раскрывающихся списка рядом - operation_key и operation_value. Исходя из выбора пользователя для operation_key, он должен видеть либо обычный (одиночный) выбор, либо множественный выбор и иметь соответствующие параметры, динамически заполняемые в соответствующем раскрывающемся списке operation_value.

Вот отображение того, как оно должно работать:

+---------------+---------------------------------+---------------------------------------+
| Operation Key | Operation Value - Dropdown Type |  Operation Value - Dropdown Options   |
+---------------+---------------------------------+---------------------------------------+
| continents    | multiple                        | North  America, South America, Europe |
| languages     | multiple                        | English, French, Spanish              |
| eye_color     | single                          | Brown, Blue, Green, Hazel             |
| age           | single                          | 18-24, 25-32, 33-40, >41              |
+---------------+---------------------------------+---------------------------------------+

Я соединил код из различных примеров и придумал следующее:

// clone functionality

const regex = /^(.+?)(\d+)$/i;
let cloneIndex = $(".operation").length;

function setIndex(elements) {
  elements.each(function(index, element) {
    // set the appropriate index on the cloned element (not working)
    $(element).find("select.operation_keys").attr("name", "operation_keys[" + index + "]");
    $(element).find("select.operation_values").attr("name", "operation_values[" + index + "]");
  });
}

function clone() {

  let $removeButton = $(this).closest(".actions").find(".remove-operation");
  let $closestOperation = $(this).closest(".actions").prev(".operation").first();

  $removeButton.show();

  $closestOperation.clone()
    .insertAfter($closestOperation).attr("id", "operation-" + cloneIndex)
    .find("*")
    .each(function() {
      let id = this.id || "";
      let match = id.match(regex) || [];
      if (match.length == 3) {
        this.id = match[1] + '-' + (cloneIndex);
      }
    });
  cloneIndex++;

  let $Operations = $(this).closest(".operation-parent").find(".operation");

  setIndex($Operations);
}

function remove() {
  $(this).closest(".actions").prev(".operation").remove();

  let $Operations = $(this).closest(".operation-parent").find(".operation");
  setIndex($Operations);
  let totalElements = $(this).closest(".operation-parent").find(".operation").length;
  if (totalElements === 1) {
    $(this).hide();
  }
  cloneIndex--;
}

$(".add-operation").on("click", clone);
$(".remove-operation").on("click", remove);

// select logic

function buildSelect(operationKey) {
  let result = {};
  switch (operationKey) {
    case 'continents':
      result['operationValues'] = ['North America', 'South America', 'Europe'];
      result['type'] = 'multiple';
      break;
    case 'languages':
      result['operationValues'] = ['English', 'French', 'Spanish'];
      result['type'] = 'multiple';
      break;
    case 'eye_color':
      result['operationValues'] = ['Brown', 'Blue', 'Green', 'Hazel'];
      result['type'] = 'single';
      break;
    case 'age':
      result['operationValues'] = ['18-24', '25-32', '33-40', '>41'];
      result['type'] = 'single';
      break;
  }
  return result;
}

$('.operation-keys').on('change', function() {
  let operationKey = $(this).val()
  // get the nearest value dropdown element (not working)
  let valueSelect = $(this).closest('.operation-values');
  // get the values + type of select we should populate
  let result = buildSelect(operationKey);
  // set the select type, if needed
  if (result['type'] == 'multiple') {
    valueSelect.attr("multiple", "multiple")
  }
  // populate the values  (not working)
  let options = result['operationValues'];
  for (var i = 0; i < options.length; i++) {
    valueSelect.options.add(new Option(option.toLowerCase(), option));
  }


});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="https://github.com/davidstutz/bootstrap-multiselect/blob/master/dist/css/bootstrap-multiselect.css" type="text/css" />


<div class="form-group row">
  <label class="col-4">Operation Key</label>
  <label class="col-6">Operation Value</label>
</div>
<div class="form-group">

  <div class="operation-parent row">
    <div class="col-8 operation">
      <div class="row">
        <div class="col-6">
          <select class="form-control form-control-lg operation-keys" name="operation_keys[]">
            <option value="continents">Continents Visited</option>
            <option value="languages">Languages Spoken</option>
            <option value="eye_color">Eye Color</option>
            <option value="age">Age</option>
          </select>
        </div>

        <div class="col-6">
          <select class="form-control form-control-lg mb-30 operation-values" name="operation_values[]">

          </select>
        </div>

      </div>
    </div>
    <div class="col-2 actions">
      <a class="btn btn-alt-success add-operation">+</a>
      <a class="btn btn-alt-danger remove-operation" style="display:none;">-</a>
    </div>
  </div>

  <script type="text/javascript" src="https://github.com/davidstutz/bootstrap-multiselect/blob/master/dist/js/bootstrap-multiselect.js"></script>

JSFiddle: https://jsfiddle.net/vs6oLugy/

Есть несколько проблем:

1) Функция setIndex() должна динамически обновлять имена клонированных полей .operation_keys и .operation_values. Например, operation_keys[1], operation_values[1], operation_keys[2], operation_values[2], et c. По какой-то причине это не работает. Я предполагаю, что это потому, что $(element).find("select.operation_keys") неправильно нацеливает элемент, но я не могу понять, почему это не так.

2) Остальная часть лога c, которая находит и обновляет соответствующий .operation_values выпадающий с соответствующим типом (обычный выбор или множественный выбор) и устанавливает доступные варианты / опции не работает. Я думаю, что проблема в строке, которая нацеливается на раскрывающийся список:

let valueSelect = $(this).closest('.operation-values');

Каждый из селекторов находится внутри родительского .col-6 div, поэтому, возможно, $(this).closest() isn ' в состоянии пройти вверх через дерево DOM? Хотя я не уверен, что должно измениться, чтобы эта часть работала. Я попытался изучить родные элементы и сначала получить родительский элемент, но ни один из них не сработал.

3) Когда клонируется строка, я не уверен, как скрыть второй (самый правый) раскрывающийся список (operation_value), чтобы побудить пользователя сначала сделать выбор в левом раскрывающемся списке (operation_key). Когда строка клонируется, строка, из которой она была клонирована, предположительно, могла иметь оба выпадающих списка, поэтому достаточно ли безопасно просто вызывать .hide() для нового самого правого выпадающего списка, созданного функцией clone()?

Любая помощь очень ценится!

Ответы [ 2 ]

0 голосов
/ 26 января 2020

В вашем коде мало изменений

  // set the select type, if needed
  if (result['type'] == 'multiple') {
      $('#operation-values').attr("multiple", "multiple");
  } else {
      $('#operation-values').removeAttr("multiple");
  }

  // populate the values  (not working)
  let options = result['operationValues'];

  $('#operation-values').html('');
  for(var i = 0; i < options.length; i++) {
     $('#operation-values').append('<option value="'+options[i].toLowerCase()+'">'+options[i]+'</option>');
  }
0 голосов
/ 26 января 2020

В этом коде необходимо учесть несколько моментов:

  1. Вы используете jQuery. Но вы смешиваете код с ванилью JS.
  2. . Несколько шагов были забыты, как и еще, чтобы удалить атрибут множественного числа и очистить выбор при добавлении новых опций.
  3. опции возвращает список только для чтения.
$('.operation-keys').on('change', function() {
  let operationKey = $(this).val()
  let valueSelect = $('.operation-values');

  // get the values + type of select we should populate
  let result = buildSelect(operationKey);
  // set the select type, if needed
  if (result['type'] == 'multiple') {
    valueSelect.attr("multiple", "multiple");
  }
  else
  {
    valueSelect.removeAttr("multiple", "multiple");
  }

  // populate the values
  let options = result['operationValues'];
  //make the list empty again
  valueSelect.empty();
  for (var i = 0; i < options.length; i++) {
    //use jQuery's append and add options through string!
    valueSelect.append(`<option value="${options[i].toLowerCase()}" >${options[i]}</option>`);
  }


});

// clone functionality

const regex = /^(.+?)(\d+)$/i;
let cloneIndex = $(".operation").length;

function setIndex(elements) {
  elements.each(function(index, element) {
    // set the appropriate index on the cloned element (not working)
    $(element).find("select.operation_keys").attr("name", "operation_keys[" + index + "]");
    $(element).find("select.operation_values").attr("name", "operation_values[" + index + "]");
  });
}

function clone() {

  let $removeButton = $(this).closest(".actions").find(".remove-operation");
  let $closestOperation = $(this).closest(".actions").prev(".operation").first();

  $removeButton.show();

  $closestOperation.clone()
    .insertAfter($closestOperation).attr("id", "operation-" + cloneIndex)
    .find("*")
    .each(function() {
      let id = this.id || "";
      let match = id.match(regex) || [];
      if (match.length == 3) {
        this.id = match[1] + '-' + (cloneIndex);
      }
    });
  cloneIndex++;

  let $Operations = $(this).closest(".operation-parent").find(".operation");

  setIndex($Operations);
}

function remove() {
  $(this).closest(".actions").prev(".operation").remove();

  let $Operations = $(this).closest(".operation-parent").find(".operation");
  setIndex($Operations);
  let totalElements = $(this).closest(".operation-parent").find(".operation").length;
  if (totalElements === 1) {
    $(this).hide();
  }
  cloneIndex--;
}

$(".add-operation").on("click", clone);
$(".remove-operation").on("click", remove);

// select logic

function buildSelect(operationKey) {
  let result = {};
  switch (operationKey) {
    case 'continents':
      result['operationValues'] = ['North America', 'South America', 'Europe'];
      result['type'] = 'multiple';
      break;
    case 'languages':
      result['operationValues'] = ['English', 'French', 'Spanish'];
      result['type'] = 'multiple';
      break;
    case 'eye_color':
      result['operationValues'] = ['Brown', 'Blue', 'Green', 'Hazel'];
      result['type'] = 'single';
      break;
    case 'age':
      result['operationValues'] = ['18-24', '25-32', '33-40', '>41'];
      result['type'] = 'single';
      break;
  }
  return result;
}

$('.operation-keys').on('change', function() {
  let operationKey = $(this).val()
  let valueSelect = $('.operation-values');
  
  // get the values + type of select we should populate
  let result = buildSelect(operationKey);
  // set the select type, if needed
  if (result['type'] == 'multiple') {
    valueSelect.attr("multiple", "multiple");
  }
  else
  {
    valueSelect.removeAttr("multiple", "multiple");
  }

  // populate the values
  let options = result['operationValues'];
  valueSelect.empty();
  for (var i = 0; i < options.length; i++) {
    valueSelect.append(`<option value="${options[i].toLowerCase()}" >${options[i]}</option>`);
  }


});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="https://github.com/davidstutz/bootstrap-multiselect/blob/master/dist/css/bootstrap-multiselect.css" type="text/css" />


<div class="form-group row">
  <label class="col-4">Operation Key</label>
  <label class="col-6">Operation Value</label>
</div>
<div class="form-group">

  <div class="operation-parent row">
    <div class="col-8 operation">
      <div class="row">
        <div class="col-6">
          <select class="form-control form-control-lg operation-keys" name="operation_keys[]">
            <option value="continents">Continents Visited</option>
            <option value="languages">Languages Spoken</option>
            <option value="eye_color">Eye Color</option>
            <option value="age">Age</option>
          </select>
        </div>

        <div class="col-6">
          <select class="form-control form-control-lg mb-30 operation-values" name="operation_values[]">

          </select>
        </div>

      </div>
    </div>
    <div class="col-2 actions">
      <a class="btn btn-alt-success add-operation">+</a>
      <a class="btn btn-alt-danger remove-operation" style="display:none;">-</a>
    </div>
  </div>

  <script type="text/javascript" src="https://github.com/davidstutz/bootstrap-multiselect/blob/master/dist/js/bootstrap-multiselect.js"></script>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...