How to get totalRows from Backgrid Client-Side search - javascript

I am having trouble getting the correct totalRecords value from my collection when performing a search with Backgrid's Client-Side filter extension.
Specifically, when I use the backspace key on my keyboard.
If I do not use the backspace, and type slowly, this seems to work fine:
//Search input field - keyup event
$("input[name='q']").keyup(function () {
console.log('searchcontainer input - keyup event');
console.log($(this).val())
console.log($(this).val().length)
var key = event.keyCode || event.charCode;
if (key == 8) { //'8' == backspace key
console.log('backspace was keyed!')
//how do I refresh the 'totalRecords' property on the collection?
}
console.log((_myCollection.state.totalRecords || "0") + " records found."));
$("#lblRecordsFound").text((_myCollection.state.totalRecords || "0") + " records found.");
});
It seems like the totalRows skips a collection update(?) when a backspace is fired?
How can I get the current totalRows, when using backspace? Do I need to reset, fetch or refresh the collection? I am unsure. Help?
I simply need the totalRows that are currently displayed in the grid, at any moment.

I ended up "altering" the backgrid-filter.js extension.
I altered the search function, like so:
/**
Takes the query from the search box, constructs a matcher with it and
loops through collection looking for matches. Reset the given collection
when all the matches have been found.
If the collection is a PageableCollection, searching will go back to the
first page.
*/
search: function () {
var matcher = _.bind(this.makeMatcher(this.query()), this);
var col = this.collection;
if (col.pageableCollection) col.pageableCollection.getFirstPage({ silent: true });
col.reset(this.shadowCollection.filter(matcher), { reindex: false });
var message = " items found.";
if (col.pageableCollection.state.totalRecords == 1) {
message = " item found.";
}
$("#lblRecordsFound").text((col.pageableCollection.state.totalRecords || "0") + message);
},
Works well. I do not understand why Backgrid does not have an exposed property for easy access to the current total rows.

Related

Javascript validation on dynamic inputs and combining inputs with some logic

In a form (named createform) many inputs are created dynamically by clicking on add button (from 0 to any number for each kind of input).
The problem I'm having is the validation of the inputs that were created dynamically, because there is a complex logic behind it.
THE SCENARIO
I can have several different inputs:
brand
model
country
region
The first of them is called brand1, model1, country1 and region1, then adding others they will be called for instance brand2... brand50
In the starting scenario there will be only brand1 and model1. The country and region inputs are added only by clicking on a button.
THE VALIDATION CONDITION
I have to submit the form only in two cases:
If there is at least one brand + one model where both of them are not empty (any brand and any model, so it can also be brand5 and model12)
If there is at least one brand + country + region not empty (all of them not empty, same logic than before)
I made the following validation function which works good if I assume that I just have the first brand,model,country and region (so brand1,model1,country1 and region1).
THE CODE
function validateForm() {
var brand = document.forms["createform"]["brand1"].value;
var model = document.forms["createform"]["model1"].value;
if (document.forms["createform"]["country1"] === undefined) {
var country = "";
} else {
var country = document.forms["createform"]["country1"].value;
}
if (document.forms["createform"]["country1"] === undefined) {
var region = "";
} else {
var region = document.forms["createform"]["region1"].value;
}
if ((brand != "") && (model != "")) {
alert("Send");
return true;
} else if ((brand != "") && (country != "") && (region != "")) {
alert("Send");
return true;
} else {
alert("Impossible to send");
return false;
}
}
For better reading of the code I added return true even if it is not necessary.
The main problem is that it is impossible to know how many inputs there will be of every different kind. I was thinking about trying by checking if the inputs are starting with brand,model,country or region but I don't know how to cross my controls in my validation function with all the possible results.
Do you guys have any idea of how to solve this?
What you need is a way to access all your brand and model elements by the start of their name.
var elements = document.querySelectorAll("form[id='createform']>input[id^='brand']");
This will give you an array (instead of your single valued variable) which you can loop through looking for your values.
The nifty querySelectorAll accepts selectors which can narrow your search to all matching elements. In the example, it gets all input elements within a form named "createform" which start with (^=) "brand".
var brandelements = document.querySelectorAll("#createform select[name^='brand'] option:checked:not([value=''])");
This is the best way to get all the values of a select (you can easily change with a normal input) that are not empty.

