Knockout JS: Dynamically adding and removing table row - javascript

I am using knockout js here.
I have a HTML table and the table has 4 columns. I have button to add a row to table and then remove button against each row to delete it. HTML table as below.
<table class="table table-bordered">
<thead class="mbhead">
<tr class="mbrow">
<th>Input</th>
<th>First Name</th>
<th>Last Name</th>
<th>Address</th>
</tr>
</thead>
<tbody data-bind="foreach: items">
<tr>
<td>
<select class="form-control common-input-text" data-bind="event: { change: $root.addNewItem }">
<option value="">One</option>
<option value="">Two</option>
<option value="">Three</option>
</select>
</td>
<td><span class="input-small" data-bind="value: firstName" /></td>
<td><span class="input-small" data-bind="value: lastName" /></td>
<td><span class="input-small" data-bind="value: address" /></td>
<td>
<input type="button" value="Remove Row" data-bind="click: removeRow" class="btn btn-danger" />
</td>
</tr>
</tbody>
</table>
<input type="button" value="Add Row" class="btn btn-primary" data-bind="click: addRow" />
My knockout as:
(function () {
var ViewModel = function () {
var self = this;
//Empty Row
self.items = ko.observableArray([]);
self.addRow = function () {
self.items.push(new Item());
};
self.removeRow = function (data) {
self.items.remove(data);
};
}
var Item = function (fname, lname, address) {
var self = this;
self.firstName = ko.observable(fname);
self.lastName = ko.observable(lname);
self.address = ko.observable(address);
};
vm = new ViewModel()
ko.applyBindings(vm);
})();
When I click add row, it adds the first row but gives me console error:
knockout.js:73 Uncaught ReferenceError: Unable to process binding "click: >function (){return removeRow }"
Message: removeRow is not defined
When I click add row again it gives me another console error:
Uncaught Error: You cannot apply bindings multiple times to the same element.
And when I click removeRow nothing happens.
When I comment out the code for removeRow, I am able to add a new row.
Not sure where I am going wrong.
Here is my jsfiddle:
https://jsfiddle.net/aman1981/nz2dtud6/2/

When using the data binding foreach the context changes to the context of its childs. To access the context of the parent, you need to add $parent to access removeRow
<td>
<input type="button" value="Remove Row" data-bind="click: $parent.removeRow" class="btn btn-danger" />
</td>

Since your <tbody> defines a new scope by using a foreach: items binding, you need to use $parent.removeRow to refer to the method.
<input data-bind="click: $parent.removeRow" type="button" value="Remove Row" />
See BindingContext

Related

Knockout JS: Get dropdown selected data and populate other fields

