Select - get previous and new value only on change - javascript

I have a page with a few select boxes on, my objective is to capture the previous value and the new value which I've managed to achieve in the code below.
This builds up a one to one pairing of arrays so array position 0 for previous should be the previous value for the new value in array position 0 of updates. There is server side processing to validate the data passed back so this part doesn't worry me too much.
The problem is - if a user clicks on the select box the focus event is thrown, I capture the previous value, if the user then decides to stay on the same value they just click back on it and the change event is not thrown which puts my array out of sync and leaves me with a false previous value (if it doesn't change I don't need to capture it as previous).
Have I overcomplicated? Any suggestions or points around better handling the user selecting the same value would be appreciated.
var updates = [];
var previous = [];
$("select").on('focus', function () {
item = {};
item["id"] = this.value;
item["name"] = $(this).find('option:selected').text();
previous.push(item);
}).change(function() {
item = {};
item["id"] = this.value;
item["name"] = $(this).find('option:selected').text();
updates.push(item);
});
Thanks to the pointer from the answer below I've ended up with the following code;
Final working code
var updates = [];
$("select").on('focus', function () {
item = {};
item["id"] = this.value;
item["name"] = $(this).find('option:selected').text();
updates.push(item);
}).change(function() {
if (updates.length > 1) {
updates = updates.slice(updates.length-1,updates.length);
}
previousPos = updates.length-1;
newItem = {};
updates[previousPos]["newid"] = this.value;
updates[previousPos]["newname"] = $(this).find('option:selected').text();
$(this).blur();
$.ajax({
type: 'POST',
url: '/update',
data: JSON.stringify(updates)
});
});
I'm handling CSRF token setup elsewhere with ajaxSetup.

why don't you just push to one array instead of two. This way you can get the array length/count to determine if there was a previously selected option?
if(item.length >= 2 ){
// do something
}

Related

Reusing function within different functions (new array from each) - javascript

Have an array which is being compiled based on user input selection (checkbox and radio buttons)
This is being compiled using an input on change function.
The array is then used to check if values in the array match data attributes referenced within each card, and those which match, show the respective card.
This is working fine.
I am now trying to get the same functionality, but instead of being based on user input, the result is being compiled based on user query string (string is also compiled from the user input - effectively saving a query for using to return to page with results without having to enter the checkbox values again). This function has a lot of if true, push which I would like to re-use instead of re-write. I have simplified here.
Problem I am having is using the same function I built for show/hide the card based on user input with the query string.
// Set Globals:
var arr = []
function buildResults() {
// Build query based on input values and push into array:
$("input").on("change", function() {
var arr = [];
$(":checkbox").each(function() {
if ($(this).is(":checked")) {
arr.push($(this).val());
}
});
$(":radio").each(function() {
if ($(this).is(":checked")) {
arr.push($(this).val());
}
});
console.log(arr);
// Join array using unique string
var vals = arr.join("--");
// Set URL to pin query to and begin pushing values to string:
var urlBegin = "https://thisisatest/?results=";
var str = vals;
$("#val").text(urlBegin + vals);
$("#query").text(vals);
$("#copyTarget").val(urlBegin + vals);
userSelection = arr;
resRec();
});
}
buildResults();
function resRec() {
// Show div based on user checkbox values:
var user = userSelection;
var dataRec = [];
var recordResultCount = 0;
console.log(user);
var first = user.includes("123");
if (first == true) {
dataRec.push(123456);
}
var recordResults = [...new Set(dataRec)];
recordResultCount = recordResults.length;
console.log(recordResultCount);
// Show only the records needed:
$(".card").each(function() {
var recordFound = $.inArray($(this).data("recordid"), dataRec);
if (recordFound === -1) {
$(this).parent().addClass("destroy");
} else {
$(this).removeClass("destroy");
}
});
}
function resQuery() {
var urlQuery = window.location.href.match(/results=(.+)/)[1];
console.log(urlQuery);
user = urlQuery;
}
// If user enters page via unique query only, and not from page start:
$(function() {
if (window.location.pathname == "https://thisisatest/?results=") {
// reuse resRec() here, but using urlQuery and not userSelection;
var user = resQuery();
resRec();
// and show only the cards which match the results built from query
}
});
resQuery();
Function reuse is still new to me, and while I think my logic is on the correct path, I am still getting resRec() not defined.
Thank you.

