javascript - two - Botones de radio Knockout+Bootstrap 3
radio button knockout binding (5)
Relacionado con: Bootstrap Radio Button Group
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:
- Sin datos-alternar: http://jsfiddle.net/fDMM2/
- Con data-toggle: http://jsfiddle.net/Kf3tj/1/
Tengo el código anterior que estoy tratando de poner en funcionamiento como lo espero. El problema es que cuando data-toggle="buttons"
se agrega al btn-group div (como en el ejemplo de Bootstrap 3 ), el enlace knockout deja de funcionar. Si dejo la información desactivada del grupo de botones, el enlace funciona como se espera pero el grupo de botones se ve horrible. Sé que esto no funcionó en Bootstrap 2 porque en realidad no usaban la entrada de radio para su estilo de radio. ¿Cómo es que se niega a trabajar ahora aunque lo hagan?
Aquí se encuentra un enfoque mucho más sencillo.
Simplemente no podemos usar el atributo de data-toggle
e intentar lograr el mismo comportamiento utilizando el enlace de datos de knockout.
Es casi tan simple como las radios nativas html.
var ViewModel = function() {
this.optionsValue = ko.observable("1");
};
ko.applyBindings(new ViewModel());
input[type="radio"] {
/* Make native radio act the same as bootstrap''s 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>
Los botones de bootstrap y el enlace checked
knockout todavía no están jugando bien:
- knockout utiliza el evento de
click
dentro del enlacechecked
para activar el observador subyacente para cambiar - bootstrap se suscribe en el evento click para hacer el alternar pero llama a
e.preventDefault()
para que no se notifique a KO sobre el clic.
Una posible solución es crear un controlador de enlace personalizado en el que se suscriba en el evento de change
(esto se inicia con bootstrap en toogle) y establezca su valor observable allí:
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);
}
}
}
Y úsalo en tu vista con:
<label class="btn btn-primary">
<input type="radio" name="options" id="option1" value="1"
data-bind="bsChecked: optionsValue"> Option 1
</label>
Demostración original con Bootstrap 3.0.2 JSFiddle .
Demostración actualizada usando Bootstrap 3.2.0 JSFiddle .
No me puedo atribuir el mérito de esto, ya que una vez en mis compañeros de trabajo se le ocurrió, pero funciona muy bien.
<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>
cambiar el controlador @nemesv propuesto para ser esto: y funcionó en mi aplicación muy bien.
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);
}
}
Knockstrap parece ser un enlace entre Bootstrap y Knockout, y parece mantenerse bastante actualizado. Con respecto a los botones de radio en específico, utilizan este código:
// 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);
}
}
};