I want to validate the user input before adding the object to an observable. For example if I have two fields, say Quantity and Price, before adding the object to the observable I want to validate the user input.
How can I achieve this behavioral?
The code that I have so far:
self.productPriceAdd = function () {
var newPrice = {
Quantity: self.newProductPriceEntry.Quantity(),
Price: self.newProductPriceEntry.Price(),
ProductBarcode: self.productPrices().Barcode
}
self.productPrices().ProductSalePrices().push(newPrice);
self.productPrices().ProductSalePrices(self.productPrices().ProductSalePrices());
self.newProductPriceEntry.Quantity(null);
self.newProductPriceEntry.Price(null);
}
The user interface looks somethig like this:
So after the user clicks the Add button, two error messages should be displayed, one for each empty field.
My HTML code:
<!-- ko if: productPrices() -->
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title"><b data-bind="text: productPrices().Name"></b></h2>
</div>
<table class="table">
<tr>
<th>
#Html.DisplayName("Quantity")
</th>
<th>
#Html.DisplayName("Price")
</th>
<th></th>
</tr>
<tbody data-bind="foreach: productPrices().ProductSalePrices()">
<tr>
<td>
<b data-bind="text: Quantity"></b>
</td>
<td>
<b data-bind="text: Price"></b>
</td>
<td>
Remove
</td>
</tr>
</tbody>
<tbody data-bind="with: newProductPriceEntry">
<tr>
<td>
<input type="number" class="form-control" data-bind="value: Quantity " placeholder="Quantity">
</td>
<td>
<input type="number" step="0.01" class="form-control" data-bind="value: Price " placeholder="Price">
</td>
<td>
Add
</td>
</tr>
</tbody>
</table>
</div>
Save
Have a look at Knockout-Validation. It is a Knockout plugin that simplifies the validation process.
https://github.com/Knockout-Contrib/Knockout-Validation
So, the solution I found is to check for false values for Quantity and Price field. Are evaluated to false the following types: false, "", undefined and null.
So, the full working code:
self.productPrices = ko.observable();
self.newProductPriceEntry = {
Quantity: ko.observable("").extend({ required: true }),
Price: ko.observable("").extend({ required: true })
}
self.productPriceAdd = function () {
var newPrice = {
Quantity: self.newProductPriceEntry.Quantity(),
Price: self.newProductPriceEntry.Price(),
ProductBarcode: self.productPrices().Barcode
}
if (newPrice.Price && newPrice.Quantity) {
self.productPrices().ProductSalePrices().push(newPrice);
self.productPrices().ProductSalePrices(self.productPrices().ProductSalePrices());
}
self.newProductPriceEntry.Quantity(null);
self.newProductPriceEntry.Price(null);
}
So what I am doing is to push the object into the observable only if the condition is true. Then set the observables to null to clear the fields.
For the number part validation I used the number HTML5 attribute.
Related
I have two number that calculates the amount and vat amount using JavaScript. Now how will I be able to do this?
<tr>
<th>Amount: </th>
<td><input id="amount" type="text" name="amount" class="form-control" required />
</td>
</tr>
<tr>
<th>VAT 20% Amt: </th>
<td>
<input id="vatAmt" type="text" name="vatAmount" class="form-control" />
</td>
</tr>
<tr>
<th>Total Amount: </th>
<td>
<span id="totalAmount"></span>
</td>
</tr>
then the amount due
<div class="col-md-6" >
<h3>Amount Due: £ <span id="amountValue"></span></h3>
</div>
My JavaScript code is below:
$("#amount").keyup(function () {
var value = $(this).val();
$("#amountValue").text(value);
});
$("#vatAmt").keyup(function () {
var valueVat = $(this).val();
var sum = value + valueVat;
$("#totalAmount").text(sum);
}).keyup();
Can someone help me figured this thing out? Any help is muchly appreciated. TIA
value isn't defined in your second keyup handler. You need to define it:
$("#vatAmt").keyup(function () {
var value = $("#amount").val(); // <--- here
var valueVat = $(this).val();
var sum = value + valueVat;
$("#totalAmount").text(sum);
}).keyup();
When you define a variable inside a function, it's only available within that function. Another option could be to define it globally, but I generally prefer keeping scope limited to only where you need it. Within your keyup handler for #varAmt you need two values, so you would have two calls to .val() to get two values.
Note: It's possible you may also want to use parseInt or something similar to perform your addition:
$("#vatAmt").keyup(function () {
var value = parseInt($("#amount").val()); // <--- here
var valueVat = parseInt($(this).val()); // <--- and here
var sum = value + valueVat;
$("#totalAmount").text(sum);
}).keyup();
JavaScript is pretty forgiving about types, but sometimes bugs can creep in if you rely on that. And .val() returns a string because the input is text, even if that text happens to be numeric characters.
This will do
<tr>
<th>Amount: </th>
<td><input id="amount" type="number" name="amount" class="form-control" required />
</td>
</tr>
<tr>
<th>VAT 20% Amt: </th>
<td>
<input id="vatAmt" type="number" name="vatAmount" class="form-control" />
</td>
</tr>
<tr>
<th>Total Amount: </th>
<td>
<span id="totalAmount"></span>
</td>
</tr>
<div class="col-md-6">
<h3>Amount Due: £ <span id="amountValue"></span></h3>
</div>
var value;
$("#amount").keyup(function() {
value = $(this).val();
$("#amountValue").text(value);
})
$("#vatAmt").keyup(function() {
var valueVat = $(this).val();
var sum = Number(value) + Number(valueVat);
$("#totalAmount").text(sum);
}).keyup();
In JavaScript hidden field value is stored.
But when clicked on next page hidden field is null it doesn't have values from previous page.
Why is it not loading previous values in next page?
How to load previous value in hidden field??
function AddRemoveCustomer(id) {
//$(".checkBoxClass").click(function (e) {
alert('in main function');
alert(id);
var CustomerIDArray = [];
var hidCID = document.getElementById("hfCustomerID");
if (hidCID != null && hidCID != 'undefined') {
var CustID = hidCID.value;
CustomerIDArray = CustID.split("|");
var currentCheckboxValue = id;
var index = CustomerIDArray.indexOf(currentCheckboxValue);
alert('index value:' + index);
debugger;
if (index == 0) {
alert('if');
CustomerIDArray.push(currentCheckboxValue);
alert('pushed value:' + CustomerIDArray);
} else {
alert('else');
var a = CustomerIDArray.splice(index, 1);
alert("a" + a);
}
hidCID.value = CustomerIDArray.join("|");
alert('Final' + hidCID.value);
} else {
alert('undefined');
}
//});
}
<table id="tblEmailScheduler" class="table-bordered col-offset-12">
<thead>
<tr class="label-primary">
<th style="padding:5px 15px;">
First Name
</th>
<th style="padding:5px 15px;">
Last Name
</th>
<th style="padding:5px 15px;">
Email ID
</th>
<th style="padding:5px 15px;">
Customer Type
</th>
<th style="padding:5px 15px;">
Customer Designation #Html.DropDownList("CustomerDesignation", new SelectList(ViewBag.SelectAllCustomerDesignationDDL, "Value", "Text"), new { id = "CustomerDesignationDDL" , name = "CustomerDesignationDDL" })
</th>
<th style="padding:5px 15px;">
Select All
<div class="checkbox control-group">
<label><input type="checkbox" id="cbSelectAll" /></label>
</div>
</th>
</tr>
</thead>
<tfoot>
<tr>
<th colspan="2">
EmailTemplate : #Html.DropDownList("EmailSubject", new SelectList(ViewBag.SelectAllEmailTemplateDDL, "Value", "Text"), new { id = "SelectAllEmailTemplateDDL" })
</th>
<th colspan="2">
Set Date And Time:
<input type="text" class="from-date-picker" readonly="readonly" />
</th>
<th colspan="2">
<input type="submit" value="Schedule" id="btnSubmit" class="btn btn-default" />
</th>
<td>
</td>
</tr>
</tfoot>
#foreach (var item in Model) {
<tr style="text-align:center">
<td id="tblFirstName">
#item.FirstName
</td>
<td id="tblLastName">
#item.LastName
</td>
<td id="tblEmailID">
#item.EmailID
</td>
<td id="tblCustomerType">
#item.CustomerType
</td>
<td id="tblCustomerDesignation">
#item.CustomerDesignation
</td>
<td>
<div class="checkbox control-group">
<label><input type="checkbox" id="#item.CustomerID" value="#item.CustomerID"
onclick="AddRemoveCustomer(#item.CustomerID)" class="checkBoxClass"/>
#*#Html.CheckBox("Select", new { id = "cbCustomer", item.CustomerID})*#</label>
</div>
</td>
</tr>
}
</table>
<input type="hidden" id="hfCustomerID" />
In looking at your code, I noticed an issue that may be causing you the problem you indicate:
var index = CustomerIDArray.indexOf(currentCheckboxValue);
you then use index to decide if the Id is in the array, by doing this:
if (index == 0) {
it should be
if (index == -1) {
as -1 indicated not found. With it as it is currently, it would hit this line:
var a = CustomerIDArray.splice(index, 1);
and try to splice a negative index, which goes backward from the end of the string, which would give unexpected results.
Your hidden hfCustomerID field is always rendered to the browser as an empty field, because you haven't bound it to anything in your Model.
I assume this table is inside a form which is being posted to your controller? If so, then you could add a new field to your Model, and then use #Html.HiddenFor to render a hidden input field which is bound to that item in your model.
Alternatively, it looks like this field is entirely calculated based on the ticked checkboxes? In which case, update your view to set the value of the hidden field (this will duplicate some logic from your JavaScript though).
Whenever I check a checkbox on a listing page, save it then go to page eg 2 (using pagination) and check something there and save it the checkbox on my first page is unchecked. I thought about using AJAX to save checked checkboxes to grails session but don't know how to do that - I'm beginner with JS and using views. Could someone help me out?
Here is the part with listing all companies and checkboxes in my gsp:
<g:form name="company-list-form" action="listCompany">
<div>
<g:textField id="search-field" name="query" value="${params.query}"/>
<span>
<g:checkBox id="only-blockades-box" name="onlyBlockades" class="submit-on-change" value="${params.onlyBlockades}" title="Pokaż tylko blokady"/>
<label for="only-blockades-box">Tylko blokady</label>
</span>
<g:actionSubmit value="${message(code: 'default.buttons.search', default: 'Szukaj')}" action="listCompany" class="button_orange"/>
<g:link action="listCompany" class="button_gray"><g:message code="default.buttons.clean" default="Wyczyść"/></g:link>
</div>
<div class="padding-top">
<table class="table_td company-list-table">
<tbody>
<tr class="gray2">
<th class="first">Id</th>
<th style="max-width: 100px;">Nazwa</th>
<th>Id Kontrahenta</th>
<th title="Dostęp do TPO">TPO</th>
<th style="width: 20px;" title="Dostawa bezpośrednio do magazynu">Dostawa bezpośrednio</th>
<th style="width: 20px;" title="Możliwość potwierdzania zamówień">Potwierdzanie zamówień</th>
<th style="width: 20px;" title="Możliwość importowania awizacji z XLS">Import z Excel</th>
<th style="width: 20px;" title="Możliwość awizowania zamówionych indeksów">Awizacja zam. indeksów</th>
<th style="width: 20px;" title="Możliwość awizowania tygodniowego">Awizacja tyg.</th>
<th style="width: 20px;" title="Dostęp jedynie do awizowania tygodniowego">Tylko awizacja tyg.</th>
<th title="Limit AGD przypadający na każdą kratkę okna prywatnego">AGD</th>
<th title="Limit rowerów przypadający na każdą kratkę okna prywatnego">Rowery</th>
<th>Blokady</th>
<th class="is-blocked-th">Zablokowany?</th>
</tr>
<g:each in="${companyInstanceList}" var="company" status="i">
<tr class="${(i % 2) == 0 ? 'even' : 'odd'} table_td_gray2 ${i + 1 == companyInstanceList?.size() ? 'last' : ''}">
<td class="first" style="text-decoration: underline;">
<g:link action="editCompany" id="${company?.id}">${company?.id}</g:link>
</td>
<td>
${company?.name}
</td>
<td>
${company?.idKontrahenta}
</td>
<td>
<g:checkBox name="tpoAccess.${company?.id}" id="tpo-access-${company?.id}"
checked="${company?.tpoAccess}"/>
</td>
<td>
<g:checkBox name="directDeliveryAvailable.${company?.id}"
id="direct-delivery-available-${company?.id}"
checked="${company?.directDeliveryAvailable}"/>
</td>
<td>
<g:checkBox name="accessToOrderConfirmation.${company?.id}"
id="access-to-order-confirmation-${company?.id}"
checked="${company?.accessToOrderConfirmation}"/>
</td>
<td>
<g:checkBox name="accessToXlsImport.${company?.id}"
id="access-to-xls-import-${company?.id}"
checked="${company?.accessToXlsImport}"/>
</td>
<td>
<g:checkBox name="accessToOrderedProductsAvisation.${company?.id}"
id="access-to-ordered-products-confirmation-${company?.id}"
checked="${company?.accessToOrderedProductsAvisation}"/>
</td>
<td>
<g:checkBox name="accessToLimitedAvisation.${company?.id}"
id="access-to-limited-avisation-${company?.id}"
checked="${company?.accessToLimitedAvisation}"/>
</td>
<td>
<g:checkBox name="accessOnlyToLimitedAvisation.${company?.id}"
id="access-only-to-limited-avisation-${company?.id}"
checked="${company?.accessOnlyToLimitedAvisation}"/>
</td>
<td>
<input type="text" name="agdPrivateWindowLimit.${company?.id}"
value="${company?.agdPrivateWindowLimit}"
class="shortText" id="agd-private-window-limit-${company?.id}"
onchange="validateLimits('agdPrivateWindowLimit.${company?.id}')">
</td>
<td>
<input type="text" name="bicyclePrivateWindowLimit.${company?.id}"
value="${company?.bicyclePrivateWindowLimit}"
class="shortText" id="bicycle-private-window-limit-${company?.id}"
onchange="validateLimits('bicyclePrivateWindowLimit.${company.id}')">
</td>
<td>
<g:link class="button_gray" controller="productGroup" action="list" params="[companyId: company?.id, query: params.query ?: '']">
Blokady
</g:link>
</td>
<td>
<g:if test="${company?.findBlockades()}">
<span title="Dostawca ma aktywne blokady grup towarowych." class="bold large">
✓
</span>
</g:if>
</td>
</tr>
</g:each>
</tbody>
</table>
</div>
<div class="paginateButtons">
<g:paginate controller="company" action="listCompany" total="${companyInstanceTotal}"
params="[query: params.query ?: '']"/>
</div>
<div style="float:right;">
<g:link action="createCompany" class="button_orange">
<g:message code="default.button.create.label" default="Utwórz"/>
</g:link>
<g:actionSubmit action="updateCompanies" name="companyListSubmit" class="button_orange" value="Zapisz"/>
</div>
</g:form>
Here is my javascript file associated with that view:
function validateLimits(name) {
document.getElementsByName(name)[0].value = document.getElementsByName(name)[0].value.replace(/[A-Za-z!##$%^&*" "]/g, "");
var quantity = document.getElementsByName(name)[0].value;
var toBeAvised = 9999;
if (quantity.indexOf(',') > -1 || quantity.indexOf('.') > -1 || /*quantity == "" ||*/ isNaN(quantity)) {
alert("Limit musi być liczbą całkowitą");
document.getElementsByName(name)[0].value = '';
} else if (parseInt(quantity) > toBeAvised) {
alert("Podana liczba jest większa niż maksymalny limit równy " +toBeAvised + ".");
document.getElementsByName(name)[0].value = '';
} else if (parseInt(quantity) < 0) {
alert("Limit musi być liczbą dodatnią!");
document.getElementsByName(name)[0].value = '';
}
}
And here is controller method (listCompany):
def listCompany(Integer max) {
Person person = Person.read(springSecurityService.principal.id)
Company comp = person?.company
params.max = Math.min(max ?: 25, 100)
params.offset = params.offset ?: 0
params.readOnly = true
String q = (params.query as String)?.toLowerCase() ?: ""
def query = Company.where {
id != comp?.id
name =~ "%$q%" || idKontrahenta as String =~ "%$q%"
if (params.onlyBlockades == "on") {
id in ProductGroupBlockade.findAllByCompanyIsNotNullAndEnabled(true)*.companyId
}
}
List<Company> companyInstanceList = query.list([max: params.int("max"), offset: params.int("offset"), sort: "name"])
Integer count = query.count()
if (flash.message) {
params.errors = flash.message
}
[companyInstanceList: companyInstanceList, companyInstanceTotal: count, companySaved: params.companySaved, errors: params.errors]
}
How I could fix that so my checkboxes stay checked after saving? Right now they become unchecked whenever I go to next page and save some checkboxes there.
I tend to use DataTables for situations like this but it depends on the amount of data you're dealing with to how you go about it.
If you have a relatively small data set, say 1000 rows or fewer you can use a plain DataTable, if you have more than this then you may want to use a server side processing DataTable.
Using a DataTable you would do away with all the Grails pagination, give your table an ID and just create the table in javascript like:
<script type="text/javascript">
$(document).ready( function() {
$( '#companyListTable' ).DataTable();
} );
</script>
All the pagination is handled in javascript and check boxes are preserved when navigating through the table pagination.
The reason is that you need to build your own search parameters to be sent with pagination:
<g:paginate total="${instanceTotal}" params="${search}" />
a similar post can be found here with more details of how you build this search params included as links as a form of comment within it.
Edited to add if you wantd to actually ammend pagination yourself by checking additional stuff through jquery i.e.
var something = $('#somFIeld').val()
and adding something through javascript to current pagination instead then take a read of this answer
I am developing my first app using Vuejs + Laravel and I am facing a problem that I couldn't solve until now!
I have an array of object and I need to edit a single of then without delete and add a new one! I have made a JS Bin to show what I need!
JS Bin
When you click in EDIT and start to typing your new value the original value edits as well but I need to change the original value only after the user hit the save button!
Anybody can help me?
PS: I will update my database and then show the new value on the table!
Is there anyway to duplicate my record as I do on the edit function without sync then?
JS
new Vue({
el: 'body',
data: {
cache: {},
record: {},
list: [
{ name: 'Google', id: 1 },
{ name: 'Facebook', id: 2 },
],
},
methods: {
doEdit: function (record) {
this.cache = record;
},
edit: function (record) {
this.record = _.cloneDeep(record);
this.cache = record;
}
}
});
HTML
<div class="container">
<form class="form-horizontal" #submit.prevent="doEdit(record)">
<div class="row">
<div class="col-md-12">
<label>Name</label>
<input type="text" class="form-control" v-el:record-name v-model="record.name">
</div>
<div class="col-xs-12" style="margin-top:15px">
<button type="submit" class="col-xs-12 btn btn-success">Save</button>
</div>
</div>
</form>
<hr>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="r in list">
<td class="text-center" style="width:90px"> {{ r.id }} </td>
<td> {{ r.name }} </td>
<td class="text-center" style="width:90px">
<span class="btn btn-warning btn-xs" #click="edit(r)"><i class="fa-fw fa fa-pencil"></i></span>
</td>
</tr>
</tbody>
</table>
</div>
You can replace the old object with the cloned-updated one.
doEdit: function (record) {
var index = _.indexOf(this.list, this.cache);
this.list.splice(index, 1, record);
}
https://jsbin.com/ruroqu/3/edit?html,js,output
If you want to save the value only after user submitted, you should not bind the record directly such as v-model="record.name".
And we can use Vue.set to change attributes of the original record.
Let's try: JS Bin
There are two tables using the same source. These tables are using source binding of kendo templates. At present the source for both these tables are employees. Both these tables are displaying the same data.
Now, we need to modify it to show only check-box selected records in the result table. Also, when user clicks on the delete button on the result table, the check-box should be un-selected in the section table.
What modification do we need to do to make it work in MVVM?
Head
<head>
<title>MVVM Test</title>
<script type="text/javascript" src="lib/kendo/js/jquery.min.js"></script>
<script type="text/javascript" src="lib/kendo/js/kendo.web.min.js"></script>
<!----Kendo Templates-->
<script id="row-template" type="text/x-kendo-template">
<tr>
<td data-bind="text: name"></td>
<td data-bind="text: age"></td>
<td><button type="button" data-bind="click: deleteEmployee">Delete</button></td>
</tr>
</script>
<script id="selection-table-template" type="text/x-kendo-template">
<tr>
<td data-bind="text: name"></td>
<td data-bind="text: age"></td>
<td>
<input type="checkbox" name="selection" value="a">
</td>
</tr>
</script>
<!--MVVM Wiring using Kendo Binding-->
<script type="text/javascript">
$(document).ready(function () {
kendo.bind($("body"), viewModel);
});
</script>
</head>
MVVM
<script type="text/javascript">
var viewModel = kendo.observable({
// model definition
employees: [
{ name: "Lijo", age: "28" },
{ name: "Binu", age: "33" },
{ name: "Kiran", age: "29" }
],
personName: "",
personAge: "",
//Note: Business functions does not access any DOM elements using jquery.
//They are referring only the objects in the view model.
//business functions (uses "this" keyword - e.g. this.get("employees"))
addEmployee: function () {
this.get("employees").push({
name: this.get("personName"),
age: this.get("personAge")
});
this.set("personName", "");
this.set("personAge", "");
},
deleteEmployee: function (e) {
//person object is created using "e"
var person = e.data;
var employees = this.get("employees");
var index = employees.indexOf(person);
employees.splice(index, 1);
}
});
</script>
Body
<body>
<table id="selectionTable">
<thead>
<tr>
<th>
Name
</th>
<th>
Age
</th>
</tr>
</thead>
<tbody data-template="selection-table-template" data-bind="source: employees">
</tbody>
</table>
<br />
<hr />
<table id="resultTable">
<thead>
<tr>
<th>
Name
</th>
<th>
Age
</th>
</tr>
</thead>
<!--The data-template attribute tells Kendo UI that the employees objects should be formatted using a Kendo UI template. -->
<tbody data-template="row-template" data-bind="source: employees">
</tbody>
</table>
</body>
REFERENCES
set method - ObservableObject - Kedo API Reference
set method - kendo Model - Kedo API Reference
Filtering source in a Kendo Template
Kendo-UI grid Set Value in grid with Javascript
First things first.
If you remove the object from the viewModel when you delete it, it will be removed from your source table as well. You would need two arrays to handle this if you wanted it to be the way you describe. But based on the first part of your question, I thought I would post a solution.
HTML
<script id="row-template" type="text/x-kendo-template">
<tr data-bind="visible: isChecked">
<td data-bind="text: name"></td>
<td data-bind="text: age"></td>
<td>
<button type="button" data-bind="click: deleteEmployee">Delete</button>
</td>
</tr>
</script>
<script id="selection-table-template" type="text/x-kendo-template">
<tr>
<td data-bind="text: name"></td>
<td data-bind="text: age"></td>
<td>
<input type="checkbox" name="selection" data-bind="checked: isChecked"/>
</td>
</tr>
</script>
<table id="selectionTable">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody data-template="selection-table-template" data-bind="source: employees"/>
</table>
<br />
<hr />
<table id="resultTable">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody data-template="row-template" data-bind="source: employees"/>
</table>
JAVASCRIPT
var viewModel = kendo.observable({
employees: [
{ name: "Lijo", age: "28", isChecked: true },
{ name: "Binu", age: "33", isChecked: true },
{ name: "Kiran", age: "29", isChecked: true }
],
personName: "",
personAge: "",
addEmployee: function () {
this.get("employees").push({
name: this.get("personName"),
age: this.get("personAge")
});
this.set("personName", "");
this.set("personAge", "");
},
deleteEmployee: function (e) {
var person = e.data;
var employees = this.get("employees");
var index = employees.indexOf(person);
var employee = employees[index];
//set
employee.set('isChecked', false);
}
});
$(document).ready(function () {
kendo.bind($("body"), viewModel);
});
JSFiddle
Fiddle
Summary
Use data-bind="visible: isChecked" in "row-template" to show only selected records in the bottom table.
Make template for checkbox as
<input type="checkbox" name="selection" data-bind="checked: isChecked"/>
In the delete function, use following
employee.set('isChecked', false);
Use a dictionary to store [employee, boolean] to store employee and the checkbox state, and bind the dictionary to the view
Check this