Using Knockout JS here.
I have a HTML table and the table has 4 columns. I have button to add a row to table and then remove button against each row to delete it. I also have a dropdown in the first column of this table. The dropdown is populated from the button click event outside the table. Below is my html:
<table class="table table-bordered">
<thead class="mbhead">
<tr class="mbrow">
<th>Input</th>
<th>First Name</th>
<th>Last Name</th>
<th>Address</th>
</tr>
</thead>
<tbody data-bind="foreach: items">
<tr>
<td>
<select class="form-control" data-bind="options: $parent.ddl, optionsText: 'name', value: $parent.selectedColumnValue, optionsCaption: '--Select--', event: { change: $parent.ddlChanged }">
</select>
</td>
<td><span class="input-small" data-bind="value: firstName" /></td>
<td><span class="input-small" data-bind="value: lastName" /></td>
<td><span class="input-small" data-bind="value: address" /></td>
<td>
<input type="button" value="Remove Row" data-bind="click: $parent.removeRow" class="btn btn-danger" />
</td>
</tr>
</tbody>
</table>
<div class="col-xs-12 col-sm-6">
<input type="button" value="Add Row" class="btn btn-primary" data-bind="click: addRow" />
<input type="submit" value="Get Data" data-bind="click: GetData" class="btn btn-primary" />
</div>
My knockout code can be seen in the jsfiddle link as below.
What I am looking for is:
When the user selects the dropdown the selected dropdown text gets populated to that rows one column and the value gets populated to that rows other columns/cell.
My Issue:
1.) I am not able to get the selected text and selected value from the dropdown
When the dropdown selected index change event is fired the event param has the below value as seen in console:
firstName : ƒ c()
lastName: ƒ c()
address : ƒ c()
proto : Object
2.) Secondly I don't know how I could update other column values based on the dropdown selection.
The json that gets binded to dropdown is like below:
'[{"FirstName":"Alex","LastName":"Sanders","Address":123},{"FirstName":"Sam","LastName":"Billings","Address":"Mahony Street"}]';
Here is my fiddle:
https://jsfiddle.net/aman1981/njbyumrs/12/
Inputs are appreciated.
You've got some parent and level stuff mixed up, and your table was binding on value instead of text. I moved the drop down binding, selectedValue to Item since it's at the row level and not the parent level. I used the KO with binding to show the values inside selectedValue for that part of the HTML.
I also added a <pre> tag with the KO values so you can see what happens as you interact with it and the KO model data changes.
Side note: The three properties in Item don't need to be observable in this demo as the values do not change while on the screen.
var ViewModel = function() {
var self = this;
//Empty Row
self.items = ko.observableArray([new Item()]);
self.ddl = ko.observableArray();
self.addRow = function() {
self.items.push(new Item());
};
self.removeRow = function(data) {
self.items.remove(data);
};
self.GetData = function() {
if (self.ddl().length === 0) {
var item1 = new Item("Alex", "Sanders", "Maple Street");
self.ddl.push(item1);
var item2 = new Item("Sam", "Billings", "Mahony Street");
self.ddl.push(item2);
}
}
}
var Item = function(fname, lname, address) {
var self = this;
self.firstName = ko.observable(fname);
self.lastName = ko.observable(lname);
self.address = ko.observable(address);
self.selectedValue = ko.observable();
};
vm = new ViewModel()
vm.GetData();
ko.applyBindings(vm);
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table class="table table-bordered">
<thead class="mbhead">
<tr class="mbrow">
<th>Input</th>
<th>First Name</th>
<th>Last Name</th>
<th>Address</th>
</tr>
</thead>
<tbody data-bind="foreach: items">
<tr>
<td><select class="form-control" data-bind="options: $parent.ddl, optionsText: 'firstName', value: selectedValue, optionsCaption: '--Select--'"> </select></td>
<td data-bind="with: selectedValue">
<span data-bind="text: firstName"></span>
</td>
<td data-bind="with: selectedValue">
<span data-bind="text: lastName"></span>
</td>
<td data-bind="with: selectedValue">
<span data-bind="text: address"></span>
</td>
<td>
<input type="button" value="Remove Row" data-bind="click: $parent.removeRow" class="btn btn-danger" />
</td>
</tr>
</tbody>
</table>
<div class="col-xs-12 col-sm-6">
<input type="button" value="Add Row" class="btn btn-primary" data-bind="click: addRow" />
</div>
<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>

AngularJS ng-click function not reached