How to obtain full value inside jQuery's keypress event?

I'm writing some jQuery which intercepts the value entered in an input field and determines whether to allow or to prevent the typed value.
I need to get the next value, i.e., what the value will be were I to permit the key-press, and I need to know this value at a point before it is displayed, so I'm using the keypress event.
My question is: inside the keypress event, how can I tell what the resultant value would be were I to permit the key-press? What is the 'potential value'?
If I write out the key-press event object to the console and inspect the properties, I can see that currentTarget.value shows this 'potential value'. However, if I use this property inside the keypress event then it returns only the value prior to the context key-press.
For example, if the user types "a" into an empty text box bound to the following jQuery
$(":input").on("keypress", function(e) {
console.log(e);
console.log(e.currentTarget.value);
});
Digging down through the first console output (e) shows that currentTarget.value = "a".
But the second console output (e.currentTarget.value) will show "".
If the user was then to type "b" into that same text box then:
Manually inspectng e and locating currentTarget.value displays "ab"; Dumping e.currentTarget.value from inside the event displays "a".
Can anyone explain how I can get this 'potential value' while inside the keypress event?
Not the prettiest of solutions (you can see the would be result just before it's reverted), but to save the trouble of discerning between arrow/control and input keys etc, you could store the original value in keypress and revert to that in keyup if needed (also storing the selection positions for complete reversion)
$(":input").keypress(function(e) {
$(this).data('orgValue', {value: this.value, pos: this.selectionStart, selend:this.selectionEnd});
}).keyup(function(e){
var val = this.value;
if(!somevalidation(val)){
var org =$(this).data('orgValue');
this.value = org.value;
this.selectionStart = org.pos;
this.selectionEnd = org.selend;
}
});
Example fiddle
Edit
Did some testing, but jquery makes predicting the outcome relatively easy. Not only does it fill the key property, it also fills other properties on its event on which the type of key can be checked. While testing charCode seems to be 0 for 'non input' keys.
The straight forward would be:
$(":input").keypress(function(e) {
if(!e.charCode)return; //is 0 for non input
var cur = this.value; //current value
var val = cur.substring(0,this.selectionStart)
+ e.key
+ cur.substring(this.selectionEnd);
return cur=== val //unchanged
|| somevalidation(val);
});
But that would not include deletes/backspaces, to handle those as well:
$(":input").keypress(function(e) {
var key,start = this.selectionStart ,end = this.selectionEnd;
if(e.charCode)
key = e.key;
else{
if(e.keyCode===8 || e.keyCode===46){
key = '';
if(end===start){
if(e.keyCode===8)
start--;
else
end++;
}
}
else
return true; //charCode is 0 for non input 46 = delete 8 = backspace
}
var cur = this.value; //current value
var val = cur.substring(0, start) + key + cur.substring(end);
return cur=== val //unchanged
|| somevalidation(val);
});
Fiddle
While testing this seemed to behave as expected. An other way might be have a hidden input field, send the keys there and examine its results, but the above should do the trick.

Filter options by reading character length inside for loop

I have a widget (the widget code in the pen linked below is not the actual code, please just pay attention to the filtering function jQuery.fn.doFilterOptions(){..}).
Use case:
I have a non-native selectbox. I need to extend its functionality to accept an onclick event which allows the user to type data into the selectbox (not targeting a traditional <select>), it should filter the .options available by simply showing or hiding them based on its inner HTML value, if no match is found at any point during the loop through the string being entered by the user, I need the options to continue not being displayed.
Issue:
Right now it works 95% of the way, the only issue is that if an invalid char is found, the loop keeps checking the rest of the users entries char by char, and if the next char is a match to any of the options in the same index, it re-display's this as a valid .option.
$('.selectbox .selected').on('keyup', function(){
var theseOptions = $(this).parent('.selectbox').find('.option');
var defaultPlaceholder = $(this).data('placeholder');
var filterOptions = (function(curSelectedVal){
if (curSelectedVal === ' ' || curSelectedVal.length === 0 || curSelectedVal === defaultPlaceholder){
theseOptions.show();
}
var optionsVal;
var doInputOptionsComparison = (function(){
var invalidOption = false;
for (var letterPos = 0; letterPos < curSelectedVal.length; letterPos++){
theseOptions.each(function(optionIteration){
var thisOption = $(this);
thisOptionsVal = thisOption.html();
if (curSelectedVal.length > thisOptionsVal.length ){ // If a longer string has been input by the user than exists in the option being iterated over, hide this option
thisOption.hide();
invalidOption = true;
}
else if ((thisOptionsVal[letterPos].toLowerCase().trim() === curSelectedVal[letterPos].toLowerCase().trim()) && invalidOption === false){ // If the input string matches this option and no invalid options have been found in the letterPos prior to it, show this option
thisOption.show();
}
else { // If the string does not match any option
invalidOptionFound = true;
thisOption.hide();
}
});
}
})();
})($(this).html());
});
Here is the demo, try selecting then typing abz you will see the filter working properly.
Now erase that input data, and now type azc. You will see the abc option comes available again because the c matches in that same index (user input[i] = optionsHtml[i] = show();), resulting the the above described undesirable effect.
http://codepen.io/nicholasabrams/pen/KwwMPG?editors=001
BONUS:
Would this be easier by using regEx to do the filtering?
I managed to use a dynamic regEx filter function it it cut the code down big time! Wow what a better solution.
$.fn.filterOptionsByUserInput = function(optionSelector){
var curInput = $(this).html().trim().replace(/ /g, '').toLowerCase();
$(optionSelector).each(function(optionIndex){
var userInputRegEx = new RegExp('^'+curInput+'.*');
if ($(this).html().toLowerCase().trim().match(userInputRegEx)){
$(this).fadeIn('slow');
}
else {
$(this).fadeOut('slow');
}
});
};
http://codepen.io/nicholasabrams/pen/LEEwrm?editors=001

Displaying all info for a contact using Cordova iOS

I have the following code that I am using successfully to produce a list of all contacts on the phone or the results of a search of all contacts. I have it working with the name and phone number, where multiple records for one contact show up as multiple list entries, just the way I want it. The problem I am having, and maybe I am missing something obvious here, is that I can't get any other fields to show up in the list. I have tried adding an "or" to the j variable with the next field and while it returns the same results, as soon as I add the next field to the list it all breaks. I do consistently get the results, but I cannot get the results to display. I also tried to figure out a way to add another "for" loop, but as expected they always just filtered down the already filtered results, and usually just gave me one result. The js is below, any help is appreciated. I can't really do a fiddle as its Cordova.
// search below
var fields = ["givenName", "familyName", "name", "emails", "phoneNumbers", "addresses", "organizations"],
options = new ContactFindOptions();
var filter = $('#contacts_filter')[0].value
// set Options
options.filter = (filter && filter !== "Search All") ? filter : "";
options.limit = 15; //doesn't work for some reason
options.multiple = true;
//find function
navigator.contacts.find(fields, function (foundContacts) {
//if their are results
if (foundContacts.length > 0) {
$("#contact_list").html("<h5 style='text-align: center'>" + foundContacts.length + " results found.</h5>");
for (var i = 0; i < foundContacts.length; i++) {
if(null != foundContacts[i].phoneNumbers )
{
for(var j=0; j < (foundContacts[i].phoneNumbers.length); j++)
{
$('#contact_list').append("<li><h2>" + foundContacts[i].name.familyName + ", " + foundContacts[i].name.givenName + "</h2><p>" + foundContacts[i].addresses[j].streetAddress + "</p><p>" + foundContacts[i].phoneNumbers[j].value + "</p></li>");
}
}
}
} else {
$("#contact_list").html("<h5 style='text-align: center'>No Contacts Found!</h5>");
}
edit: extraneous id removed.
I figured out, through researching on the phoneGap forums. that I wasn't allowing for null answers in my loops. So adding if(foundContacts[i].addresses != null solved the problem. Thanks for everyones help.
if you're positive you're getting the results and that it not displaying then it's probably not an issue with your existing code but your dynamically added results. Make sure you're refreshing if you're using jquery mobile; as a side note, i've done something similar to your code before, and if you're having problems with each consecutive entry after the first one its because you aren't emptying the object i handled this by doing something like this in my code:
navigator.contacts.displayName.empty();
however i'm sure if you return false; it could potentially stop the data from bubbling up in the contacts object as well.
make sure you only use your id once as well, otherwise make it a class, every time it gets through that loop you're adding the same "contact_name" id.

Doing key combos with jQuery / JavaScript

I'm curious how i, with the following jQuery plugin code im writing at the bottom of this question, could implement key combos. How it's working so far is it allows a user to create key commands simply by doing a normal jQuery like syntax and provide an event for the key command, like so:
$(window).jkey('a',function(){
alert('you pressed the a key!');
});
or
$(window).jkey('b c d',function(){
alert('you pressed either the b, c, or d key!');
});
and lastly what i want is the ability to do, but can't figure out:
$(window).jkey('alt+n',function(){
alert('you pressed alt+n!');
});
I know how to do this outside of the plugin (on keyup set a var false and on keydown set the var true and check if the var is true when you press the other key), but i don't know how to do this when you dont know what keys are going to be pressed and how many. How do I add this support? I want to be able to allow them to do things like alt+shift+a or a+s+d+f if they wanted. I just can't get my head around how to implement this. Any ideas?
I'm going to release this as an open source plugin and i'd love to give whoever gives me the right, working, answer some credit on the blog post and in the code it's self. Thanks in advance!
(function($) {
$.fn.jkey = function(keyCombo,callback) {
if(keyCombo.indexOf(' ') > -1){ //If multiple keys are selected
var keySplit = keyCombo.split(' ');
}
else{ //Else just store this single key
var keySplit = [keyCombo];
}
for(x in keySplit){ //For each key in the array...
if(keySplit[x].indexOf('+') > -1){
//Key selection by user is a key combo... what now?
}
else{
//Otherwise, it's just a normal, single key command
}
switch(keySplit[x]){
case 'a':
keySplit[x] = 65;
break;
case 'b':
keySplit[x] = 66;
break;
case 'c':
keySplit[x] = 67;
break;
//And so on for all the rest of the keys
}
}
return this.each(function() {
$this = $(this);
$this.keydown(function(e){
if($.inArray(e.keyCode, keySplit) > -1){ //If the key the user pressed is matched with any key the developer set a key code with...
if(typeof callback == 'function'){ //and they provided a callback function
callback(); //trigger call back and...
e.preventDefault(); //cancel the normal
}
}
});
});
}
})(jQuery);
Use keypress instead of keyup/keydown because the latter two do not accurately protray the keycode (reference, see last paragraph). You can reference the altKey ctrlKey and shiftKey boolean properties of the event object in this case...
$(document).keypress(function(e) {
var key = String.fromCharCode(e.which);
var alt = e.altKey;
var ctrl = e.ctrlKey
var shift = e.shiftKey;
alert("Key:" + key + "\nAlt:" + alt + "\nCtrl:" + ctrl + "\nShift:" + shift);
});
Also, you can use String.fromCharCode to translate the key code to an actual letter.
You can't trap multiple keys aside from combinations with Ctrl, Alt, and Shift. You simply can't do it in a single event. So throw the a+s+d+f idea out the window.
Note: Obviously there are certain key combinations that are used by the browser. For instance, Alt + F usually brings up the File menu in Windows. Ctrl + N usually launches a new window/tab. Do not attempt to override any of these combinations.
Here's a live demo for your testing pleasure.
Here's what I came up with. Essentially what I did was created a JSON object that stores all the key codes. I then replace all the provided keys with the codes. If the keys are using the '+' to make a key combo, I then create an array of the codes out of it.
We then create another array that stores all the keys that are being pressed (keyDown add the item, keyUp removes it). On keyDown, we check if it's a single key command or combo. If it's a combo, we check it against all the currently active key presses. If they all match, we execute the callback.
This will work with any number of key combos. Only time I saw that it wasn't working is when you use the 'alert()' to display a message on the key combo because it will no longer remove the items from the active key press array.
(function($) {
$.fn.jkey = function(keyCombo,callback) {
// Save the key codes to JSON object
var keyCodes = {
'a' : 65,
'b' : 66,
'c' : 67,
'alt' : 18
};
var x = '';
var y = '';
if(keyCombo.indexOf(' ') > -1){ //If multiple keys are selected
var keySplit = keyCombo.split(' ');
}
else{ //Else just store this single key
var keySplit = [keyCombo];
}
for(x in keySplit){ //For each key in the array...
if(keySplit[x].indexOf('+') > -1){
//Key selection by user is a key combo
// Create a combo array and split the key combo
var combo = Array();
var comboSplit = keySplit[x].split('+');
// Save the key codes for each element in the key combo
for(y in comboSplit){
combo[y] = keyCodes[ comboSplit[y] ];
}
keySplit[x] = combo;
} else {
//Otherwise, it's just a normal, single key command
keySplit[x] = keyCodes[ keySplit[x] ];
}
}
return this.each(function() {
$this = $(this);
// Create active keys array
// This array will store all the keys that are currently being pressed
var activeKeys = Array();
$this.keydown(function(e){
// Save the current key press
activeKeys[ e.keyCode ] = e.keyCode;
if($.inArray(e.keyCode, keySplit) > -1){ // If the key the user pressed is matched with any key the developer set a key code with...
if(typeof callback == 'function'){ //and they provided a callback function
callback(); //trigger call back and...
e.preventDefault(); //cancel the normal
}
} else { // Else, the key did not match which means it's either a key combo or just dosn't exist
// Check if the individual items in the key combo match what was pressed
for(x in keySplit){
if($.inArray(e.keyCode, keySplit[x]) > -1){
// Initiate the active variable
var active = 'unchecked';
// All the individual keys in the combo with the keys that are currently being pressed
for(y in keySplit[x]) {
if(active != false) {
if($.inArray(keySplit[x][y], activeKeys) > -1){
active = true;
} else {
active = false;
}
}
}
// If all the keys in the combo are being pressed, active will equal true
if(active === true){
if(typeof callback == 'function'){ //and they provided a callback function
callback(); //trigger call back and...
e.preventDefault(); //cancel the normal
}
}
}
}
} // end of if in array
}).keyup(function(e) {
// Remove the current key press
activeKeys[ e.keyCode ] = '';
});
});
}
})(jQuery);
This is just a shot in the dark but maybe it'll help you down the right path.
If it's possible to have that function recognize a hexadecimal value for the key that you entered instead of the literal key (such as 0x6E for the letter 'n'), you could derive what "alt+n" translates to in hex and have the function look out for that value.
If you're looking for something that will let a user easily enter and define key combos using a plain input box, I wrote a plugin that does it for you: http://suan.github.com/jquery-keycombinator/

Categories