I am quite new to KnockoutJS and have noticed there is quite a lot out there about radio buttons and Knockout, but unfortunately none of these could help me.
I have two radio buttons in two separate containers and can select one of them, but when selecting the other one it unfortunately doesn't change the selection even though value equals the value passed to the checked binding. Here is the simplified html:
<div>
<label>
<input
type="radio"
name="type-1"
data-bind="click: function(data, event) { setUserClass('User_Classification_1', data, event); }, checked: userClass"
value="User_Classification_1"/>
Some Text
</label>
</div>
<div>
<label>
<input
type="radio"
name="type-1"
data-bind="click: function(data, event) { setUserClass('User_Classification_2', data, event);}, checked: userClass"
value="User_Classification_2"/>
Some Text.
</label>
</div>
And here the JS :
var certViewModel = function() {
var self = this;
self.userClass = ko.observable(undefined);
self.setUserClass = function(uClass, data, event) {
self.userClass(uClass);
console.log(self.userClass());
};
};
var viewModel = new certViewModel();
ko.applyBindings(viewModel);
Here is an example in a fiddle that shows the problem:
https://jsfiddle.net/1vu0skpg/
EDIT: Fixed it myself. Returning true from the click handlers fixed the issue.
Just throwing in my two cents...
HTML
<div>
<label>
<input
type="radio"
name="type-1"
data-bind="checked: userClass"
value="User_Classification_1"/>
Some Text
</label>
</div>
<div>
<label>
<input
type="radio"
name="type-1"
data-bind="checked: userClass"
value="User_Classification_3"/>
Some Text.
</label>
</div>
This is the selected radio: <strong><span data-bind="text: userClass"></strong>
Script
var certViewModel = function() {
var self = this;
self.userClass = ko.observable();
};
var viewModel = new certViewModel();
ko.applyBindings(viewModel);
I think you were way overthinking this, unless you were triyng to do something really specific?
https://jsfiddle.net/1vu0skpg/5/
Related
I am currently learning localStorage and tried to implement some basic function using checkbox, localStorage. Basically what I am trying to implement is whenever the checkbox is checked, I want them are keep checked after page reload.
view:
<div>
<label><input type="checkbox"value="<%=obj.id%>" name="selected" class="select_class"></label>
</div>
JS
$("#save_button").click(function(){
var selecteditems = [];
if (!$("input[name='selected']:checked").is(":checked")) {
localStorage.removeItem('chx');
//if checkboxs are not checked, remove the setItem
}
else{
$("input[name='selected']:checked").each(function(){
var checked = $(this).val();
if(checked){
selecteditems.push(checked);
localStorage.setItem('chx', JSON.stringify(selecteditems));
var localcheckdata =JSON.parse(localStorage.getItem('chx'));
}
$.each($("input[name='selected']"), function(){
localcheckdata.push($(this).val());
});
I will very appreciate yours helps and if you don't mind could you please let me know what the problem is?
You need to store the values on save, getting the ID of each checkbox element in your dom.
Then once the page loads back, you have to reiterate the values stored in local storage and then get update the dom
Your code had some unknown variables, hence this example below :
var checkboxValues = JSON.parse(localStorage.getItem('checkboxValues')) || {},
$checkboxes = $("#checkbox-container :checkbox");
// on save button clicked
$("#save_button").click(function(){
$checkboxes.each(function(){
checkboxValues[this.id] = this.checked;
});
localStorage.setItem("checkboxValues", JSON.stringify(checkboxValues));
});
// On page load
$.each(checkboxValues, function(key, value) {
$("#" + key).prop('checked', value);
});
<div id="checkbox-container">
<div>
<label for="option1">Option 1</label>
<input type="checkbox" id="option1">
</div>
<div>
<label for="option2">Option 2</label>
<input type="checkbox" id="option2">
</div>
<div>
<label for="option3">Option 3</label>
<input type="checkbox" id="option3">
</div>
</div>
I've been playing around with KnockoutJS in order to gain a better understanding of how it works, which eventually leads to some unconventional coding.
This is the script:
var ViewModel = function() {
var self = this;
self.counter = ko.observable(0);
self.someFruits = ko.observableArray();
self.createbox = function (){
var value = {
name: self.counter(),
isChecked: ko.observable(true)
};
self.someFruits.push(value);
$("#div1").append('<div><input type="checkbox" data-bind="checked: someFruits()[' + self.counter() + '].isChecked" /> Cherry</div>');
$("#div2").append('<div><input type="checkbox" data-bind="checked: someFruits()[' + self.counter() + '].isChecked" /> Cherry</div>');
self.counter(self.counter() + 1);
}
};
ko.applyBindings(new ViewModel());
And here is the HTML:
<div>
<div>
<button data-bind="click: createbox" type="button" class="btn btn-warning">Create Box</button>
</div>
<div id="div1">
</div>
<br>
<br>
<div id="div2">
</div>
</div>
What I'm trying to simulate is the functionality to dynamically create check-boxes that will be data-bound to an observable object inside an observable-array. So I made a button that will push a new object that will contain a ko.observable into an observableArray. Then use Jquery to append HTML markups to create new checkboxes. I append two identical checkboxes each time to different div just to see if they are updating according to the bound object.
It is brutish, and ideally, I shouldn't use JQuery for these purposes, and perhaps a foreach would be nice here. But I'd still like to understand why this isn't working when I thought it should.
EDIT: For example, if I click the button 3 times, 3 checkboxes will be created for each div, making it a total of 6 checkboxes in the enitre page. If I check the first checkbox in the first id=div1, then the first checkbox in id=div2 should also update equally. I've been using JSFiddle to test this, and the checkboxes won’t automatically update when its counterpart is clicked.
In knockout, you rarely have to append/ remove things from the UI using jquery. Your viewModel should control all the adding, removing or any kind of DOM manipulation. In your case, you are pushing to someFruits observableArray. Use foreach binding to display them.
So, in the below snippet, I have added an input to add new fruits. Also, a computed property which displays the "checked" fruits as and when you change the checkboxes.
var ViewModel = function() {
var self = this;
self.fruitName = ko.observable();
self.someFruits = ko.observableArray([]);
self.createbox = function() {
self.someFruits.push({
name: self.fruitName(),
isChecked: ko.observable(true)
});
// clear the input after a fruit is added
self.fruitName('');
}
// every time "someFruits" or "isChecked" changes this gets computed again
self.checkedFruits = ko.computed(function() {
return self.someFruits()
.filter(a => a.isChecked())
.map(b => b.name);
})
};
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div>
<div>
<input data-bind="value: fruitName" />
<button data-bind="click: createbox" type="button" class="btn btn-warning">Create Box</button>
</div>
<div id="div1" data-bind="foreach:someFruits">
<div>
<input type="checkbox" data-bind="checked: isChecked" />
<span data-bind="text: name"></span>
</div>
</div>
<br> Selected fruits:
<span data-bind="text: checkedFruits"></span>
</div>
Click on Run Code snippet to test it out. Here's a fiddle if you want to play around with it. If you're learning knockout, I think it's better to not include jquery for some time :)
I'm not sure if I'm just stuck in a jQuery mindset but is there a way to update 2 model attributes with one radio button? Currently I have 2 radio buttons with one hidden. The visible one checks the second with an #click event that gets the next input and sets it to true.
var app = new Vue({
data: {
order: {
amount:
type:
}
},
methods: {
selectType: function(e) {
e.currentTarget.getElementSibling.checked = true;
}
}
});
<form>
<input type="radio" v-model="order.amount" value=15 #click="selectType">$15</input><br>
<input type="radio" v-model="order.type" value="small" style="display:none">
<input type="radio" v-model="order.amount" value=15 #click="selectType">$15</input><br>
<input type="radio" v-model="order.type" value="med" style="display:none" #click="selectType">
<input type="radio" v-model="order.amount" value=20 >$20</input><br>
<input type="radio" v-model="order.type" value="large" style="display:none">
</form>
The way I understand it, the v-model syntax is best for binding a single value. You could try to somehow make the value a JSON string and then decode it... but that sounds like a bad idea. Here are three ideas:
Using JQuery and Vue
Instead, you could give the radio buttons attributes for each value you want, and then parse out those attributes on the click callback. For example:
<input type="radio" name="rad" btn-amount="10" btn-type="small" #click="selectType($event)">$15 <br>
<input type="radio" name="rad" btn-amount="15" btn-type="med" #click="selectType">$15<br>
<input type="radio" name="rad" btn-amount="20" btn-type="large" #click="selectType">$20<br>
and then a method:
selectType: function(e) {
this.order.amount = $(e.currentTarget).attr('btn-amount');
this.order.type = $(e.currentTarget).attr('btn-type');
}
Here's a JSFiddle showing it in action.
Using Vue only
Alternatively, you could move the data for the options into the vue instance, rather than placing them on on the radio buttons. For example, add an options array to the data, and iterate over it in the HTML to create the buttons
<div v-for="option in options">
<input type="radio" name="rad" #click="selectType(option)">${{ option.amount }}
</div>
Notice that you can pass the current option in the for loop to the click handler! That means you can write selectType as:
selectType: function(option) {
this.order = option;
}
This is very clean, and what I recommend if you plan on keeping the radio-button functionality simple.
Here is a JSFiddle showing it in action.
Using Vue Components
But, if you plan on making things more complex you may want to encapsulate the radio button functionality into a component.
Consider the template:
<template id="radio-order">
<div>
<input type="radio" :name="group" #click="setOrder">${{ amount }}
</div>
</template>
and its associated component:
Vue.component('radio-order', {
template: '#radio-order',
props: ['group', 'amount', 'type'],
methods: {
'setOrder': function() {
this.$dispatch('set-order', {
amount: this.amount,
type: this.type
})
}
}
});
Now you can make <radio-order> components that dispatch a set-order event when clicked. The parent instance can listen for these events and act appropriately.
Admittedly, this method is more verbose. But, if you're thinking of implementing more complex functionality, it's probably the way to go.
Here's a JSFiddle of it in action.
Of course, there are many more ways to solve the problem, but I hope these ideas help!
I'm developing a single page application using knockout.js and sammy.js.
I know how I can remove one item by attaching a button with a click event to each item like so:
self.deleteItem = function(item) {
self.array.remove(item);
}
I'm trying to figure out how I can use checkboxes to remove multiple items at the same time.
Can someone point me in the right direction?
Thanks!
You can achieve this by adding new array to your vm for storing selected rows. Bind it to checkboxes using checked binding:
function ViewModel() {
var self = this;
self.items = ko.observableArray(["One", "Two", "Three"]);
self.selectedItems = ko.observableArray();
self.deleteSelected = function () {
self.items.removeAll(self.selectedItems());
self.selectedItems.removeAll();
}
}
ko.applyBindings(new ViewModel());
<div data-bind="foreach: items">
<input type="checkbox" data-bind="value: $data, checked: $parent.selectedItems" />
<span data-bind="text: $data"></span>
<br/>
</div>
<input type="button" value="Remove Selected" data-bind="click: deleteSelected" />
Here is an example: http://jsfiddle.net/zvFnW/
I have some checkboxes bound to an array in my model. This works great, when you check a box the array is updated accordingly.
However when the value has changed i wish to call a method on my model to filter the results given the new values. I have tried hooking up the change event but this seems to have the values prior to the change rather than after the change.
I have illustrated my issue in a jsfiddle http://jsfiddle.net/LpKSe/ which might make this make more sense.
For completeness my code is repeated here.
JS
function SizeModel() {
var self = this;
self.sizes = ko.observableArray(["small", "medium", "large"]);
self.sizes2 = ko.observableArray(["small", "medium", "large"]);
self.getResults = function(e) {
alert(self.sizes());
};
self.getResults2 = function(e) {
alert(self.sizes2());
};
}
$(document).ready(function() {
sizeModel = new SizeModel();
ko.applyBindings(sizeModel);
});
Html
<h3>Size
<input type="checkbox" value="small" data-bind=" checked: sizes, event:{change: getResults}"/>
<span class='headertext'>Small</span>
<input type="checkbox" value="medium" data-bind=" checked: sizes, event:{change: getResults}" />
<span class='headertext'>Medium</span>
<input type="checkbox" value="large" data-bind=" checked: sizes, event:{change: getResults}" />
<span class='headertext'>Large</span>
</h3>
<h3>Size
<input type="checkbox" value="small" data-bind=" checked: sizes2, event:{click: getResults2}"/>
<span class='headertext'>Small</span>
<input type="checkbox" value="medium" data-bind=" checked: sizes2, event:{click: getResults2}" />
<span class='headertext'>Medium</span>
<input type="checkbox" value="large" data-bind=" checked: sizes2, event:{click: getResults2}" />
<span class='headertext'>Large</span>
</h3>
You don't need the change event. If you subscribe to the observableArray you will be notified when it changes, and be passed the updated array: http://jsfiddle.net/jearles/LpKSe/53/
function SizeModel() {
var self = this;
self.sizes = ko.observableArray(["3", "2", "1"]);
self.sizes.subscribe(function(updated) {
alert(updated);
});
}
In your fiddle you're missing commas in your data-bind-s, here's a fixed example: http://jsfiddle.net/4aau4/1/
Re the problem - it might be either a KnockoutJS-related problem (i.e. it updates the observableArray after the change event is fired), or something similar to what I stucked on some time ago: Checkboxes are being checked before click handler is even called
EDIT:
What a tough Sunday, I think I'm still not awake :)
Take a look at this snippet: http://jsfiddle.net/4aau4/2/ - it looks like DOM is properly updated and it's ko.observableArray that lags behind. ($('input:checked').length says how many checkboxes are actualy checked).