When I'm adding new product to a product list its not working. So the products get loaded well but the ng-click function does not getting called. (The alert I put in the addProduct function is not executed).
HTML
<div ng-controller="ProductController as ProductCtrl">
Zoeken <input type="text" ng-model="search" placeholder="Search" />
<div>
Filter Type
<button ng-repeat="cat in categories" ng-click="filterProductsByCategory(cat)">{{cat}}</button>
</div>
<table cellpadding="5" cellspacing="0" border="1">
<tr>
<th>ID</th>
<th>Product</th>
<th>Type</th>
<th>Price</th>
<th>Toevoegen</th>
</tr>
<tr ng-repeat="product in ProductCtrl.products">
<td>{{product.id}}</td>
<td>{{product.name}}</td>
<td>{{product.type}}</td>
<td>{{product.price}}</td>
<td></td>
</tr>
<tr><td></td>
<td><input type="text" name="newProduct.name" ng-model="productCtrl.newProduct.name"></td>
<td><input type="text" name="newProduct.price" ng-model="productCtrl.newProduct.price"></td>
<td><input type="text" name="newProduct.type" ng-model="productCtrl.newProduct.type"></td>
<td><a href ng-click="productCtrl.addProduct()">Product {{productCtrl.newProduct.name}} toevoegen</a></td></tr>
</table>
Any help would appreciated.
The controller:
app.controller('ProductController', function(productService) {
var that = this;
productService.getProducts().success(function(data) {
that.products = data;
});
this.newProduct = "";
this.addProduct = function() {
that.products.push(this.newProduct);
window.alert(that.products);
this.newProduct = "";
};
});
Its a typo, your controller alias name is ProductCtrl not productCtrl, Additionally you need to change your ng-model's to correct the same thing
Replace productCtrl to ProductCtrl will fix your issue.
<tr>
<td>
<input type="text" name="newProduct.name" ng-model="ProductCtrl.newProduct.name"/>
</td>
<td>
<input type="text" name="newProduct.price" ng-model="ProductCtrl.newProduct.price"/>
</td>
<td>
<input type="text" name="newProduct.type" ng-model="ProductCtrl.newProduct.type"/>
</td>
<td>
<a href ng-click="ProductCtrl.addProduct()">
Product {{productCtrl.newProduct.name}} toevoegen
</a>
</td>
</tr>

remove readonly attribute from multiple fields on clicking button in jquery

I have a table with the details of the student. These fields are readonly field and can be edited on clicking the edit button. But I am having problem to select all the input fields of that row at once on clicking the edit button.
Here is my html code
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Checklist</th>
<th>Id</th>
<th>Student Name</th>
<th>Address</th>
<th>Phone</th>
<th>Class</th>
<th colspan="2">Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="checkbox" id="editCheck" class="btn1" />
<input type="checkbox" id="deleteCheck" />
</td>
<td>1</td>
<td><input type="text" class="form-control item" readonly="readonly" /></td>
<td><input type="text" class="form-control item" readonly="readonly" /></td>
<td><input type="text" class="form-control item" readonly="readonly" /></td>
<td>12</td>
<td><button type="button" class="btn btn-info btn-xs" id="btn1">Edit</button></td>
<td><button type="button" class="btn btn-danger btn-xs" id="dbtn1">Delete</button></td>
</tr>
<tr>
<td>
<input type="checkbox" id="editCheck" class="btn2" />
<input type="checkbox" id="deleteCheck" />
</td>
<td>1</td>
<td><input type="text" class="form-control item" readonly="readonly" /></td>
<td><input type="text" class="form-control item" readonly="readonly" /></td>
<td><input type="text" class="form-control item" readonly="readonly" /></td>
<td>12</td>
<td><button type="button" class="btn btn-info btn-xs" id="btn2">Edit</button></td>
<td><button type="button" class="btn btn-danger btn-xs" id="dbtn2">Delete</button></td>
</tr>
</tbody>
</table>
And here is the jquery. I have made the checkbox selected on pressing the edit button.
$(document).ready(function(){
$('.btn.btn-info.btn-xs').click(function(){
var newClass = $(this).attr('id');
$('.'+newClass).prop('checked','true');
});
});
</script>
You can simply add this into your click handler
$(this).closest('tr').find('input').removeAttr('readonly');
Which finds the tr containing the clicked button, locates all of its input elements, and removes their readonly attribute.
Live example: http://jsfiddle.net/zxsq0m5n/
Incidentally, you could use the same trick to locate your checkbox, negating the need to tie it together with the edit button using id/class
$('.btn.btn-info.btn-xs').click(function(){
var $tr = $(this).closest('tr')
$tr.find('input:checkbox').first().prop('checked','true');
$tr.find('input').removeAttr('readonly');
});
Live example: http://jsfiddle.net/zxsq0m5n/1/
$('.btn.btn-info.btn-xs').on('click', function (e) {
var $btn = $(e.currentTarget),
newClass = '.' + $btn.attr('id');
$btn
.parents('tr')
.find(newClass).prop('checked', true)
.end()
.find('input').removeAttr('readonly');
});
You can update your code to following
Logic - Get the tr row i.e. parent of parent of input -> tr -> td -> button. Then for that row find all the input and remove the attribute. Please note you can add conditions if required
$('.btn.btn-info.btn-xs').click(function(){
var newClass = $(this).attr('id');
$('.'+newClass).prop('checked','true');
$(this).parent().parent().find("input").each(function(){
$(this).removeAttr("readonly");
});
});