A function to affect multiple rows to substr and copy with Javascript

So, i have this code, it works:
var curp = document.getElementById("id_sc_field_curp_id_1");
var getcurp = curp.options[curp.selectedIndex].text;
var rfc = getcurp.substr(0, 10);
document.getElementById("id_sc_field_virtual_rfc_1").value = rfc;
It copy the text inside the field (td - CURP) "id_sc_field_curp_id_1", and trim it to put the result in another field (RFC) "id_sc_field_virtual_rfc_1"
Example img
JSFIDDLE: https://jsfiddle.net/90yzgcqe/1/
I want to adapt the code to work with the other rows, witch have an incremental id...
id_sc_field_curp_id_1,id_sc_field_curp_id_2,id_sc_field_curp_id_3, d_sc_field_virtual_rfc_1, d_sc_field_virtual_rfc_2, d_sc_field_virtual_rfc_3...etc
Im making this function, but... i dont know how to make it work...
function rfc() {
for (var i = 0; i <= 19; i++) {
var curp = document.getElementById("id_sc_field_curp_id_" + i);
var getcurp = curp.options[curp.selectedIndex].text;
var rfc = getcurp.substr(0, 10);
document.getElementById("id_sc_field_virtual_rfc_" + i).value = rfc;
}
}
What is wrong?
Some jQuery gets us there fairly easily, first get the matching dropdowns and then interact with them.
$(function() {
//get the list of dropdowns that start with all but the numeral
var lst = $("[id^='id_sc_field_curp_id_']");
$.each(lst, function(idx, elem) {
//lets store the dropdown for use in the loop
let $field = $(elem);
//for example lets print the selected text
console.log($field.find("option:selected").text());
});
});
There are a couple of options from there, you can use the dropdown to create the rfc's id, or use the jQuery function closest() to get it. Once you have the associated rfc's input it should be trivial to get set the value.
EDITED:1
More specific javascript, and a link to a modified jsFiddle
$(function() {
//get the list of dropdowns that start with all but the numeral
var lst = $("[id^='id_sc_field_curp_id_']");
$.each(lst, function(idx, elem) {
//lets store the dropdown for use in the loop
let $field = $(elem);
//for example lets alert the selected text
alert($field.find("option:selected").text().substr(0,10));
$field.closest("[id^='idVertRow']")
.find("[id^='id_sc_field_virtual_rfc_']")
.val($field.find("option:selected").text().substr(0,10));
});
});

Get all items in NotesXSPDocument

