knockout automatically presses button after updating view - javascript

I have a table with TRbind in foreach loop:
<tbody data-bind="foreach: data">
<tr>
<td><input type="text" class="form-control" data-bind="value: DeviceSerialNumber" required /></td>
<td><input type="datetime" class="form-control datepicker" placeholder="Od" data-bind="value: StartDate" required /></td>
<td><button class="btn btn-default" data-bind='click: $root.removeRow'>Delete</button></td>
</tr>
</tbody>
the button is bind to this function:
self.removeRow = function (eq) {
self.data.removeAll([eq]);
};
I add data in this way:
var a = new Eq();
console.log(self);
a.StartDate(self.StartDateTemp());
a.DeviceId(self.DeviceTemp());
console.log(a);
console.log(self.data().length);
self.data.push(a);
console.log(self.data().length);
and data is:
var ViewModel = function () {
var self = this;
self.DeviceTemp = ko.observable();
self.StartDateTemp = ko.observable();
self.data = ko.observableArray([]);
}
The problem is occurring only in IE10. If I add anything to the data array the view is Updated. As in the view I add the is button bind to remove at the end of the tr. For unknown reason knockout or browser clicks that button and removes freshly added row.
Can You help me?
JSFIDDLE

Try adding return false after self.data.push(a); self.Test(""); to prevent event from bubbling:
self.searchKeyboardCmd = function (data, event) {
if (event.keyCode == 13) {
if ((self.StartDateTemp() === undefined)
|| (self.number() != 1 && self.Test() != undefined)
&& (self.number() != 1 && self.Test() != "")) {
alert("Bledna konfiguracja. Urzadzenia posiadajace numer seryjny nie moga byc dodane wiecej niz 1 raz")
return true;
}
if (self.number() < 1 || isNaN(self.number())) {
alert("Bledna ilosc");
return true;
}
var a = new Eq();
a.StorageId(self.StorageTemp());
a.StartDate(self.StartDateTemp());
a.DeviceSerialNumber(self.Test());
a.DeviceId(self.DeviceTemp());
a.Issue(self.Issue())
a.IssueDesc(self.IssueDesc());
a.Quantity(self.number());
a.Project(self.Project());
a.MeUser(self.MeUser());
self.data.push(a);
self.Test("");
return false;
}
return true;
};

Related

Knockout: Table Select Unselect Row. Issue/challenge with unselecting Row

I have a Table using Knockout to fill in data and to select the rows. The challenge right now is I can select the row and it's details I can see. But when I click on paging or if on any particular search box I wish to deselect the Row.
Here is the Fiddle which will explain More
Below is the Model Code for the HTML Page
var RowModel = function(id, name, status) {
this.ID = ko.observable(id);
this.Name = ko.observable(name);
this.Status = ko.observable(status);
};
RowModel.fromRawDataPoint = function(dataPoint) {
return new RowModel(dataPoint.id, dataPoint.name, dataPoint.status);
};
From the fiddle, I can see that you are implementing deselect by calling self.selected(null) and self.enableEdit(false).
So you can simply call these again whenever the page is changed or when a search is done.
self.deselect = function(){
self.selected(null);
self.enableEdit(false);
};
this.next = function() {
self.deselect();
if(self.pageNumber() < self.totalPages()) {
self.pageNumber(self.pageNumber() + 1);
}
}
this.lastpage = function() {
self.deselect();
if(self.pageNumber() < self.totalPages()) {
self.pageNumber(self.totalPages());
}
}
this.firstpage = function() {
self.deselect();
if(self.pageNumber() != 0) {
self.pageNumber(self.pageNumber()-self.pageNumber());
alert(self.pageNumber());
}
}
this.previous = function() {
self.deselect();
if(self.pageNumber() != 0) {
self.pageNumber(self.pageNumber() - 1);
}
}
Edit: After your comment about the ID, Name and Status not getting updated, I added 3 new observables selectedName, selectedID and selectedStatus. I am using these observables in HTML so that they can be updated whenever selected is changed. This is done by using a subscribe function on selected.
HTML
<input type="text" name="ID" data-bind="value: selectedID, enable: enableEdit" />
<br>Name :
<input type="text" name="Name" data-bind="value: selectedName, enable: enableEdit" />
<br>Status :
<input type="text" name="Status" data-bind="value: selectedStatus, enable: enableEdit" />
JS
self.selected = ko.observable(self.items()[0]);
self.selectedID=ko.observable(self.items()[0].ID());
self.selectedName=ko.observable(self.items()[0].Name());
self.selectedStatus=ko.observable(self.items()[0].Status());
self.selected.subscribe(function(newValue){
if (newValue === null){
self.selectedID(null);
self.selectedName(null);
self.selectedStatus(null);
return;
}
if (typeof newValue !== 'undefined'){
self.selectedID(newValue.ID());
self.selectedName(newValue.Name());
self.selectedStatus(newValue.Status());
}
});

