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>
Related
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>
See this plunker
In jQuery I can get the text of its td and put it in an alert but how can I make it in Angular? Should I make it an javascript native?
This is the script
var app = angular.module('plunker',[]);
app.controller('ctrl',function($scope){
$scope.edit = function(){
alert("ID = " + $scope.id + "\n NAME = " + $scope.name);
};
$scope.items = [
{id:"1",name:"name 1"},
{id:"2",name:"name 2"},
{id:"3",name:"name 3"}
];
});
The HTML
<body ng-controller="ctrl" ng-app="plunker">
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>edit</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in items">
<td ng-model="x.id">{{x.id}}</td>
<td>{{x.name}}</td>
<td><button ng-click="edit()">edit</button></td>
</tr>
</tbody>
</table>
</body>
P.S
The ng-repeat is dynamic so how can I get the value of it?
Just pass the value to the edit function as parameter,
<td><button ng-click="edit(x)">edit</button></td>
and then function would be,
$scope.edit = function(x){
console.log("Id is",x.id);
}
DEMO
var app = angular.module('plunker',[]);
app.controller('ctrl',function($scope){
$scope.edit = function(){
alert("ID = " + $scope.id + "\n NAME = " + $scope.name);
};
$scope.items = [
{id:"1",name:"name 1"},
{id:"2",name:"name 2"},
{id:"3",name:"name 3"}
];
$scope.edit = function(x){
console.log("$$Id is",x.id);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-controller="ctrl" ng-app="plunker">
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>edit</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in items">
<td ng-model="x.id">{{x.id}}</td>
<td>{{x.name}}</td>
<td><button ng-click="edit(x)">edit</button></td>
</tr>
</tbody>
</table>
</body>
Make your function edit() take a variable and then pass it in:
$scope.edit = function(obj){
alert("ID = " + obj.id + "\n NAME = " + obj.name);
};
and then:
<td><button ng-click="edit(x)">edit</button></td>
You can create an array $scope.itemModel of size items.length in your controller and set the model in markup using $index on $scope.itemModel[$index] and then access the item by passing $index to the handler from your controller.
I have an array of objects for which I am showing their properties.
How can add an individual edit functionality to them? Lets say it to be an edit button for each one of the elements of the list.
I want to show input fields instead of text fields when the object is in edit mode, for this I am using the visible binding. So I need a Boolean observable for each of them.
How can I do this without knowing the amount of elements in the list... I also have add and delete, so I would need to add more observables to this array each time a new element is created.
I also tried to give a ko.observable element to my objects but I could not do this.
I like to use an object inside the observableArray. Here is an example of an inline edit feature for as many many rows as needed.
function Employee(emp) {
var self = this;
self.Name = ko.observable(emp.Name);
self.Age = ko.observable(emp.Age);
self.Salary = ko.observable(emp.Salary);
self.EditMode = ko.observable(emp.EditMode);
self.ChangeMode = function() {
self.EditMode(!self.EditMode());
}
}
function viewModel() {
var self = this;
self.Employees = ko.observableArray()
self.Employees.push(new Employee({
Name: "Joe",
Age: 20,
Salary: 100,
EditMode: false
}));
self.Employees.push(new Employee({
Name: "Steve",
Age: 22,
Salary: 121,
EditMode: false
}));
self.Employees.push(new Employee({
Name: "Tom",
Age: 24,
Salary: 110,
EditMode: false
}));
}
var VM = new viewModel();
ko.applyBindings(VM);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table border=1>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Salary</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: Employees">
<tr data-bind="if: !EditMode()">
<td data-bind="text: Name"></td>
<td data-bind="text: Age"></td>
<td data-bind="text: Salary"></td>
<td><button data-bind="click: ChangeMode">Edit</button></td>
</tr>
<tr data-bind="if: EditMode()">
<td>
<input data-bind="value: Name">
</td>
<td>
<input data-bind="value: Age">
</td>
<td>
<input data-bind="value: Salary">
</td>
<td><button data-bind="click:ChangeMode">Save</button></td>
</tr>
</tbody>
</table>
One data binding works well and display automatic data but when I add another copying the same pattern of first, second gives error and do not work. So in below example if I do comment anyone, another do not work.
HTML CODE:
<table>
<thead>
<tr><th>Item Name</th><th>Price</th></tr>
</thead>
<tbody data-bind="foreach: itemsdisplay">
<tr>
<td data-bind="text: Name" />
<td data-bind="text: Price" />
</tr>
</tbody>
</table>
<table>
<thead>
<tr><th colspan="3" style="color:#06C">Stock Market Metal Rates</th></tr>
</thead>
<tbody data-bind="foreach: MetalDisplay">
<tr>
<td data-bind="text: Name" />
<td data-bind="text: Price" />
<td data-bind="text: Dated" />
</tr>
</tbody>
</table>
This is my JS code:
function GetProducts(handleData) {
$.ajax({
url: 'form.php',
type: "post",
data: '',
dataType: 'json',
success: function(data){
handleData(data);
},
error:function(data){
alert('Failed');
}
});
}
function GetMetals(handleData) {
$.ajax({
url: 'form2.php',
type: "post",
data: '',
dataType: 'json',
success: function(data){
handleData(data);
},
error:function(data){
alert('Failed');
}
});
}
$(function () {
var ItemDisplayViewModel = function() {
var self = this;
self.itemsdisplay = ko.observableArray();
self.update = function() {
GetProducts(function(output){
self.itemsdisplay.removeAll();
$.each(output, function (i) {
self.itemsdisplay.push(new product(output[i]));
});
});
}
};
var MetalViewModel = function() {
var self = this;
self.MetalDisplay = ko.observableArray();
self.update = function() {
GetMetals(function(output){
self.MetalDisplay.removeAll();
$.each(output, function (i) {
self.MetalDisplay.push(new metals(output[i]));
});
});
}
};
var ItemDisplayViewModel = new ItemDisplayViewModel();
window.setInterval(ItemDisplayViewModel.update,1000);
ko.applyBindings(ItemDisplayViewModel);
var MetalViewModel = new MetalViewModel();
window.setInterval(MetalViewModel.update,1000);
ko.applyBindings(MetalViewModel);
});
var product = function (data) {
return {
Name: ko.observable(data.Name),
Price: ko.observable(data.Price)
};
};
var metals = function (data) {
return {
Name: ko.observable(data.Name),
Price: ko.observable(data.Price),
Dated: ko.observable(data.Dated)
};
};
Can anyone help please!
You can do the following.
Provide id for both the tables
<table id="item">
<thead>
<tr><th>Item Name</th><th>Price</th></tr>
</thead>
<tbody data-bind="foreach: itemsdisplay">
<tr>
<td data-bind="text: Name" />
<td data-bind="text: Price" />
</tr>
</tbody>
</table>
<table id="metal">
<thead>
<tr><th colspan="3" style="color:#06C">Stock Market Metal Rates</th></tr>
</thead>
<tbody data-bind="foreach: MetalDisplay">
<tr>
<td data-bind="text: Name" />
<td data-bind="text: Price" />
<td data-bind="text: Dated" />
</tr>
</tbody>
</table>
The modify your script to pass the table element to ko.applyBindings method as below.
var itemDisplayViewModel = new ItemDisplayViewModel();
var x = window.setInterval(itemDisplayViewModel.update,1000);
ko.applyBindings(itemDisplayViewModel,document.getElementById("item"));
var metalViewModel = new MetalViewModel();
var y = window.setInterval(metalViewModel.update,1000);
ko.applyBindings(metalViewModel, document.getElementById("metal"));
Please refer 'Activating knockout' section in http://knockoutjs.com/documentation/observables.html - The part of text from the link given below.
For example, ko.applyBindings(myViewModel, document.getElementById('someElementId')). This restricts the activation to the element with ID someElementId and its descendants, which is useful if you want to have multiple view models and associate each with a different region of the page.
What you are doing does not work. you need to do it like this.
ko.applyBindings(ItemDisplayViewModel,document.getElementById('first_div_id'));
ko.applyBindings(MetalViewModel,document.getElementById('second_div_id'));
Or here is another method
var viewModel = function(){
var self = this
self.Item = ko.observable(new ItemDisplayViewModel())
self.Metal = ko.observable(new MetalViewModel())
}
ko.applyBindings(viewModel)
And now
<table data-bind="with:Item">
.
.
.
</table>
<table data-bind="with:Metal">
.
.
.
</table>
And finally
window.setInterval(viewModel.Item().update, 1000);
window.setInterval(viewModel.Metal().update, 1000);
You can take a look at this post for better understanding.
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"));