In my Notes Database, I perform an audit when the document is saved. Pretty easy in LotusScript. I grab the original document (oDoc) from the server, then in the document I modified (mDoc), I do a Forall loop that gets the names of each item; forall item in mDoc.items. Grab the same item from oDoc, execute a function with the new item as an argument that will run down a case statement that will see if its a field we care about. if so, I update a set of list values in the document with "When", "Who", "What field", and the "New Value".
I'm doing this in a server side script. In trying this, I discovered a couple of interesting things;
currentDocument is the NotesXSPDocument that contains everything that was just changed.
currentDocument.getDocument() contains the pre-change values. It also returns a NotesDocument which has the "items" field that I can run through.
Thing is, I need something similar in the NotesXSPDocument. Is there a way in an iterative loop to grab the names and values of all items from there?
Here's the broken code. (Currently it's walking through the NotesDocument items, but those are the old values. I'd rather walk down the XSP document items)
function FInvoice_beginAudit() {
var original_doc:NotesDocument = currentDocument.getDocument();
var oItem:NotesItem;
var oItems:java.util.Vector = original_doc.getItems();
var iterator = oItems.iterator();
while (iterator.hasNext()) {
var oItem:NotesItem = iterator.next();
item = currentDocument.getItemValue(oItem.getName());
if (oItem == undefined) {
var MasterItem = ScreenAudit(doc,item,True)
if (MasterItem) { return true }
} else {
if (item.getValueString() != oItem.getValueString()) {
var MasterItem = ScreenAudit(doc,Item,True);
if (MasterItem) { return true }
}
}
}
}
You can get both versions of a document after submit - the original and the one with changed/new values:
original: var original_doc:NotesDocument = currentDocument.getDocument();
changed: var changed_doc:NotesDocument = currentDocument.getDocument(true);
This way you can compare the items for changes.
But, there is a pitfall: after assigning "changed_doc" to currentDocument.getDocument(true) the "original_doc" has the changed values too because both variables point to the same document. That's why we have to copy all items from currentDocument.getDocument() to a new temporary document first and only after get the changed values with currentDocument.getDocument(true). As an alternative you could read the original document from server like you do in LotusScript.
This is a code for detecting changed items as a starting point:
var original_doc:NotesDocument = database.createDocument();
currentDocument.getDocument().copyAllItems(original_doc, true);
var changed_doc:NotesDocument = currentDocument.getDocument(true);
var oItems:java.util.Vector = original_doc.getItems();
var iterator = oItems.iterator();
while (iterator.hasNext()) {
var oItem:NotesItem = iterator.next();
var itemName = oItem.getName();
var cItem:NotesItem = changed_doc.getFirstItem(itemName);
if (cItem.getText() !== oItem.getText()) {
print("changed: " + itemName);
}
oItem.recycle();
cItem.recycle();
}
original_doc.remove(true);
original_doc.recycle();

Kendo UI Grid select by data item

I have a Kendo UI Grid with a large datasource and paging.
I have an event that fires where I know the underlying data item that I want to select, but am unsure on how to programatically page/select this item in the grid. If the item is not on the current grid page, I cannot use datasource.view() to poke through when the data is not on the current page.
Does anyone know how I can select an item by its underlying data source object?
I've got a similar situation to where i am at #:
http://jsfiddle.net/Sbb5Z/1050/
I can get the data item with the following:
change: function (e) {
var selectedRows = this.select();
var dataItem = this.dataItem(selectedRows[0]);
}
But then I don't know how to select the same row in the other grid.
Basically in the select event of one grid, I want to go select the same item in another grid. These are not the same datasource, as they have different page setups, but it is the same underlying data array.
I have the data item in the target grid -- but I have no clue how to page/select it in the target grid.
Edit:
The best I've come up with sofar is creating a datasource with the same parameters as the original, and paging through it programatically, until I find what I am looking for. Surely there must be a better way?
I've gotten this back from Telerik, and is a little cleaner:
http://jsfiddle.net/RZwQ2/
function findDataItem(theGrid, dataItem) {
//get grid datasource
var ds = theGrid.dataSource;
var view = kendo.data.Query.process(ds.data(), {
filter: ds.filter(),
sort: ds.sort()
})
.data;
var index = -1;
// find the index of the matching dataItem
for (var x = 0; x < view.length; x++) {
if (view[x].Id == dataItem.Id) {
index = x;
break;
}
}
if (index === -1) {
return;
}
var page = Math.floor(index / theGrid.dataSource.pageSize());
var targetIndex = index - (page * theGrid.dataSource.pageSize()) + 1;
//page is 1-based index
theGrid.dataSource.page(++page);
//grid wants a html element. tr:eq(x) by itself searches in the first grid!
var row = $("#grid2").find("tr:eq(" + targetIndex + ")");
theGrid.select(row);
console.log('Found it at Page: ' + page + 'index: ' + targetIndex);
}
You need to have a common id, or field in the data that you can use to uniquely identify the object in the other dataSource, because the kendo generated UID's are not going to be the same accross two different DataSource instances.
Most generally you define the id in the Model you bound to the grid, which you can use to quickly pluck items from the datasource
change: function (e) {
var selectedRows = this.select();
var dataItem = this.dataItem(selectedRows[0]);
var otherItem = otherGrid.dataSource.get(dataItem.id) // will get
}
if you don't have a common ID field specified in the model, but do know how to find the item you can loop through the data source looking for it
var selectedRows = this.select();
var dataItem = this.dataItem(selectedRows[0]);
var data = otherGrid.dataSource.view();
var otherItem;
for ( var i = 0; i < data.length; i++ ){
if( data[i].myCommonField === dataItem.myCommonField ) {
otherItem = data[i];
break;
}
}
UPDATE:
to select the item in the other grid you need to do this:
var elements = otherGrid.items(),
element;
element = elements.filter("[data-uid='" + otherItem.uid + "']")
otherGrid.select(element) // to select just the one item
//OR
otherGrid.select( otherGrid.select().add(element) ) // to add the item to the current selection
I the fiddle you provided uses a really old version of kendo Grid where this won't work...I just realized. are you stuck on the 2011 version? I can probably get something to work at least in theory but the above will work in the newer versions
essentailly you need to match the item you have to a DOM element, in later versions you can use UID because the dom elements all get that on them "data-uid" it looks like if you at id to your model: { } def you can get the tr elements to have data-id which you can use to select the right select using jquery. I use the items()1 method which also doesn't seem to exist on the early version but you can usegrid2.table.find("tr[data-id=]")` instead I believe
Assume div id will be Grid then first we need find the kendoGrid
var grid = $("#Grid").data("kendoGrid");
then call the grid.select() to select the currently selected one
finally call the grid.dataItem() to get the selected item.
var selectedDataItem = grid.dataItem(grid.select());
To expand upon others, I have a method that takes a single (or multiple) ids to match against:
function selectItems(grid, idAr)
{
if(!idAr instanceof Array)idAr = [idAr];
var items = grid
.items()
.filter(function(i, el)
{
return idAr.indexOf(grid.dataItem(el).Id) !== -1;
});
grid.select(items);
}
* Obviously Id could be replaced by any field that is in your data item.
Use for selection:
selectItems(grid, "5");
selectItems(grid, ["6", "7"]);

