Store data of a table in Local Storage in jQuery - html

I use jQuery to create a table with CRUD operations. I give the table the ability to sort and filter data. The sort and filter functions work perfectly as well as all CRUD actions.
I have to store the table's data in local storage. Because of this, I add some codes. It functions and stores the data locally not properly. I need to store properly in array format. I need your assistance to address this issue.
<!DOCTYPE html>
<html lang="en">
<head>
<title>CRUD Table jQuery</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-xs-4 col-xs-offset-8">
<input id="searchfield" type="search" class="form-control" placeholder="Search...">
</div>
<div class="col-md-12 text-right">
<input type="button" id="btnAdd" class="btn btn-primary para" value="Add New" />
</div>
</div>
<div class="row pt-3">
<div class="col-md-12 col-sm-12 col-12 p-2 ">
<table id="tblData" class="table table-bordered table-hover table-striped">
<thead>
<tr id="headings">
<th class="num">ID</th>
<th class="text">Name</th>
<th class="text">Address</th>
<th class="num">Age</th>
<th class="tdaction">Action</th>
</tr>
</thead>
<tbody id="searchable">
</tbody>
</table>
</div>
</div>
</div>
<script>
var emptyNewRow = "<tr class='trNewRow'>";
emptyNewRow = emptyNewRow + "<td class='tdID'><input type='text' class='form-control txtID' /></td>";
emptyNewRow = emptyNewRow + "<td class='tdName'><input type='text' class='form-control txtName' /></td>";
emptyNewRow = emptyNewRow + "<td class='tdAddress'><input type='text' class='form-control txtAddress' /></td>";
emptyNewRow = emptyNewRow + "<td class='tdAge'><input type='text' class='form-control txtAge' /></td>";
emptyNewRow = emptyNewRow + "<td class='tdAction'><button class='btn btn-sm btn-success btn-save'> Save</button><button class='btn btn-sm btn-success btn-cancel'> Cancel</button></td>";
emptyNewRow = emptyNewRow + "</tr>";
var rowButtons ="<button class='btn btn-success btn-sm btn-edit' > Edit </button> <button class='btn btn-danger btn-sm' > Delete </button>";
var rowUpdateButtons ="<button class='btn btn-success btn-sm btn-save' > Update </button> <button class='btn btn-danger btn-sm btn-save' > Delete </button> ";
$(document).ready(function () {
$("#btnAdd").click(function () {
$("#tblData tbody").append(emptyNewRow);
});
$('#tblData').on('click', '.btn-save', function () {
const id = $(this).parent().parent().find(".txtID").val();
$(this).parent().parent().find(".tdID").html(""+id+"");
const name = $(this).parent().parent().find(".txtName").val();
$(this).parent().parent().find(".tdName").html(""+name+"");
const address = $(this).parent().parent().find(".txtAddress").val();
$(this).parent().parent().find(".tdAddress").html(""+address+"");
const age = $(this).parent().parent().find(".txtAge").val();
$(this).parent().parent().find(".tdAge").html(""+age+"");
$(this).parent().parent().find(".tdAction").html(rowButtons);
//local storage
let arr = JSON.parse(localStorage.getItem('Data'));
if(arr==null){
let data = ["ID " + id,"NAME "+name,"ADDRESS "+address,"AGE "+ age];
localStorage.setItem('Data', JSON.stringify(data));
}else{
arr.push(id,name,address,age);
localStorage.setItem('Data', JSON.stringify(arr));
}
});
$('#tblData').on('click', '.btn-danger', function () {
$(this).parent().parent().remove();
});
$('#tblData').on('click', '.btn-cancel', function () {
$(this).parent().parent().remove();
});
$('#tblData').on('click', '.btn-edit', function () {
const id =$(this).parent().parent().find(".tdID").html();
$(this).parent().parent().find(".tdID").html("<input type='text' value='"+id+"' class='form-control txtID' />");
const name =$(this).parent().parent().find(".tdName").html();
$(this).parent().parent().find(".tdName").html("<input type='text' value='"+name+"' class='form-control txtName' />");
const address =$(this).parent().parent().find(".tdAddress").html();
$(this).parent().parent().find(".tdAddress").html("<input type='text' value='"+address+"' class='form-control txtAddress' />");
const age =$(this).parent().parent().find(".tdAge").html();
$(this).parent().parent().find(".tdAge").html("<input type='text' value='"+age+"' class='form-control txtAge' />");
$(this).parent().parent().find(".tdAction").html(rowUpdateButtons);
});
//filtering
$("#searchfield").on("keyup", function() {
var value = $(this).val().toLowerCase();
$("#searchable tr").filter(function() {
$(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
});
});
//sorting
$('th').each(function (col) {
$(this).data("type", $(this).attr("class"));
$(this).click(function () {
var type = $(this).data("type");
var records = $("table").find("tbody > tr");
records.sort(function(x,y){
var val1 = $(x).children("td").eq(col).text();
var val2 = $(y).children("td").eq(col).text();
if (type == "num")
{
val1 *= 1;
val2 *= 1;
}
return (val1 < val2) ? -1 : (val1 > val2 ? 1 :0)
});
$.each(records, function(index, row){
$("tbody").append(row);
});
});
});
});
</script>
</body>
</html>

If you need to serialize the whole table content updating the value you have previously saved, you had the option to keep track of changed rows and update only that part of the state. But it would require lots of efforts compared to saving the whole table from scratch and replacing its previously serialized state with the new one.
I focused here on that task only: how to serialize your table in a structured manner, dumping each row as an object having key-values pair as tdClassName-inputValue.
I extrapolated the table you have in your snippet and populated it with some sample data. Then I added a new button that triggers the logic to dump its content inside a variable that will be returned by the dumpTable() function.
When you click the button, it will print on console the object containing the table saved state. You can copy that logic in your code so that you'll call that function one moment before replacing the json data in the storage.
function dumpClickHandler() {
const dataTable = dumpTable();
console.log(dataTable);
}
function dumpTable() {
const dataTable = [];
//fetch table rows and iterate through all of them
const rows = document.querySelectorAll('#tblData tbody tr');
for (const row of rows) {
const dataRow = {};
//fetch row fields
const fields = row.querySelectorAll('td');
//for each field in row
for (const field of fields) {
//skip the action field containing buttons
if (field.classList.contains('tdAction'))
continue;
//fetch field name using the class assigned to the table cell
const fieldName =
Object.values(field.classList).filter(className => className.startsWith("td"))?.[0];
//fetch field value
dataRow[fieldName] = field.querySelector('input')?.value;
}
//push the fetched row in the array
dataTable.push(dataRow);
}
return dataTable;
}
h1{
margin: 2rem 0 2rem 0 !important;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<div class="container col-10">
<h1>Table filled with sample data:</h1>
<table id="tblData" class="table table-bordered table-hover table-striped">
<thead>
<tr id="headings">
<th class="num">ID</th>
<th class="text">Name</th>
<th class="text">Address</th>
<th class="num">Age</th>
<th class="tdaction">Action</th>
</tr>
</thead>
<tbody id="searchable">
<tr class="trNewRow">
<td class="tdID">
<input type="text" class="form-control txtID" value="1">
</td>
<td class="tdName">
<input type="text" class="form-control txtName" value="Name1">
</td>
<td class="tdAddress">
<input type="text" class="form-control txtAddress" value="Address1">
</td>
<td class="tdAge">
<input type="text" class="form-control txtAge" value="10">
</td>
<td class="tdAction">
<button class="btn btn-sm btn-success btn-save">Save</button>
<button class="btn btn-sm btn-success btn-cancel">Cancel</button>
</td>
</tr>
<tr class="trNewRow">
<td class="tdID">
<input type="text" class="form-control txtID" value="2">
</td>
<td class="tdName">
<input type="text" class="form-control txtName" value="Name2">
</td>
<td class="tdAddress">
<input type="text" class="form-control txtAddress" value="Address2">
</td>
<td class="tdAge">
<input type="text" class="form-control txtAge" value="20">
</td>
<td class="tdAction">
<button class="btn btn-sm btn-success btn-save"> Save</button>
<button class="btn btn-sm btn-success btn-cancel"> Cancel</button>
</td>
</tr>
</tbody>
</table>
<button type="button" class="btn btn-primary" onclick="dumpClickHandler();">DUMP TABLE CONTENT</button>
</div>

Related

Create a show all button to show hidden row

My code is like this: when you check the 'hide' column, the whole row is hidden. I also have a 'Show All' checkbox. When I check it, all rows (including the hidden ones) are shown. But when I uncheck it, all rows disappear. How can I make it so only the hidden rows disappear when I uncheck 'Show All', and the other rows still remain visible?
<body ng-app="myApp" ng-controller="myController">
<h1>Fruits in Vietnam</h1>
<hr>
<form name="myForm">
<div class="input-group input-group-lg">
<input type="text" class="form-control" name="mdfruitname" id="txtfruitname" ng-model="mdfruitname"
required>
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" ng-click="addnew()"
ng-disabled="myForm.mdfruitname.$pristine || myForm.mdfruitname.$invalid">Add
</button>
</div>
</div>
</form>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>Description</th>
<th>Hide</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="fruit in fruitnameDetail" ng-show="showall" ng-hide="hidef">
<td>{{fruit.fruitname}}</td>
<td><input type="checkbox" ng-model="hidef"></td>
<td>
<a class="btn btn-xs delete-record" ng-click="removefruitname($index)">
<i class="glyphicon glyphicon-trash"></i>
</a>
</td>
</tr>
</tbody>
</table>
<div><input type="checkbox" ng-model="showall"><span>SHOW ALL</span></div>
</body>
<script>
var app = angular.module("myApp", []);
app.controller('myController', function ($scope, $window) {
$scope.fruitnameDetail = [
{
"fruitname": "Banana"
},
{
"fruitname": "Mango"
},
{
"fruitname": "Orange"
}
];
$scope.addnew = function () {
var fruitnameDetail = {
fruitname: $scope.mdfruitname
};
$scope.fruitnameDetail.push(fruitnameDetail);
$scope.mdfruitname = '';
};
$scope.removefruitname = function (index) {
var name = $scope.fruitnameDetail[index].fruitname;
if ($window.confirm("Do you want to delete " + name + " ?")) {
$scope.fruitnameDetail.splice(index, 1);
}
};
});
</script>
Do not use ng-show and ng-hide at the same time. Just change a little bit your condition for ng-repeat. Here the example that should work:
<body ng-app="myApp" ng-controller="myController">
<h1>Fruits in Vietnam</h1>
<hr>
<form name="myForm">
<div class="input-group input-group-lg">
<input type="text" class="form-control" name="mdfruitname" id="txtfruitname" ng-model="mdfruitname"
required>
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" ng-click="addnew()"
ng-disabled="myForm.mdfruitname.$pristine || myForm.mdfruitname.$invalid">Add
</button>
</div>
</div>
</form>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>Description</th>
<th>Hide</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="fruit in fruitnameDetail" ng-show="showall || !hidef">
<td>{{fruit.fruitname}}</td>
<td><input type="checkbox" ng-model="hidef"></td>
<td>
<a class="btn btn-xs delete-record" ng-click="removefruitname($index)">
<i class="glyphicon glyphicon-trash"></i>
</a>
</td>
</tr>
</tbody>
</table>
<div><input type="checkbox" ng-model="showall"><span>SHOW ALL</span></div>
</body>

Create a table in jQuery with crud operations and data save in local storage

I tried to create a table in jQuery with Create, Update, Edit and Delate row functions. I need to save the data in local storage. The table must have a column wise filter and sorting.
When I run the application the table header and first row should be displayed. The first row includes some sample data. What are the changes I need to make?
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<div class="container">
<div class="form-row">
<div class="col-md-3">
<label>Name <span class="errdiv"> *</span></label>
<input id="txtName" type="text" class="form-control txt txtName" placeholder="Enter Name" required>
<input id="txtId" type="hidden" class="form-control txt">
</div>
<div class="col-md-3 col-sm-6 col-6">
<label>Contact No <span class="errdiv"> *</span></label>
<input id="txtContact" data-type="number" type="text" minlength="10" maxlength="10" class="form-control txt txtContact" placeholder="Contact No" required>
</div>
<div class="col-md-3 col-sm-6 col-6">
<label>Alt Contact No</label>
<input id="txtAltNo" type="text" class="form-control txt txtAltNo" minlength="10" maxlength="10" placeholder="Alt Contact No">
</div>
</div>
<div class="form-row">
<div class="col-md-6 col-sm-7">
<label>Address <span class="errdiv"> *</span></label>
<textarea id="txtAddress" class="form-control txt txtAddress" rows="3" minlength="15" placeholder="Enter Address" required></textarea>
</div>
</div>
<div class="row pt-2">
<div class="col-md-2">
<button type="button" class="btn btn-success" id="btnSave">Save</button>
</div>
<div class="col-md-2">
<button type="button" class="btn btn-secondary" id="btnClear">Clear</button>
</div>
</div>
<div class="row pt-3">
<div class="col-md-12 col-sm-12 col-12 p-2 ">
<table id="tblData" class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>ID</th>
<th> Name</th>
<th>Phone</th>
<th>Alt Phone</th>
<th>Address</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
var emptyRow = "<tr><td colspan='6' class='text-center'> No Records Available</td></tr>";
$(document).ready(function() {
loadDataFromLocal();
$('#tblData').on('click', '.btn-edit', function() {
debugger;
const name = $(this).parent().parent().find(".txtName").html();
const contact = $(this).parent().parent().find(".txtContact").html();
const altContact = $(this).parent().parent().find(".txtAltNo").html();
const address = $(this).parent().parent().find(".txtAddress").html();
const id = $(this).parent().parent().find(".txtName").attr("data-id");
$("#txtName").val(name);
$("#txtContact").val(contact);
$("#txtAltNo").val(altContact);
$("#txtAddress").val(address);
$("#txtId").val(id);
$("#btnSave").text("Update");
});
$('#tblData').on('click', '.btn-delete', function() {
debugger;
const id = $(this).parent().parent().find(".txtName").attr("data-id");
deleteDataFromLocal(id);
});
$("#btnSave").click(function() {
debugger;
if ($("#txtId").val() == '') {
addDataToLocal();
} else {
updateDataFromLocal();
}
});
$("#btnClear").click(function() {
debugger;
clearForm();
});
});
function clearForm() {
debugger;
$("#txtName").val("");
$("#txtContact").val("");
$("#txtAltNo").val("");
$("#txtAddress").val("");
$("#btnSave").text("Add");
}
function addEmptyRow() {
debugger;
if ($("#tblData tbody").children().children().length == 0) {
$("#tblData tbody").append(emptyRow);
}
}
function loadDataFromLocal() {
debugger;
let localData = localStorage.getItem('localData');
if (localData) {
$("#tblData tbody").html("");
let localArray = JSON.parse(localData);
let index = 1;
localArray.forEach(element => {
let dynamicTR = "<tr>";
dynamicTR = dynamicTR + "<td> " + index + "</td>";
dynamicTR = dynamicTR + "<td class='txtName' data-id=" + element.id + ">" + element.name + "</td>";
dynamicTR = dynamicTR + "<td class='txtContact'>" + element.contact + "</td>";
dynamicTR = dynamicTR + "<td class='txtAltNo'>" + element.altContact + "</td>";
dynamicTR = dynamicTR + "<td class='txtAddress'>" + element.address + "</td>";
dynamicTR = dynamicTR + " <td class='tdAction text-center'>";
dynamicTR = dynamicTR + " <button class='btn btn-sm btn-success btn-edit'> Edit</button>";
dynamicTR = dynamicTR + " <button class='btn btn-sm btn-danger btn-delete'> Delete</button>";
dynamicTR = dynamicTR + " </td>";
dynamicTR = dynamicTR + " </tr>";
$("#tblData tbody").append(dynamicTR);
index++;
});
}
addEmptyRow();
}
function addDataToLocal() {
debugger;
let localData = localStorage.getItem('localData');
if (localData) {
let localArray = JSON.parse(localData);
const obj = {
id: localArray.length + 1,
name: $("#txtName").val(),
contact: $("#txtContact").val(),
altContact: $("#txtAltNo").val(),
address: $("#txtAddress").val()
};
localArray.push(obj);
localStorage.setItem('localData', JSON.stringify(localArray));
loadDataFromLocal();
} else {
const arryObj = [];
const obj = {
id: 1,
name: $("#txtName").val(),
contact: $("#txtContact").val(),
altContact: $("#txtAltNo").val(),
address: $("#txtAddress").val()
};
arryObj.push(obj);
localStorage.setItem('localData', JSON.stringify(arryObj));
loadDataFromLocal();
}
clearForm();
}
function updateDataFromLocal() {
debugger;
let localData = localStorage.getItem('localData');
let localArray = JSON.parse(localData);
const oldRecord = localArray.find(m => m.id == $("#txtId").val());
oldRecord.name = $("#txtName").val();
oldRecord.contact = $("#txtContact").val();
oldRecord.altContact = $("#txtAltNo").val();
oldRecord.address = $("#txtAddress").val();
localStorage.setItem('localData', JSON.stringify(localArray));
loadDataFromLocal();
clearForm();
}
function deleteDataFromLocal(id) {
debugger;
let localData = localStorage.getItem('localData');
let localArray = JSON.parse(localData);
let i = 0;
while (i < localArray.length) {
if (localArray[i].id === Number(id)) {
localArray.splice(i, 1);
} else {
++i;
}
}
localStorage.setItem('localData', JSON.stringify(localArray));
loadDataFromLocal();
}

How do i add dynamic values and append it in one field?

form, which is fetching service name in <td>Name</td> and jQuery below is appending sub total as quantity increases with respect to price.
$(document).on('change', '.qty', function() {
var quantity = $(this).val();
var sub = $(this).parent().next().children();
var price = $(this).parent().prev().children().val();
var subTotal = parseInt(quantity) * parseInt(price);
sub.val(subTotal);
});
var i = 1;
$(function() {
$("#btnAdd").bind("click", function() {
var div = $("<tr />");
div.html(GetDynamicTextBox(""));
$("#TextBoxContainer").append(div);
});
$("body").on("click", ".remove", function() {
$(this).closest("tr").remove();
// GrandTotal();
});
});
function GetDynamicTextBox(value) {
i++;
return '<td><select class="form-control select2 select-service" style="width: 100%;" id="service-select' + i + '" required name="product[]"><option value="">--Select Product--</option> <?php foreach($dbf->fetchOrder("service") as $services){?><option value="<?= $services['
id '];?>"><?= $services['
service '];?></option><?php }?></select></td>' +
'<td><input class="form-control rate" name = "rate[]" type="text" placeholder="Rate" id="rate1' + i + '" required readonly/></td>' +
'<td><input class="form-control qty" placeholder="Quantity" name = "qty[]" type="number" id="qty1' + i + '" /></td>' +
'<td><input class="form-control sub-total" placeholder="Sub Total" name = "sub_total[]" type="text" id="sub_total1' + i + '" readonly required/></td>' +
'<td><button type="button" class="btn btn-danger remove"><i class="glyphicon glyphicon-remove-sign"></i></button></td>'
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class="table table-responsive table-striped table-bordered">
<thead>
<tr>
<td>Name</td>
<td>Price</td>
<td>Quantity</td>
<td>Sub Total</td>
<td><button id="btnAdd" type="button" class="btn btn-primary" data-toggle="tooltip" data-original-title="Add more controls"><i class="glyphicon glyphicon-plus-sign"></i> Add </button></td>
</tr>
</thead>
<tbody id="TextBoxContainer">
<tr>
<td>
<select class="form-control select-service" style="width: 100%;" id="service-select" required name="service[]">
<option value="">--Select Services--</option>
<?php foreach($dbf->fetchOrder("service") as $services){?>
<option value="<?= $services['id'];?>">
<?= $services['service'];?>
</option>
<?php }?>
</select>
</td>
<td><input type="text" class="form-control rate" placeholder="Rate" readonly name="rate[]" id="rate1" required></td>
<td><input type="number" min="1" class="form-control qty" placeholder="Quantity" name="qty[]" id="qty1"></td>
<td><input type="text" min="1" class="form-control sub_total" placeholder="Sub Total" name="sub_total[]" readonly id="sub_total1"></td>
<td><button type="button" class="btn btn-danger remove"><i class="glyphicon glyphicon-remove-sign"></i></button></td>
</tr>
</tbody>
<tfoot>
</tfoot>
</table>
this is the multiple dynamic input field of the same form above, which is adding Name,Price,Quantity,Sub Total dynamic field as add button is clicked.
My goal is to add all the sub-total from the multiple dynamic field and display it inside a different field.
You can use each loop to iterate through all inputs i.e : sub_total then get value of input and add it to some variable on each iteration and finally append it to some element.
Demo Code :
$(document).on('change', '.qty', function() {
var quantity = $(this).val();
var sub = $(this).parent().next().children();
var price = $(this).parent().prev().children().val()
var subTotal = parseInt(quantity) * parseInt(price);
sub.val(subTotal);
calculate_total(); //call to calculate
});
var i = 1;
$(function() {
$("#btnAdd").bind("click", function() {
var div = $("<tr />");
div.html(GetDynamicTextBox(""));
$("#TextBoxContainer").append(div);
});
$("body").on("click", ".remove", function() {
$(this).closest("tr").remove();
calculate_total(); //call to calculate
});
});
function GetDynamicTextBox(value) {
i++;
return '<td><select class="form-control select2 select-service" style="width: 100%;" id="service-select' + i + '" required name="product[]"><option value="">--Select Product--</option></select></td>' + '<td><input class="form-control rate" value="10" name = "rate[]" type="text" placeholder="Rate" id="rate1' + i + '" required readonly/></td>' + '<td><input class="form-control qty" placeholder="Quantity" name = "qty[]" type="number" id="qty1' + i + '" /></td>' + '<td><input class="form-control sub_total" placeholder="Sub Total" name = "sub_total[]" type="text" id="sub_total1' + i + '" readonly required/></td>' + '<td><button type="button" class="btn btn-danger remove"><i class="glyphicon glyphicon-remove-sign">x</i></button></td>'
}
function calculate_total() {
var total = 0;
//loop through all inputs(sub-total)
$("input.sub_total").each(function() {
//check if value is not null
total += ($(this).val() != "") ? parseInt($(this).val()) : 0;
})
//add total
$("#total").text(total)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class="table table-responsive table-striped table-bordered">
<thead>
<tr>
<td>Name</td>
<td>Price</td>
<td>Quantity</td>
<td>Sub Total</td>
<td><button id="btnAdd" type="button" class="btn btn-primary" data-toggle="tooltip" data-original-title="Add more controls"><i class="glyphicon glyphicon-plus-sign"></i> Add </button></td>
</tr>
</thead>
<tbody id="TextBoxContainer">
<tr>
<td>
<select class="form-control select-service" style="width: 100%;" id="service-select" required name="service[]">
<option value="">--Select Services--</option>
<option value="1">1 </option>
<option value="2">2 </option>
<option value="3">3 </option>
</select>
</td>
<td><input type="text" class="form-control rate" placeholder="Rate" readonly name="rate[]" value="10" id="rate1" required></td>
<td><input type="number" min="1" class="form-control qty" placeholder="Quantity" name="qty[]" id="qty1"></td>
<td><input type="text" min="1" class="form-control sub_total" placeholder="Sub Total" name="sub_total[]" readonly id="sub_total1"></td>
<td><button type="button" class="btn btn-danger remove"><i class="glyphicon glyphicon-remove-sign">x</i></button></td>
</tr>
</tbody>
<tfoot>
<tr>
<th colspan="3">Total : </th>
<th id="total">0</th>
</tr>
</tfoot>
</table>

Uncaught ReferenceError: myFunction is not defined at HTMLButtonElement.onclick in JQuery and HTML

I am applying JQuery:
HTML
<div id="myNotice" class="container-fluid">
<div style="margin-bottom: 10px;" class="row">
<div class="col-lg-12" >
<button type="button" onclick="myFunction()" class="btn btn-block btn-info">
<i class="fas fa-arrow-right"></i>
<span >Proceed</span>
</button>
</div>
</div>
</div>
<div id="myMSF" class="container-fluid" style="display:none">
<table id="msfTable" class=" table table-bordered table-striped table-hover datatable">
<thead>
<tr>
<th scope="col" width="4%">ID</th>
<th scope="col" width="25%">Core Value<span style="color:red;">*</span></th>
<th scope="col" width="25%">Rating<span style="color:red;">*</span></th>
<th scope="col" width="46%">Comment</th>
</tr>
</thead>
<tbody class="resultbody">
</tbody>
</table>
</div>
JQuery
<script type="text/javascript">
$(document).ready(function() {
//Try to get tbody first with jquery children. works faster!
var tbody = $('#msfTable').children('tbody');
//Then if no tbody just select your table
var table = tbody.length ? tbody : $('#msfTable');
$('[name=count_skills_no]').text();
function myFunction() {
var x = document.getElementById("myMSF");
var y = document.getElementById("myNotice");
if (x.style.display === "none") {
x.style.display = "block";
y.style.display = "none";
} else {
x.style.display = "none";
y.style.display = "block";
}
var nx = ($('.resultbody tr').length - 0) + 1;
var num_rows = $("#msfTable tbody tr").length + 1;
var rows = $('[name=count_skills_no]').val() - 1;
var sn;
for (var i = 0; i < rows; i++) {
sn = num_rows + i;
//Add row
table.append('<tr>\n' +
'<td class="no">' + sn + '</td>\n' +
' <td><select class="form-control select2bs4" data-placeholder="Choose Employee Type" tabindex="1" name="appraisal_skill_id[]" required>\n' +
' <option value="0" selected="true" disabled="true">Select Core Value</option>\n' +
' #if($skills->count() > 0 )\n' +
' #foreach($skills as $skill)\n' +
' <option value="{{$skill->id}}">{{$skill->skill_name}}</option>\n' +
' #endforeach\n' +
' #endif\n' +
' </select></td>\n' +
' <td><select class="form-control select2bs4" data-placeholder="Choose Rating" tabindex="1" name="appraisal_respondent_rating_id[]" required>\n' +
' <option value="0" selected="true" disabled="true">Select Rating</option>\n' +
' #if($ratings->count() > 0 )\n' +
' #foreach($ratings as $rating)\n' +
' <option value="{{$rating->id}}">{{$rating->rating_value}} - {{$rating->rating_name}}</option>\n' +
' #endforeach\n' +
' #endif\n' +
' </select></td>\n' +
' <td><input type="text" name="comment[]" placeholder="Enter comment here" class="form-control comment" required></td>\n' +
'</tr>');
}
}
});
</script>
By default
<div id="myMSF" class="container-fluid" style="display:none">
is hidden while
<div id="myNotice" class="container-fluid">
is visible.
<button type="button" onclick="myFunction()" class="btn btn-block btn-info">
<i class="fas fa-arrow-right"></i>
<span >Proceed</span>
</button>
is to display myMSF and hide. Also it creates tbody of the table
When the button proceed is onclick, I got this error:
Uncaught ReferenceError: myFunction is not defined at HTMLButtonElement.onclick
and this code is highlighted
<button type="button" onclick="myFunction()" class="btn btn-block btn-info">
How do I resolve it?
Thanks
You defined function myFunction() { inside the context of $(document).ready(function() {. Hence, it is not visible outside. Move such a function outside the dom ready.
Moreover, avoid inline event handler, use instead .on():
$('#myNotice button.btn-info').on('click', function(e) {
.....
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/popper.js#1.16.0/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
<div id="myNotice" class="container-fluid">
<div style="margin-bottom: 10px;" class="row">
<div class="col-lg-12" >
<button type="button" onclick="myFunction()" class="btn btn-block btn-info">
<i class="fas fa-arrow-right"></i>
<span >Proceed</span>
</button>
</div>
</div>
</div>
<div id="myMSF" class="container-fluid" style="display:none">
<table id="msfTable" class=" table table-bordered table-striped table-hover datatable">
<thead>
<tr>
<th scope="col" width="4%">ID</th>
<th scope="col" width="25%">Core Value<span style="color:red;">*</span></th>
<th scope="col" width="25%">Rating<span style="color:red;">*</span></th>
<th scope="col" width="46%">Comment</th>
</tr>
</thead>
<tbody class="resultbody">
</tbody>
</table>
</div>
<script>
function myFunction() {
var x = document.getElementById("myMSF");
var y = document.getElementById("myNotice");
if (x.style.display === "none") {
x.style.display = "block";
y.style.display = "none";
} else {
x.style.display = "none";
y.style.display = "block";
}
var nx = ($('.resultbody tr').length - 0) + 1;
var num_rows = $("#msfTable tbody tr").length + 1;
var rows = $('[name=count_skills_no]').val() - 1;
var sn;
for (var i = 0; i < rows; i++) {
sn = num_rows + i;
//Add row
table.append('<tr>\n' +
'<td class="no">' + sn + '</td>\n' +
' <td><select class="form-control select2bs4" data-placeholder="Choose Employee Type" tabindex="1" name="appraisal_skill_id[]" required>\n' +
' <option value="0" selected="true" disabled="true">Select Core Value</option>\n' +
' #if($skills->count() > 0 )\n' +
' #foreach($skills as $skill)\n' +
' <option value="{{$skill->id}}">{{$skill->skill_name}}</option>\n' +
' #endforeach\n' +
' #endif\n' +
' </select></td>\n' +
' <td><select class="form-control select2bs4" data-placeholder="Choose Rating" tabindex="1" name="appraisal_respondent_rating_id[]" required>\n' +
' <option value="0" selected="true" disabled="true">Select Rating</option>\n' +
' #if($ratings->count() > 0 )\n' +
' #foreach($ratings as $rating)\n' +
' <option value="{{$rating->id}}">{{$rating->rating_value}} - {{$rating->rating_name}}</option>\n' +
' #endforeach\n' +
' #endif\n' +
' </select></td>\n' +
' <td><input type="text" name="comment[]" placeholder="Enter comment here" class="form-control comment" required></td>\n' +
'</tr>');
}
}
$(document).ready(function() {
//Try to get tbody first with jquery children. works faster!
var tbody = $('#msfTable').children('tbody');
//Then if no tbody just select your table
var table = tbody.length ? tbody : $('#msfTable');
$('[name=count_skills_no]').text();
});
</script>

second page is not updating while using tr dir-paginate

I am using onaftersave() function to save the table data. In dir-paginate i am using ng-repeat to read the table row values. I kept itemsperpage is 5. obviously 6th row will be display in second Page. When I try to edit the second page row values it is not updating the second page rows. How to resolve it?
<form editable-form name="tableform1" oncancel="cancel()" onaftersave="saveTable(Manage_Role.Description)">
<div class="row">
<div class="col-xs-12">
<div class="panel panel-default panel-table">
<div class="panel-heading">
<div class="row">
<div class="col col-xs-6">
</div>
<div class="col col-xs-6 text-right">
<button type="button" data-toggle="modal" data-target="#myModal" class="btn btn-sm btn-primary btn-create" title="Add New Role">Add New Role</button>
</div>
</div>
</div>
<div class="panel-body scrollbar">
<table class="table table-striped table-bordered table-list">
<thead>
<tr>
<th style="width: 25%" align="center">
Name
</th>
<th style="width: 35%">
Description
</th>
<th style="width: 30% " align="center">
IsActive?
</th>
<th style="width: 10% " align="center"><a href="" ng-click="orderByField='IsActive';
reverseSort = !reverseSort" title="Assign">Assign</a></th>
<th style="width: 1%" align="center" ng-show="false">DirtyFlag</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="text" id="RoleNameID" ng-model="Manage_Role1.RoleName" placeholder="search" class="searchbox" onkeydown="return (event.keyCode!=13);"
/>
</td>
<td>
<input type="text" id="DescriptionID" ng-model="Manage_Role1.Description" placeholder="search" class="searchbox" onkeydown="return (event.keyCode!=13);"
/>
</td>
<td>
<select name="DropFilter" ng-model="Manage_Role1.IsActive" class="searchbox">
<option value="">All</option>
<option value=false>False</option>
<option value=true>True</option>
</select>
</td>
<!--<td></td>-->
</tr>
<tr dir-paginate="Manage_Role in Manage_Roleser | itemsPerPage:5 | filter:DropFilter | filter:Manage_Role1 | orderBy:orderByField:reverseSort "
ng-class-odd="'odd'">
<td ng-click="tableform1.$show()">
<span e-ng-change="applyHighlight($data,$index)" editable-text="Manage_Role.RoleName" ng-attr-title="{{Manage_Role.RoleName}}"
e-form="rowform" ng-click="rowform.$show()">
{{ Manage_Role.RoleName | uppercase }}
</span>
</td>
<td ng-click="tableform1.$show()">
<div>
<span e-ng-change="applyHighlight($data,$index)" editable-text="Manage_Role.Description" e-form="rowform" ng-attr-title="{{Manage_Role.Description}}"
ng-click="rowform.$show()">
{{ Manage_Role.Description || 'empty' }}
</span>
</div>
</td>
<td ng-click="tableform1.$show()" align="center">
<span e-ng-change="applyHighlight($data,$index)" ng-checked="Manage_Role.IsActive" editable-checkbox="Manage_Role.IsActive"
e-form="rowform" ng-click="applyHighlight($data,$index);tableform.$show()">
<input type="checkbox" ng-model="Manage_Role.IsActive" width="20" ng-attr-title="{{Manage_Role.IsActive}}" />
</span>
</td>
<td align="center">
<input type="image" data-toggle="modal" ng-click="Rolepopupclick(Manage_Role._id,Manage_Role.RoleName)" title="Assign UI"
data-target="#Div1" src="App_Themes/Images/AssignUser.png"
alt="Submit" width="28" height="28" />
</td>
<td ng-show="false">
<input type="text" ng-model="Manage_Role.DirtyFlag" ng-show="false" />
</td>
</tr>
</tbody>
</div>
</table>
</div>
<div class="panel-footer">
<div class="row">
<div class="col col-xs-8">
<dir-pagination-controls max-size="5" direction-links="true" boundary-links="true">
</dir-pagination-controls>
</div>
<div class="col col-xs-4">
<div class="btn-form" ng-show="tableform1.$visible" style="float:right">
<button type="submit" ng-disabled="tableform1.$waiting" title=" Save Changes" class="btn btn-primary fa fa-save" ng-click="NotifyClick()"> Save</button>
<input type="button" ng-disabled="tableform1.$waiting" title=" Close Edit Form Without Saving Changes" ng-click="refreshUI();tableform1.$cancel()"
class="btn btn-danger fa fa-close" value="X Cancel" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
controller code:
$scope.saveTable = function ()
{
debugger;
if(NotifyCount!=1){
var RoleNamechk = '';
for (var i = 0; i < $scope.Manage_Roleser.length; i++) {
RoleNamechk = $scope.Manage_Roleser[i].RoleName;
for (var j = 0; j < $scope.Manage_Roleser.length; j++) {
var RoleName = $scope.Manage_Roleser[j].RoleName;
if (RoleNamechk == RoleName) {
count++;
}
}
}
if (count == $scope.Manage_Roleser.length)
{
for (var i = 0; i < $scope.Manage_Roleser.length; i++) {
var id = $scope.Manage_Roleser[i]._id;
var rolename = $scope.Manage_Roleser[i].RoleName;
var isactive = $scope.Manage_Roleser[i].IsActive;
var description = $scope.Manage_Roleser[i].Description;
if (description == "")
{
description = 'empty';
}
if ($scope.Manage_Roleser[i].DirtyFlag == "True")
{
$scope.LoggedinUsers = $cookieStore.get('LoggedinUser');
var updatedBy= $scope.LoggedinUsers;
$http.put('/Roleupdate/' + id + '/' + rolename + '/' + isactive + '/' + description + '/' + updatedBy).then(function (response) {
refresh();
});
}
count=0;
}
$notify.success('Success', 'Role is Updated Successfully');
}
else {
$notify.warning('Warning', 'Role Name is Already Exists');
count = 0;
return '';
}
}
};
in ng-change I am checking dirty flag of selected index because it is updating all the row values. for that I am updating only selected($dirty) row alone. In second page it is not taking table index correctly. If I select 2nd page 3 row it will assume or taking index of the particular page. It should take index of selected row from the over all table row
$scope.applyHighlight = function ($data,index) {
$scope.Manage_Roleser[index].DirtyFlag = "True";
var dataSpan = $(event.currentTarget).closest('td');
if (!dataSpan.hasClass("highlighted")) {
$(dataSpan).addClass("highlighted");
}
}