Create array based on user entered inputs

I have table/form where the length is dynamic (user can add/delete rows). I would like to create an array onsubmit containing the values the user has entered and use console.log to show the array values as well as the length of array.
HTML
<div class="container">
<form id="online1" action="#">
<table class="table" id="tblData">
<thead>
<tr>
<th>Youtube ID</th>
<th>Add/Delete</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="text" class="form-control song-input" placeholder="5YCcad6hklE" />
</td>
<td>
<button type="button" class="btn btn-default btn-add">Add</button>
</td>
</tr>
<tr>
<td>
<input type="text" class="form-control" placeholder="t6Lr8ggJWi4" />
</td>
<td>
<button type="button" class="btn btn-danger btn-del">Del</button>
</td>
</tr>
<tr>
<td>
<input type="text" class="form-control" placeholder="YmzfaapaPMA" />
</td>
<td>
<button type="button" class="btn btn-danger btn-del">Del</button>
</td>
</tr>
</tbody>
</table>
<button type="button" class="btn btn-primary">Submit</button>
</form>
jQuery
jQuery(".btn-primary").click(function(){
var values = [];
$('.yt-mix').each(function() {
values[this.name] = this.value;
});
var mix_size = values.length;
console.log(values); // "5YCcad6hklE", "t6Lr8ggJWi4", "YmzfaapaPMA"
console.log(mix_size); // 3 rows
});
Working on this fiddle http://jsfiddle.net/1jmjdxLL/
You can use each to loop over all the textboxes. :text pseudo-selector selects all the textboxes.
$(document).ready(function() {
$(document).on('click', '.btn-primary', function() {
var values = [];
$('#tblData :text').each(function() {
if ($(this).val()) {
values.push($(this).val());
}
});
console.log(values);
console.log(values.length);
});
});
Demo: https://jsfiddle.net/tusharj/1jmjdxLL/1/
$(".btn-primary").click(function(){
var values = [];
$('#tblData input').each(function() {
values.push($(this).val());
});
var mix_size = values.length;
console.log(values); // "5YCcad6hklE", "t6Lr8ggJWi4", "YmzfaapaPMA"
console.log(mix_size); // 3 rows
});

How can I get multiple views working in my viewmodel in knockout.js

