Ноктюрн + бутстрап 3 радио кнопки
Связано с: Группа кнопок загрузки Bootstrap
HTML:
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-primary">
<input type="radio" name="options" id="option1" value="1" data-bind="checked: optionsValue"> Option 1
</label>
<label class="btn btn-primary">
<input type="radio" name="options" id="option2" value="2" data-bind="checked: optionsValue"> Option 2
</label>
<label class="btn btn-primary">
<input type="radio" name="options" id="option3" value="3" data-bind="checked: optionsValue"> Option 3
</label>
</div>
<br />
<span data-bind="text: optionsValue"></span>
JavaScript:
var ViewModel = function() {
this.optionsValue = ko.observable()
};
ko.applyBindings(new ViewModel());
JsFiddle:
У меня есть код выше, который я пытаюсь работать, как я ожидаю. Проблема заключается в том, что когда data-toggle="buttons"
добавляется в div группы btn (как в примере Bootstrap 3), привязка нокаута перестает работать. Если я оставлю переключатель данных от группы кнопок, привязка работает так, как ожидалось, но группа кнопок выглядит ужасно. Я знаю, что это не работало в Bootstrap 2, потому что на самом деле они не использовали радиовход для своего радиоуправления. Почему он отказывается работать сейчас, даже если они делают?
Ответы
Ответ 1
кнопки бутстрапа и нокаут checked
привязка по-прежнему не очень приятная:
- нокаут использует событие
click
внутри привязки checked
для тигрования подстилающего наблюдаемого для изменения
- bootstrap подписывается на событие click для переключения, но вызывает
e.preventDefault()
, поэтому KO не будет уведомлен о клике.
Одним из возможных решений является создание настраиваемого обработчика привязки, в котором вы подписываетесь на событие change
(это запускается загрузочной загрузкой в Google) и устанавливают там значение наблюдаемости:
ko.bindingHandlers.bsChecked = {
init: function (element, valueAccessor, allBindingsAccessor,
viewModel, bindingContext) {
var value = valueAccessor();
var newValueAccessor = function () {
return {
change: function () {
value(element.value);
}
}
};
ko.bindingHandlers.event.init(element, newValueAccessor,
allBindingsAccessor, viewModel, bindingContext);
},
update: function (element, valueAccessor, allBindingsAccessor,
viewModel, bindingContext) {
if ($(element).val() == ko.unwrap(valueAccessor())) {
setTimeout(function () {
$(element).closest('.btn').button('toggle');
}, 1);
}
}
}
И используйте его в своем представлении с помощью:
<label class="btn btn-primary">
<input type="radio" name="options" id="option1" value="1"
data-bind="bsChecked: optionsValue"> Option 1
</label>
Оригинальная демонстрация с использованием Bootstrap 3.0.2 JSFiddle.
Обновлено демо с помощью Bootstrap 3.2.0 JSFiddle.
Ответ 2
Я не могу взять на себя ответственность за это, поскольку однажды мои коллеги придумали это, но он работает очень хорошо.
<div class="btn-group" data-toggle="buttons">
<label data-bind="css: { active: !HideInLeaderboards() },
click: function () { HideInLeaderboards(false); },
clickBubble: false"
class="btn btn-default">
Show Name
</label>
<label data-bind="css: { active: HideInLeaderboards() },
click: function () { HideInLeaderboards(true); },
clickBubble: false" class="btn btn-default">
Hide Name
</label>
</div>
Ответ 3
измените обработчик @nemesv, предложенный таким образом: и он отлично работал в моем приложении.
ko.bindingHandlers.bsChecked = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var value = valueAccessor();
var newValueAccessor = function () {
return {
change: function () {
value(element.value);
}
}
};
if ($(element).val() == ko.unwrap(valueAccessor())) {
$(element).closest('.btn').button('toggle');
}
ko.bindingHandlers.event.init(element, newValueAccessor, allBindingsAccessor, viewModel, bindingContext);
}
}
Ответ 4
Knockstrap кажется связующим звеном между Bootstrap и Knockout, и, похоже, он хорошо обновляется. Что касается переключателей в конкретном, они используют этот код:
// Knockout checked binding doesn't work with Bootstrap radio-buttons
ko.bindingHandlers.radio = {
init: function (element, valueAccessor) {
if (!ko.isObservable(valueAccessor())) {
throw new Error('radio binding should be used only with observable values');
}
$(element).on('change', 'input:radio', function (e) {
// we need to handle change event after bootsrap will handle its event
// to prevent incorrect changing of radio button styles
setTimeout(function() {
var radio = $(e.target),
value = valueAccessor(),
newValue = radio.val();
value(newValue);
}, 0);
});
},
update: function (element, valueAccessor) {
var $radioButton = $(element).find('input[value="' + ko.unwrap(valueAccessor()) + '"]'),
$radioButtonWrapper;
if ($radioButton.length) {
$radioButtonWrapper = $radioButton.parent();
$radioButtonWrapper.siblings().removeClass('active');
$radioButtonWrapper.addClass('active');
$radioButton.prop('checked', true);
} else {
$radioButtonWrapper = $(element).find('.active');
$radioButtonWrapper.removeClass('active');
$radioButtonWrapper.find('input').prop('checked', false);
}
}
};
Ответ 5
Существует гораздо более простой подход нашел здесь.
Мы можем просто не использовать атрибут data-toggle
и пытаться добиться такого же поведения, используя привязку данных нокаута.
Это почти так же просто, как родные html-радиостанции.
var ViewModel = function() {
this.optionsValue = ko.observable("1");
};
ko.applyBindings(new ViewModel());
input[type="radio"] {
/* Make native radio act the same as bootstrap radio. */
display: none;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class="btn-group">
<label class="btn btn-primary" data-bind="css: {active: optionsValue() == '1'}">
<input type="radio" name="options" id="option1" value="1" data-bind="checked: optionsValue">Option 1
</label>
<label class="btn btn-primary" data-bind="css: {active: optionsValue() == '2'}">
<input type="radio" name="options" id="option2" value="2" data-bind="checked: optionsValue">Option 2
</label>
<label class="btn btn-primary" data-bind="css: {active: optionsValue() == '3'}">
<input type="radio" name="options" id="option3" value="3" data-bind="checked: optionsValue">Option 3
</label>
</div>
<br/>
<span data-bind="text: optionsValue"></span>
Ответ 6
Там простое решение, которое я удивляю, пока не упоминалось.
1) Удалите data-toggle="buttons"
из группы btn
Теперь нокаут должен работать
2) Bootstrap применяет пользовательский css для этих входов, чтобы скрыть их, что мы просто потеряли, удалив переключение данных, поэтому примените следующие css к радиовводам:
position: absolute;
clip: rect(0,0,0,0);
pointer-events: none;
3) Последнее, что нам нужно, это выбрать выбранную кнопку выбора активного класса. Добавьте следующие кнопки меток:
data-bind="css: {active: optionsValue() == valueOfThisInput}"