En rapport avec: Groupe de boutons radio 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:
J'ai le code ci-dessus que j'essaye de faire fonctionner comme je l'attends. Le problème est que lorsque data-toggle="buttons"
est ajouté au div de btn-group (comme dans l'exemple Bootstrap 3 ), la liaison par knock-out cesse de fonctionner. Si je laisse la bascule de données hors du groupe de boutons, la liaison fonctionne comme prévu, mais le groupe de boutons est affreux. Je sais que cela ne fonctionnait pas dans Bootstrap 2 car ils n'utilisaient pas réellement l'entrée radio pour leur style. Comment se fait-il qu'il refuse de travailler maintenant même s'ils le font?
Les boutons bootstrap et le knockout checked
binding ne lisent toujours pas Nice:
click
à l'intérieur de la liaison checked
pour activer le changement d'observable sous-jacente.preventDefault()
pour que KO ne soit pas averti du clic.Une solution possible consiste à créer un gestionnaire de liaison personnalisé dans lequel vous vous abonnez à l'événement change
(déclenché par bootstrap sur toogle) et définissez la valeur de vos observables à cet emplacement:
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);
}
}
}
Et utilisez-le à vos yeux avec:
<label class="btn btn-primary">
<input type="radio" name="options" id="option1" value="1"
data-bind="bsChecked: optionsValue"> Option 1
</label>
Démo originale utilisant Bootstrap 3.0.2 JSFiddle .
Mise à jour de la démo avec Bootstrap 3.2.0 JSFiddle .
Je ne peux pas en croire le mérite, car une fois sur mes collègues l'a proposé mais cela fonctionne vraiment 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>
changer le gestionnaire @nemesv a proposé d'être ceci: et cela a bien fonctionné dans mon application.
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 semble être une liaison entre Bootstrap et Knockout, et il semble être assez bien tenu à jour. En ce qui concerne les boutons radio en particulier, ils utilisent ce code:
// 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);
}
}
};
Il existe une approche beaucoup plus facile trouvée ici .
Nous pouvons simplement ne pas utiliser l'attribut data-toggle
et essayer d'obtenir le même comportement en utilisant la liaison de données de knockout.
C'est presque aussi simple que les radios html natives.
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>
Il y a une solution simple ici qui, je suis surpris, n'a pas encore été mentionnée.
1) Supprimez le data-toggle="buttons"
du btn-group
Maintenant, KO devrait fonctionner
2) Bootstrap applique des CSS personnalisés pour ces entrées afin de les masquer, ce que nous venons de perdre en supprimant le basculement des données. Appliquez donc le CSS suivant à la radio input :
position: absolute;
clip: rect(0,0,0,0);
pointer-events: none;
3) La dernière chose dont nous avons besoin est que le bouton de l'option sélectionnée ait la classe active. Ajoutez les éléments suivants aux boutons label :
data-bind="css: {active: optionsValue() == valueOfThisInput}"