I am able to get the ImagesInputView to work in my ViewModel, but the other two will not. The way I know they are not working, is that I cannot delete the other two, and they add more than I am allowing them to.
Here is my html code:
<div id="image_inputs" class="image_gallery_area">
<table>
<thead>
<tr>
<th>
<label class="ae_field_label">Images:</label>
</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: imageInput">
<tr>
<td>
<input class="ae_filed_value" type="file" data-bind="value: value" />
</td>
<td>
<button type="button" class="minus" data-bind="click: $root.removeImageInput">X</button>
</td>
</tr>
</tbody>
</table>
<button type="button" class="plus" data-bind="click: addImageInput, enable: imageInput().length < 8">Add Image</button>
</div>
<div id="app_inputs" class="app_link_area">
<table>
<thead>
<tr>
<th><label class="ae_field_label">App Download Links:</label></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: appInput">
<tr>
<td>
<label class="ae_field_label" for="appLinkName">Link Name:</label>
</td>
<td>
<input class="ae_filed_value" data-bind="value: appLinkName" maxlengthe="255" />
</td>
<td rowspan="2">
<button type="button" class="minus" data-bind="click: $root.appInput">X</button>
</td>
</tr>
<tr>
<td>
<label class="ae_field_label" for="appURL">URL:</label>
</td>
<td>
<input class="ae_filed_value" data-bind="value: appURL" maxlengthe="255" />
</td>
</tr>
</tbody>
</table>
<button type="button" class="plus" data-bind="click: appInput, enable: appInput().length < 4">Add App Input</button>
</div>
<div id="web_link_inputs" class="web_thumbs_area">
<table>
<thead>
<tr>
<th>
<label class="ae_field_label">Web Thumbnail Links:</label>
</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: webLinkInput">
<tr>
<td>
<label class="ae_field_label" for="webLinkName">Link Name:</label>
</td>
<td>
<input class="ae_filed_value" data-bind="value: webLinkName" maxlengthe="255" />
</td>
<td rowspan="2">
<button type="button" class="minus" data-bind="click: $root.webLinkInput">X</button>
</td>
</tr>
<tr>
<td>
<label class="ae_field_label" for="webURL">URL:</label>
</td>
<td>
<input class="ae_filed_value" data-bind="value: webURL" maxlengthe="255" />
</td>
</tr>
</tbody>
</table>
<button type="button" class="plus" data-bind="click: webLinkInput, enable: webLinkInput().length < 2">Add Web Thumbnail</button>
</div>
Here is my knockout code:
function ImageInputView (value){
var self = this;
self.value = value
}
function AppLinkView (appLinkName, appURL){
var self = this;
self.appLinkName = appLinkName;
self.appURL = appURL;
}
function WebLinkView (webLinkName, webURL){
var self = this;
self.webLinkName = webLinkName;
self.webURL = webURL;
}
function ViewModel(){
var self = this;
self.imageInput = ko.observableArray();
self.addImageInput = function() {
self.imageInput.push(new ImageInputView(""));
}
self.removeImageInput = function(imageInput) {
self.imageInput.remove(imageInput);
}
self.appInput = ko.observableArray();
self.addAppInput = function() {
self.appInput.push(new AppLinkView("",""));
}
self.removeAppInput = function(appInput) {
self.appInput.remove(appInput);
}
self.webLinkInput = ko.observableArray();
self.addWebLinkInput = function() {
self.webLinkInput.push(new WebLinkView("",""));
}
self.removeWebLinkInput = function(webLinkInput) {
self.webLinkInput.remove(webLinkInput);
}
}
ko.applyBindings(new ViewModel());
here is my jsFiddle
I get a "RefferenceError: variableName is not defined" in my javascript errors.
Thank you in advance for any help. I am new to knockout, and have been at this for a few hours, and can't seem to figure it out or find any helpful sources in my searching.
Here is the fiddle that works http://jsfiddle.net/qj3y9/
You were missing a couple of things
This
<button type="button" class="plus" data-bind="click: appInput, enable: appInput().length < 4">Add App Input</button>
Must be changed to
<button type="button" class="plus" data-bind="click: addAppInput, enable: appInput().length < 4">Add App Input</button>
Same for
<button type="button" class="plus" data-bind="click: webLinkInput, enable: webLinkInput().length < 2">Add Web Thumbnail</button>
Must be changed to
<button type="button" class="plus" data-bind="click: addWebLinkInput, enable: webLinkInput().length < 2">Add Web Thumbnail</button>
Also your remove buttons were not bound to the correct function in the view model. You can look in the fiddle It is fixed now there.

Categories

Resources