I'm fairly new to Knockout and JavaScript in general, I am trying to figure out how to get this working I am trying to create a simple shopping list application using knockout.js I have it currently where it's adding the Item Name and quantity to the table however it's adding them both as separate rows instead of row and column.
HTML Table issue
var SimpleListModel = function(items) {
self = this;
self.items = ko.observableArray(items);
self.itemToAdd = ko.observable("");
self.quantityToAdd = ko.observable("");
self.deleteItem = function(item) {
self.items.remove(item);
return self.items;
}
self.addItem = function() {
if (self.itemToAdd() != "") {
self.items.push(self.itemToAdd());
self.itemToAdd("");
}
if (self.quantityToAdd() != "") {
self.items.push(self.quantityToAdd());
self.quantityToAdd("");
}
}.bind(this);
};
ko.applyBindings(new SimpleListModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<form data-bind="submit: addItem">
New item:<input data-bind='value: itemToAdd, valueUpdate: "afterkeydown"' /> Quantity:
<input data-bind='value: quantityToAdd, valueUpdate: "afterkeydown"' />
<button type="submit" data-bind="enable: itemToAdd().length > 0">Add</button>
</form>
<p></p>
<table border="1">
<thead>
<th>Item</th>
<th>Quantity</th>
<th>Remove</th>
</thead>
<tbody data-bind="foreach:items">
<tr>
<td data-bind="text: $data"></td>
<td><input type="button" data-bind="click:$root.deleteItem" value="X"></input>
</td>
</tr>
</tbody>
</table>
the expected result needs to be:
Expected result image
That's because you're adding the item and the quantity to the items array as separate items. You'll want to use an object instead:
var SimpleListModel = function(items) {
self = this;
self.items = ko.observableArray(items);
self.itemToAdd = ko.observable("");
self.quantityToAdd = ko.observable("");
self.deleteItem = function(item) {
self.items.remove(item);
return self.items;
}
self.addItem = function() {
if (self.itemToAdd() && self.quantityToAdd()) {
self.items.push({ item: self.itemToAdd(), quantity: self.quantityToAdd() });
self.itemToAdd("");
self.quantityToAdd("");
}
};
};
ko.applyBindings(new SimpleListModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<form data-bind="submit: addItem">
New item:<input data-bind='value: itemToAdd, valueUpdate: "afterkeydown"' /> Quantity:
<input data-bind='value: quantityToAdd, valueUpdate: "afterkeydown"' />
<button type="submit" data-bind="enable: itemToAdd().length > 0">Add</button>
</form>
<p></p>
<table border="1">
<thead>
<th>Item</th>
<th>Quantity</th>
<th>Remove</th>
</thead>
<tbody data-bind="foreach:items">
<tr>
<td data-bind="text: item"></td>
<td data-bind="text: quantity"></td>
<td><input type="button" data-bind="click:$root.deleteItem" value="X"></input>
</td>
</tr>
</tbody>
</table>
Related
Why event does not occur on items when I am changing their values?
$(".target").change(function () {
alert($(".target").val());
});
function MyViewModel() {
var self = this;
self.items = ko.observableArray();
self.items.push({ name: 'Jhon' });
self.items.push({ name: 'Smith' });
}
ko.applyBindings(new MyViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<h2>Index</h2>
<table>
<thead>
<tr>
<th>Passenger name</th>
</tr>
</thead>
<tbody data-bind="foreach: items">
<tr>
<td><input class="target" data-bind="value: name" /></td>
</tr>
</tbody>
</table>
Knockout generates new html upon applying foreach bindings, so you need to register your event globally, just like option 1 or you can do the neet binding of knockout just like in option 2.
I recommend option 2 to use knockout bindings.
$(document).on('change',".target",function () {
alert('option 1 - ' + $(this).val());
});
function MyViewModel() {
var self = this;
self.items = ko.observableArray();
self.items.push({ name: 'Jhon' });
self.items.push({ name: 'Smith' });
self.alert = function(data, e){
alert('option 2 - ' + data.name);
};
}
ko.applyBindings(new MyViewModel(), document.getElementById('tblSample'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<h2>Index</h2>
<table id="tblSample">
<thead>
<tr>
<th>Passenger name</th>
</tr>
</thead>
<tbody data-bind="foreach: items">
<tr>
<td><input class="target" data-bind="value: name, event:{change:$parent.alert}" /></td>
</tr>
</tbody>
</table>
Hard to describe. Here is a snippet.
(function() {
var PhoneNumber = function() {
this.name = ko.observable();
this.phone = ko.observable();
};
$('[data-mask="phone"]').inputmask({
mask: '(999)999-9999'
});
var vm = {
newNumber: ko.observable(new PhoneNumber()),
numbers: ko.observableArray([]),
};
console.log('vm', vm.numbers());
vm.numbersJson = ko.computed(function() {
return ko.toJSON(vm.numbers);
});
vm.newNumberJson = ko.computed(function() {
return ko.toJSON(vm.newNumber);
});
ko.applyBindings(vm);
//events
function addNewNumber() {
vm.numbers.push(vm.newNumber());
vm.newNumber(new PhoneNumber());
}
function removeNumber() {
var item = ko.dataFor(this);
var remVal = _.reject(vm.Numbers(), function(el) {
return el.name === item.name;
});
vm.Numbers(remVal);
};
$(document).on('click', '#btnAdd', addNewNumber);
$(document).on('click', '.btnRemove', removeNumber);
})();
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet" />
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://rawgit.com/RobinHerbots/jquery.inputmask/3.x/dist/jquery.inputmask.bundle.js"></script>
<table class="table table-condensed table-hover table-striped table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Phone</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach:numbers">
<tr>
<td><span data-bind="text:name"></span>
</td>
<td><span data-bind="text:phone"></span>
</td>
<td class="form-group">
<button class="btn btn-sm btnRemove btn-default" type="button"> <i class="fa fa-trash"></i>
</button>
</td>
</tr>
</tbody>
<tfoot data-bind="with:newNumber">
<tr>
<td>
<input type="text" class="input-sm form-control" data-bind="textInput:name" />
</td>
<td>
<input type="text" class="input-sm form-control" data-mask="phone" data-bind="textInput:phone" />
</td>
<td>
<button class="btn btn-sm btn-default" id="btnAdd" type="button"><i class="fa fa-plus"></i>
</button>
</td>
</tr>
</tfoot>
</table>
<h2>Values</h2>
<p>Numbers: <span data-bind="text:numbersJson"></span>
</p>
<p>New Number:
<span data-bind="text:newNumberJson"></span>
</p>
Please note there is phone inputmask in place when DOM is ready. When we type in the Phone textbox, the input mask is effective.
But after the first record added into the array and the textbox is cleared, the input mask is lost.
Question: how can we keep the input mask on the textbox?
The easiest thing to do is to put mask-setting code in the addNewNumberroutine.
function setMask() {
$('[data-mask="phone"]').inputmask({
mask: '(999)999-9999'
});
}
vm.addNewNumber = function () {
vm.numbers.push(vm.newNumber());
vm.newNumber(new PhoneNumber());
setTimeout(setMask, 0);
};
I made a Fiddle. It would probably be better to do with a custom binding, but, as I said, this is easiest.
Now I've made a custom binding handler in another Fiddle. It saves on searching for elements to mask, because it knows what element it is on.
ko.bindingHandlers.phoneTextInput = {
init: function (element, valueAccessor, allBindings, data, context) {
ko.bindingHandlers.textInput.init(element, valueAccessor, allBindings, data, context);
$(element).inputmask({
mask: '(999)999-9999'
});
}
}
Uncaught ReferenceError: Unable to process binding "foreach: function (){return Educations }"
Uncaught ReferenceError: Unable to process binding "foreach: function (){return WorkExperience }"
I couldn't figure out why the binding is failing.
i have the following two tables one for Education and other for Work Experience, They give the error when i'm trying to bind the both table in one view, If i remove the binding (JS + HTML code) it works fine
HTML:
<div id=divtable1 class="widget widget-simple widget-table">
<table id="Table1" class="table table-striped table-content table-condensed boo-table table-hover">
<thead>
<tr id="Tr1" class="filter">
<th>University<span class="required">*</span></th>
<th>Location <span class="required">*</span></th>
<th></th>
</tr>
</thead>
<tbody data-bind='foreach: Educations'>
<tr>
<td><input type="text" class='span11 required' data-bind="value: SchoolName" /></td>
<td><input type="text" class='span11 required' data-bind="value: Location" /></td>
<td><a href='#' data-bind='click: $root.removeEducation'>Delete</a></td>
</tr>
</tbody>
</table>
<button data-bind='click: $root.addEducation' class="btn btn-blue">Add Education</button>
</div>
<div id="divtable2">
<table id="Table2">
<thead>
<tr id="Tr2" class="filter">
<th>Employer Name<span class="required">*</span></th>
<th>EmployerAddress <span class="required">*</span></th>
<th></th>
</tr>
</thead>
<tbody data-bind='foreach: WorkExperience'>
<tr>
<td><input type="text" class='span11 required' data-bind="value: EmployerName" /></td>
<td><input type="text" class='span11 required' data-bind="value: EmployerAddress" /></td>
<td><a href='#' data-bind='click: $root.removeWorkExperience'>Delete</a></td>
</tr>
</tbody>
</table>
<button data-bind='click: $root.addWorkExperience' class="btn btn-blue">Add Work Experience</button>
</div>
Java Script:
<script type="text/javascript">
var Educations = function (educations) {
var self = this;
self.Educations = ko.mapping.fromJS(educations);
self.addEducation = function () {
self.Educations.push({"SchoolName": ko.observable(""), "Location": ko.observable("")});
};
self.removeEducation = function (education) {
self.Educations.remove(education);
};
};
var viewModel = new Educations(#Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.Educations)));
ko.applyBindings(viewModel);
</script>
<script type="text/javascript">
var WorkExperience = function (workexperiences) {
var self = this;
self.WorkExperience = ko.mapping.fromJS(workexperiences);
self.addWorkExperience = function () {
self.WorkExperience.push({ "EmployerName": ko.observable(""), "EmployerAddress": ko.observable("")});
};
self.removeWorkExperience = function (workexperience) {
self.WorkExperience.remove(workexperience);
};
};
var viewModel = new WorkExperience(#Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.WorkExperience)));
ko.applyBindings(viewModel);
</script>
Also I tried to bind Table1 but it didn't work
ko.applyBindings(viewModel, $('#Table1')[0]);
try adding this <pre data-bind="text: ko.toJSON($data, null, 2)"></pre> to your view. it will output the data that knockout contains in the current context.
Also you have one view and two view models that are trying to bind to it. create one viewmodel with both Educations and WorkExperience as properties.
something like
var vm = {
Educations : educationViewModel,
WorkExperience: workExperienceViewModel
}
ko.applyBindings(vm);
If you want to bind two view-models separately, you must define which section of your view to bind to. You do this by providing the element to ko.applyBindings as the second parameter.
ko.applyBindings(viewModel, document.getElementById("divtable1"));
I have a observable Array and I display that in a select element, for example if I have 4 elements in the array, then I will have 4 Select Element.
When I change the items in the Select list the observable array seems to be unchanged.
Please help me on how to update the observable array.
The jsfiddle for the same is here
HTML Code
<div>
<div>
<table>
<th>Name</th>
<th>Age</th>
<th>Country</th>
<tbody data-bind="foreach: players">
<tr>
<td data-bind="text: name"></td>
<td data-bind="text: age"></td>
<td data-bind="text: country"></td>
<td data-bind="text: trophies"></td>
<td>
<button data-bind="click: $root.editPlayers">Edit</button>
</td>
</tr>
</tbody>
</table>
</div>
<div data-bind="with: editPlayer">
<h3>Edit</h3>
Name:
<input type="text" data-bind="value: name" />
<br/>Age:
<input type="text" data-bind="value: age" />
<br/>Country:
<input type="text" data-bind="value: country" />
<span data-bind="foreach: trophies">
<br/>Tropies:
<select data-bind="options: $root.trophiesList, value:$data, optionsCaption:'Choose..'"></select>
</span>
</div>
</div>
JavaScript Code
var player = function (name, age, country, trophyArray) {
this.name = ko.observable(name);
this.age = ko.observable(age);
this.country = ko.observable(country);
this.trophies = ko.observableArray(trophyArray);
};
var playerModel = function () {
var self = this;
self.players = [
new player('Roger', 32, 'swiss', ['AO','FO','WB','US']),
new player('Murray', 28, 'Scott', ['WB','US'])];
self.editPlayer = ko.observable();
self.trophiesList = ['AO','US','FO', 'WB'];
self.editPlayers = function (player) {
self.editPlayer(player);
}
}
ko.applyBindings(new playerModel());
Remove the following markup:
<span data-bind="foreach: trophies">
and the closing </span> tag.
Then use the selectedOptions binding to manage the trophies array:
<br/>Tropies:
<select data-bind="options: $root.trophiesList, selectedOptions:trophies, optionsCaption:'Choose..'" multiple="true" size="10"></select>
The user can then select/deselect multiple trophies.
Hi I have created an updated fiddle for you
self.players = [
new player('Roger', 32, 'swiss', [{key: ko.observable('AO')},{key:ko.observable('FO')},{key:ko.observable('WB')},{key: ko.observable('US')}]),
new player('Roger', 32, 'swiss', [{key: ko.observable('AO')},{key:ko.observable('FO')},{key:ko.observable('WB')},{key: ko.observable('US')}])];
http://jsfiddle.net/M8m8R/4/
Hope this helps
I want to have multiple data-bindings on my view so my text box contains the right value and when the value changes it calls a function. Basically I want to use amplify.js local storage every time a value on my form changes.
Agency view
<section class="view">
<header>
<button class="btn btn-info btn-force-refresh pull-right"
data-bind="click: refresh">
<i class="icon-refresh"></i>Refresh</button>
<button class="btn btn-info"
data-bind="click: save">
<i class="icon-save"></i>Save</button>
<h3 class="page-title" data-bind="text: title"></h3>
<div class="article-counter">
<address data-bind="text: agency().length"></address>
<address>found</address>
</div>
</header>
<table>
<thead>
<tr>
<th>Agency Name</th>
<th>Category</th>
<th>URL</th>
<th>Number of employees</th>
</tr>
</thead>
<tbody data-bind="foreach: agency">
<tr>
<td>
<!--<input data-bind="value: agencyName" /></td>-->
<input data-bind="value: agencyName, onchange: test()"/>
<td>
<input data-bind="value: category" /></td>
<td>
<input data-bind="value: Url" /></td>
<td>
<input data-bind="value:numberOfEmployees" /></td>
</tr>
<tr>
<td>Activities</td>
<td>Declared Billings</td>
<td>Campaigned Billings</td>
</tr>
<tr>
<td>
<input data-bind="value: activities" /></td>
<td>
<input data-bind="value: declaredBillings" /></td>
<td>
<input data-bind="value: campaignBillings" /></td>
</tr>
</tbody>
</table>
</section>
Agency ViewModel
define(['services/datacontext'], function (dataContext) {
//var myStoredValue = amplify.store("Agency"),
// myStoredValue2 = amplify.store("storeExample2"),
// myStoredValues = amplify.store();
var agency = ko.observableArray([]);
var initialized = false;
var save = function (agency) {
return dataContext.saveChanges(agency);
};
var vm = { // This is my view model, my functions are bound to it.
//These are wired up to my agency view
activate: activate,
agency: agency,
title: 'agency',
refresh: refresh, // call refresh function which calls get Agencies
save: save
};
return vm;
function activate() {
if (initialized) {
return;
}
initialized = true;
return refresh();
}
function refresh() {
return dataContext.getAgency(agency);
}
function test() {
alert("test");
}
});
Every time I type a new value, for example
<input data-bind="value: agencyName, onchange: test()"/>
I want to fire the function test. I then want to store the view model latest data into local storage.
Does anyone know how to do multiple bindings for this?
You should use this binding:
<input data-bind="value: agencyName, event: { change: $parent.onAgencyNameChanged}"/>
Note that I used $parent to refer to the vm Object.
And add an handler to your viewModel.
var vm = {
....
onAgencyNameChanged: function(agency){
// do stuff
}
};
return vm;
Another solution could be to subscribe on the agencyName of all agencies. But I think this isn't suited to this case. After creating the vm you could do this :
ko.utils.arrayForEach(vm.agency(), function(a){
a.agencyName.subscribe(function(){
// do stuff
});
});
I hope it helps.
Try to subscribe your object manually for each element that you have to bind.
Have a look at the explanation and the example in the knockout documentation here.