marking a item "done" in localStorage

I have an application where I am adding li elements to the web page. I need to change the class name of the element to "done" inside of local storage when I mark it as "done" on the webpage. (It should say done: true). With my current code I am unintentionally making two items in local storage, one which is done: true and the other which is done: false. I'll show my code here:
function updateDone(e) {
var spanClicked = e.target;
var id = spanClicked.parentElement.id;
var done = spanClicked.parentElement.className;
spanClicked.innerHTML = " ✔ ";
spanClicked.setAttribute("class", "done");
var key = "todos" + id;
for(var i = 0; i < todos.length; i++) {
if(todos[i].id == id) {
var mark = todos[i];
mark.done = true;
console.log(mark);
spanClicked.setAttribute("class", "done");
var newKey = JSON.stringify(mark);
localStorage.setItem(key, newKey);
if(mark.done == false) {
spanClicked.setAttribute("class", "not done");
spanClicked.innerHTML = "not done";
}
}
}
}
They are both labeled with the same id which is how I keep track of each item, yet there are two of them. Also, when i refresh the page there are two list items shown, one which is marked done. My question is how do I prevent another item from being created and instead mark just one item as done in localStorage?
You need a way to uniquely identify each item, so you can ensure your marks are being set on the items you intend and are not overwriting because you might have, say, two items with the same key. Since you are looping through a list, maybe you can change your keys to be composed of two parts.
var parent_key = "todos" + parent_id;
And then, in the loop :
var store_key = parent_key + ":" + i;
...
localStorage.set(store_key,newKey);
This way (so long as the order is going to be consistent), you can separate multiple list elements from the same parent.
As commented, a live example in jsFiddle or something would help better address your requirement.
However if this solution is insufficient you could try the following idea, effectively setting a "table" within localstorage.
var parent_key = "todos" + id;
var parent_table = {};
// for loop
parent_table[i] = newKey;
// end of for loop
localStorage.set(parent_key,parent_table);
So you have a table inside of local storage, to give you finer granularity.

Categories