$().each(function() not getting proper dynamic data

I have an existing table with data coming from the database inside <input></input>. If I want to click the Add button or press Enter key, it should append a new row.
Once I enter a data in the newly added row, I compare it with my existing array or previously added data on the same table. If there's an existing data in either my array or in the same table, the <input></input> shall remain empty until I enter a data that is unique and automatically appends a new row and so on.
Currently, only the first Enter keypress is working.
Here's my HTML:
<table id="datatable-boxes" class="table table-hover table-bordered table-striped forex-datatable">
<thead>
<tr>
<th>Courier Tracking #</th>
<th>Courier</th>
<th>Vendor</th>
<th width="20%">Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="text" form="edit-boxes-form" class="form-control input-sm retrieve_track_no from_db" name="retrieve_courier_tracking_no[]" id="retrieve_courier_tracking_no_1" value="4987176601285" /></td>
<td><input type="text" form="edit-boxes-form" class="form-control input-sm from_db" name="retrieve_courier_name[]" id="retrieve_courier_name_1" value="Philpost" /></td>
<td><input type="text" form="edit-boxes-form" class="form-control input-sm from_db" name="retrieve_vendor_name[]" id="retrieve_vendor_name_1" value="Ebay" /></td>
<td class="box-action"><button class="btn btn-danger btn-xs clear-data" data-toggle="tooltip" data-toggle="modal" title="Delete Box data" data-target="#delete_box_modal_1"><span class='glyphicon glyphicon-trash' aria-hidden="true"></span></button></td>
</tr>
</tbody>
</table>
My JS:
$(function(){
// GET ID OF last row and increment it by one - Edit Boxes
var $lastRetChar = 1, $newRetRow;
$get_lastRetID = function(){
var str = $('#datatable-boxes tr:last-child td:first-child input').attr("id");
var index = str.lastIndexOf("_");
var result = str.substr(index+1);
var prev_char = $lastRetChar = parseInt(result);
var courier_name = $('#retrieve_courier_name_'+prev_char).val();
var vendor_name = $('#retrieve_vendor_name_'+prev_char).val();
$lastRetChar = $lastRetChar + 1;
$newRetRow = "<tr><td><input type='text' form='edit-boxes-form' class='form-control input-sm retrieve_track_no' name='retrieve_courier_tracking_no[]' id='retrieve_courier_tracking_no_"+$lastRetChar+"' autofocus='true'/></td><td><input form='edit-boxes-form' type='text' class='form-control input-sm' name='retrieve_courier_name[]' id='retrieve_courier_name_"+$lastRetChar+"' value='"+courier_name+"'/></td><td><input form='edit-boxes-form' type='text' class='form-control input-sm' name='retrieve_vendor_name[]' id='retrieve_vendor_name_"+$lastRetChar+"' value='"+vendor_name+"' /></td><td class='box-action'><button class='btn btn-danger btn-xs del_RetrieveBox'><span class='glyphicon glyphicon-trash' data-toggle='tooltip' title='Delete row'></span></button></td></tr>";
return $newRetRow;
}
$('#datatable-boxes').on('keypress','input[name^="retrieve_courier_tracking_no[]"]:last',function(e){
console.log(this);
$get_lastRetID();
if (e.which == 13) {
var seen = {};
$('input[name^="retrieve_courier_tracking_no[]"]').each(function() {
var ret_txt = $(this).val();
alert(ret_txt);
found = findItem(boxes_array, ret_txt);
if (seen[ret_txt] || found){
if($(this).hasClass("from_db")){
checker = true;
return true;
} else {
$('.alert-batch-box-data').css('visibility', 'visible');
if(seen[ret_txt]){
$('.alert').append('<p class="text-left">You scanned <strong>'+ret_txt+'</strong> more than once.</p>');
}
if (found) {
$('.alert').append('<p class="text-left"><strong>'+ret_txt+'</strong> already exists in Batch # <strong>'+found.batch_no+'</strong>.</p>');
}
$(this).val('');
box_qty = parseInt($('#datatable-boxes tbody>tr').length);
$('#retrieve_box_qty').val(box_qty);
checker = false;
return false;
}
} else{
if(ret_txt == ''){
alert('eto ata yun');
checker = false;
return false;
}
seen[ret_txt] = true;
if($('#retrieve_courier_tracking_no_1').val() == ''){
$('#add_RetrieveBox').css('visibility', 'hidden');
$('#update_RetrieveBox').css('visibility', 'hidden');
checker = false;
return false;
} else {
checker = true;
return true;
}
}
});
if(checker == true){
$('#datatable-boxes tbody').append($newRetRow);
$(this).closest('tr').next().find('input.retrieve_track_no').focus();
e.preventDefault();
$('#retrieve_box_qty').val($lastRetChar);
$('#add_RetrieveBox').css('visibility', 'visible');
$('#update_RetrieveBox').css('visibility', 'visible');
}
}
});
//Append new row on edit boxes > receive
$('#add_RetrieveBox').on("click", function(){
$get_lastRetID();
$('#datatable-boxes tbody').append($newRetRow);
$('#retrieve_box_qty').val($lastRetChar);
});
//Delete newly added row on frontend - Edit boxes-form
$('#datatable-boxes').on('click','tr .del_RetrieveBox',function(e){
e.preventDefault();
$(this).closest('tr').remove();
$lastRetChar = $lastRetChar-1;
$('#retrieve_box_qty').val($lastRetChar);
});
});
When I tried to alert() the current value that's being called on the second row, it just calls the first row and then returns empty twice. Since empty already showed twice, it will return false so there will be no new row.
Example:
1st data = 4987176601285 (This is from the database)
2nd data = 123 (This is in frontend)
After hitting enter to have a 3rd row, the alerts are these:
4987176601285
*blank*
*blank*
eto na yun
I'm not sure why my newly entered data is not being picked up. Any help is highly appreciated. Thanks!
I changed my each() function with filter(). I'm not entirely sure why this one worked and the other one did not.
$('#datatable-boxes input[name^="retrieve_courier_tracking_no[]"]').filter(function() {
value = $(this).val();
ret_found = findItem(boxes_array, value);
if (existing.indexOf(value) >= 0) {
// console.log($(this).val());
ret_checker = false;
if(ret_found){
$('.alert-batch-box-data').css('display', 'visible');
$('.alert').append('<p class="text-left"><strong>'+value+'</strong> already exists in Batch # <strong>'+ret_found.batch_no+'</strong>.</p>');
} else if((ret_checker == false) && (!ret_found)){
frontend_duplicate = true;
if($(this).val() == ''){
frontend_duplicate = false;
}
} else {
frontend_duplicate = false;
}
$(this).val('');
} else {
ret_checker = true;
frontend_duplicate = false
}
existing.push(value);
});
Then I modified it to fit with my previous code.

How to assign reassign checkbox ng-model on ng-repeat

Please help me out. I have a checkboxes with models defined. I am displaying checkboxes and using the model to set if the checkbox is selected or not. Below is the code for setting the ng-model.
LoadValues(obj) {
vm.index = false;
vm.create = false;
vm.edit = false;
vm.delete = false;
vm.other = false;
var pList = obj.Functions;
var currentModule = obj.Name;
for (var i = 0; i < pList.length; i++) {
var currentItem = pList[i];
console.log(currentItem)
if (currentItem.search("Index") > 0) {
vm.index = true;
console.log(vm.index);
} else if (currentItem.search("Create") > 0) {
vm.create = true;
} else if (currentItem.search("Edit") > 0) {
vm.edit = true;
} else if (currentItem.search("Delete") > 0) {
vm.delete = true;
} else if (currentItem.search("Other") > 0) {
vm.other = true;
}
}
}
Below is the check boxes.
<tbody>
<tr ng-repeat="item in list">
<td>
{{item.Name}}
</td>
<td>
<input id="Index" type="checkbox" ng-model="vm.index" ng-click="EditRole(Right,item.Module,'Index')">
</td>
<td>
<input id="Create" type="checkbox" ng-model="vm.create" ng-click="EditRole(item.Role,'Create')">
</td>
<td>
<input id="Edit" type="checkbox" ng-model="vm.edit" ng-click="EditRole(item.Role,item.Module,'Edit')">
</td>
<td>
<input id="Delete" type="checkbox" ng-model="vm.delete" ng-click="EditRole(item.Role,item.Module,'Delete')">
</td>
<td>
<input id="Other" type="checkbox" ng-model="vm.other" ng-click="EditRole(item.Role,item.Module,'Other')">
</td>
</tr>
</tbody>
The problem is it assigns the same ng-model to all the items in the list. I have tried to find solutions nothing is helping. Your help would be very much appreciated.
i am reading my data from a json file. Below is some example data:
[
{"Role":"Staff","Admins":[{"Name":"Username","userRights":["UserEdit","UserCreate"]
}]
The easiest way to use ng-model on a checkbox is to pass it an abject. The code below converts an array of items into an object for the checkboxes.
I created a variable called $scope.userRights which contains all of the available options.
In the HTML we loop though each field displaying its name and then loop though all of the userRights.
The submit button then converts the object back into the array format we received.
HTML
<div ng:controller="MainCtrl">
<button ng-click="submit()">Submit</button>
<table>
<tr ng-repeat="field in fields">
<td ng-bind="field.Name"></td>
<td ng-repeat="right in userRights">
<label>
<input type="checkbox" ng-model="field.userRights[right]" /> {{right}}
</label>
</td>
</tr>
</table>
<pre ng-bind="fields | json"></pre>
</div>
JavaScript
app.controller('MainCtrl', function($scope) {
$scope.userRights = ["UserEdit","UserCreate","UserSomethingElse"];
$scope.fields = [
{"Name":"Username","userRights":["UserEdit","UserCreate"]},
{"Name":"Password","userRights":["UserEdit"]}
];
// Convert array to object
$scope.fields.forEach(function(field) {
var res = {};
field.userRights.forEach(function(right) {
res[right] = true;
});
field.userRights = res;
});
function objectValues(obj) {
var res = [];
var keys = Object.keys(obj);
for (var i=0; i<keys.length; i++) {
if (obj[keys[i]]) res.push(keys[i]);
}
return res;
}
// Convert object to array
$scope.submit = function() {
$scope.fields.forEach(function(field) {
field.userRights = objectValues(field.userRights);
});
};
});
Demo
Your ng-model has to be like so:
ng-model="item.index"
And then in your controller inside the for loop:
current_item.index = true;
Hope it helps =)

Control page behavior by key board

I need control the behavior page by key board ... I was trying like this:
this.showSearcherCustomerKeyCommand = function (data, event) {
if (event.shiftKey && event.keyCode == 49) {
alert("Combination done !");
}
};
And I set the binding in a secction, after I've set the bindings in a div, and the result was unsuccessfully ... So what I need is that the behavior of my page can be controlled by keyboard ... That my user can make this combination: Crtl + 1, ctrl + 2, whatever and that by knockout I can show, hide (modal), apply or cleannode bindings, and another things ... It's possible ?? Thanks ...
UPDATE
Here is my view model:
Billing.BillingViewModel = function () {
this.billingDate = ko.observable();
this.billingCode = ko.observable();
this.billingId = ko.observable();
this.billingIva = ko.observable(0);
this.billingSubTotal = ko.observable(0);
this.billingTotal = ko.observable(0);
this.billingObservations = ko.observable("");
this.billingDetails = ko.observableArray();
this.billingState = ko.observable();
this.billingClient = new Billing.ClientViewModel();
this.billingCurrentProductSelected = new Billing.ProductViewModel();
this.showSearcherCustomerKeyCommand = function (data, event) {
if (event.shiftKey && event.keyCode == 49) {
alert("Combination done !");
}
};
};
Here is when apply the bindings:
Billing.SetViewModel = function () {
theMainViewModel = Billing.PrepareBilling();
theManagerViewModel = new Billing.ManagerBillingViewModel();
theGeneralViewModel = new Billing.GeneralViewModel();
ko.applyBindings(theMainViewModel.Billing, $("#" + Billing.BillingHeaderSecction)[0]);
ko.applyBindings(theMainViewModel.Billing, $("#" + Billing.BillFooterSecction)[0]);
ko.applyBindings(theMainViewModel.Billing, $("#" + Billing.BillProductChoosenSecction)[0]);
ko.applyBindings(theMainViewModel.Billing, $("#" + Billing.BillDetailsSecction)[0]);
ko.applyBindings(theMainViewModel.FinishBilling, $("#" + Billing.BillFinishSecction)[0]);
ko.applyBindings(theManagerViewModel, $("#" + Billing.ManagerSecction)[0]);
ko.applyBindings(theMainViewModel, $("#" + Billing.BillActionsSecction)[0]);
ko.applyBindings(theMainViewModel, $("#" + Billing.SelectorModalSearcherCustomerId)[0]);
ko.applyBindings(theMainViewModel, $("#" + Billing.SelectorModalSearcherProductId)[0]);
//ko.applyBindings(theGeneralViewModel, $("#" + Billing.BillGeneralSecction)[0]);
theManagerViewModel.theBillings.push(theMainViewModel);
Billing.SetMask();
};
And here is my secction tag control (HTML):
<section id="BillHeaderSecction" **data-bind="event: { keypress: billingClient.showSearcherCustomerKeyCommand }, valueupdate: 'afterkeydown'"**>
<input type="hidden" id="hdfBillingId" name="BillingId" data-bind="value: billingId" />
<table style="width: 100%">
<tr>
<td style="width: 30%">
<label>Cliente</label>
<input style="width: 100%" type="text" readonly name="Client" id="txtClient" data-bind="value: billingClient.name" />
</td>
<td style="width: 5%"></td>
<td style="width: 30%">
<label>Fecha</label>
<input style="width: 100%" type="text" readonly name="CreationDate" id="txtCreationDate" data-bind="value: billingDate" />
</td>
<td style="width: 5%"></td>
<td style="width: 30%">
<label>Código</label>
<input style="width: 100%" type="text" readonly name="BillingCode" id="txtBillingCode" data-bind="value: billingCode" />
</td>
</tr>
</table>
</section>
PD: I've put on bold the secction tag that I want the being controlled by keyboard
keydown and keypress will give you different values for event.keyCode. I guess you have to use keydown instead.
Look this Keyevent Tester
I just tested your code with keydown and keypress events:
...
var showSearcherCustomerKeyCommand = function (event) {
if (event.shiftKey && event.keyCode == 49) {
alert("Combination done ! (Fired by keydown event)");
}
};
window.onkeydown = showSearcherCustomerKeyCommand; // alert 49
window.onkeypress = showSearcherCustomerKeyCommand; // alert 33
And... event will be the first parameter passed to your function.
http://jsfiddle.net/01teg8yL/2/

JavaScript Form Validation on submit

I have a simple form and am validating onchange and need a final validation onsubmit. I am displaying a message to the right of the inputbox on error. I'm trying to keep this at DOM 1 compatible.
HTML
<form id = "myForm" action = "" onsubmit = "return validateForm(this);">
<table class = "table-submit" border = "0">
<tr>
<td>
Username:
</td>
<td>
<input type = "text" id = "username"
size = "30" maxlength = "30"
onchange = "validateUsername(this, 'msgUsername')" />
</td>
<td id = "msgUsername">
</td>
</tr>
<tr>
<td>
Password:
</td>
<td>
<input type = "password" id = "password"
size = "30" maxlength = "30"
onchange = "validatePassword(this, 'msgPassword')" />
</td>
<td id = "msgPassword">
</td>
</tr>
<tr>
<td> </td>
<td>
<input type = "submit" value = "Submit" />
<input type = "reset" value = "Clear" />
</td>
</tr>
</table>
</form>
JavaScript
function validateUsername(myItem, myElement) {
var dom = document.getElementById(myElement);
if (myItem.value.length < 3) {
dom.innerHTML = " Username needs to be a minimum of 3 characters! ";
return false;
}
else {
dom.innerHTML = "";
return true;
}
}
function validatePassword(myItem, myElement) {
var dom = document.getElementById(myElement);
if (myItem.value.length < 5) {
dom.innerHTML = " Password needs to be a minimum of 5 characters! ";
return false;
}
else {
dom.innerHTML = "";
return true;
}
}
function validateForm (itm) {
// kind of stuck here...
}
As you may of noticed, I am a bit stuck on my validateForm() function.
The code validates on each inputbox onchange event.
Not sure what is the best way to go from here. I thought about doing an If for my both single input box validation, but I would need to send each parameters which is what i was trying to avoid by using this.
Would like some suggestions.
Separate concerns. Instead of having the validate functions not only validate but also report your painting your self into a corner. Instead have a validate function which only returns true/false and another that is your onChange event handler which calls the validate function and displays the error message if needed. Then your onSubmit handler can easily call the validation functions in an if/else block to allow or cancel the submit action.
function validateUsername(username) {
return username.length >= 3;
}
function validatePassword(password) {
return password.length >= 5;
}
function showErrorFn(divId, message) {
var div = document.getElementById(divId);
message = " " + message;
return function(message) {
div.innerHTML = message;
};
}
function makeChangeHandler(myItem, validationFn, errorFn) {
return function(e) {
if (validationFn(myItem.value)) {
return true;
} else {
errorFn();
return false;
}
};
}
function makeSubmitHandler(usernameEl, passwordEl) {
return function(e) {
if (validateUsername(usernameEl.value) && validatePassword(passwordEl.value)) {
return true;
} else {
e.preventDefault();
return false;
}
}
var usernameEl = document.getElementById("username");
var usernameErrorEl = document.getElementById("msgUsername");
usernameEl.addEventListener("change", makeChangeHandler(
usernameEl,
validateUsername,
showErrorFn("Username must be more then 3 characters")
);
var usernameEl = document.getElementById("password");
var usernameErrorEl = document.getElementById("msgPassword");
usernameEl.addEventListener("change", makeChangeHandler(
usernameEl,
validatePassword,
showErrorFn("Password must be more then 5 characters")
);
var formEl = document.getElementById("myForm");
formEl.addEventListener("submit", makeSubmitHandler(usernameEl, password));
You can try this...
function validateForm (itm) {
var flag = true;
flag = (validateUsername(itm.username, 'msgUsername') && flag);
flag = (validatePassword(itm.password, 'msgPassword') && flag);
return flag;
}

Categories

Resources