我有两列的表单,其中包含左右下拉菜单。
根据用户在左侧下拉列表中选择的内容,右侧下拉列表将呈现为纯下拉列表(<select>
或多选(<select multiple="multiple">
)并填充值。
大多数情况下都有效,但是在添加一行然后更改新添加的行中的左侧下拉选择时,控制台出现Uncaught TypeError: Cannot read property 'length' of undefined
错误。该代码按预期工作(它会更新正确的下拉列表),但是我不确定为什么首先将错误引发该错误。
示例:
const regex = /^(.+?)(\d+)$/i;
let cloneIndex = $(".operation").length;
bindToSelector($('.operation-keys'));
function setIndex(elements) {
elements.each(function(index, element) {
$(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();
let selector = $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);
bindToSelector(selector);
}
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;
}
function bindToSelector(selector) {
$(selector).on('change', function() {
let operationKey = $(this).val()
let valueSelect = $(this).parent().next().find('.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'];
console.log(options);
// empty the dropdown in the cloned row (not working - empties all value dropdowns)
valueSelect.empty();
for (var i = 0; i < options.length; i++) {
valueSelect.append(`<option value="${options[i].toLowerCase()}" >${options[i]}</option>`);
}
});
}
<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"><script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://github.com/davidstutz/bootstrap-multiselect/blob/master/dist/css/bootstrap-multiselect.css" type="text/css"/>
<script type="text/javascript" src="https://github.com/davidstutz/bootstrap-multiselect/blob/master/dist/js/bootstrap-multiselect.js"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<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="" selected disabled>Select One</option>
<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>
违规行是:
for (var i = 0; i < options.length; i++) {
我只是不明白为什么它首先会引发错误。
复制步骤:
+
添加新行。 奖励点:添加新行(上面的步骤2)时,如何清除新行右侧下拉列表中的值(因此它与克隆行不同) ?我知道我需要在.empty();
末尾某个地方的选择器上调用clone()
,但不确定应该使用哪个选择器。
答案 0 :(得分:0)
我相信问题出在您的克隆函数中:
let selector = $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);
}
});
// ...
bindToSelector(selector);
运行.find(“ *”)时,选择器将变成由10个项目组成的大型数组。然后,每个这些都将发送到选择器函数并绑定到事件处理程序。
更改值时,这些项通过buildSelect
函数运行,但不匹配任何内容,因为您只希望通过它发送select元素。因此,将返回默认值{}
。该空对象没有尝试使用result['operationValues']
访问的值“ operationValues”,因此它是未定义的,因此没有长度。