Handsontable Replace autocomplete values with key before posting - javascript

I am using HandsOnTable to make editing database tables more interactive on my site.
HandsOnTable fulfils nearly all my requirements except that some columns in my database actually store foreign keys rather than local string values.
In the UI I would like these columns to appear as dropdown menus where the user selects a readable value mapped to the previously mentioned foreign key (I.e. something like an HTML name/value select).
Unfortunately HandsOnTable does not have such a cell type. The closest thing to it is autocomplete. This allows me to create a dropdown, but it only contains values; no corresponding keys. Here is how it is created:
"source": ["Jebediah", "Bob", "Bill", "Buzz"]
So what I am planning is to send two Json strings from the server:
One containing the parameters needed by HandsOnTable to render the table:
{
"data": [
{ "ID": 1, "Description": "Crude", "Volume": 204, "Customer": "jebediah" },
{ "ID": 2, "Description": "Hidrogen", "Volume": 513, "Customer": "Bob" },
{ "ID": 3, "Description": "Coal", "Volume": '67', "Customer": "Bill" },
{ "ID": 4, "Description": "Wood", "Volume": '513', "Customer": "Buzz" }
],
"columns": [
{ "data": "ID", "type": "numeric" },
{ "data": "Description", "type": "text"},
{ "data: "Volume", "type": "numeric" },
{ "data": "color", "type": "autocomplete", "strict": "true",
"source": ["Jebediah", "Bob", "Bill", "Buzz"]}
]
}
The second mapping keys to values
{
"mappings": [
{"key": 0, "value": "Jebediah"},
{"key": 0, "value": "Bob"},
{"key": 0, "value": "Bill"},
{"key": 0, "value": "Buzz"}
]
}
So far so good. Now for the tricky part:
HandsOnTable has a function (getData()) that allows me to retrieve the tables data as a Json string ready to be sent back to the server:
var jdata = myHandsOnTable.getData();
Where jdata would look something like this:
"data": [
{ "ID": 1, "Description": "Crude", "Volume": 204, "Customer": "jebediah" },
{ "ID": 2, "Description": "Hidrogen", "Volume": 513, "Customer": "Bob" },
{ "ID": 3, "Description": "Coal", "Volume": '67', "Customer": "Bill" },
{ "ID": 4, "Description": "Wood", "Volume": '513', "Customer": "Buzz" }
]
Now before posting, I would like to replace that values for the Customer node with their matching pair key within the mappings json string.
How can I best achieve this in JavaScript/JQuery?
Is there a function that works something as follows?:
jdata.replaceNode('node', mappings)
Thanks

I had a similar issue and here's what I did...
For each foreign key column, I stored 2 values in handsontable; one for the id itself, which I set as a hidden column and the other is the user friendly readable text value as dropdowns.
Everytime the value of a dropdown is changed, I also change the corresponding hidden id. In my case I have a dropdown outside the handsontable as a filter which I use to map key/value pairs, but you could use Hashtables or anything else.
Now the code...
Handsontable config:
afterChange: function (changes, source) { AfterChange(changes, source); }
After change event (called everytime there is a change in the table):
function AfterChange(Changes, Source) {
if (Source === 'loadData') {
return; //don't save this change
}
var rowIndex = 0, columnID = 1, oldTextVal = 2, newTextVal = 3, ntv = '', nv = '';
$(Changes).each(function () {
if (this[columnID] === 'CategoryID') {
// Do nothing...
//To make sure nothing else happens when this column is set through below
}
else if (this[columnID] === 'CategoryName') {
ntv = this[newTextVal];
//This is where I do my mapping using a dropdown.
nv = $('#CategoriesFilterDropdown option').filter(function () { return $(this).text() === ntv; }).val();
//13 is my CategoryID column
$container.handsontable('setDataAtCell', this[rowIndex], 13, nv);
}
});
}
}
This way, you change the foreign keys as you and don't need to loop through it all before saving. It also makes it easy to send the table data as is back to server.
In summary,
The user interacts with CategoryName column (which is of type autocomplete).
The CatgoryID column is hidden to the user by setting the column width to 0 using the colWidths option of handsontable.
When the CategoryName field changes, use afterChange event to set the corresponding CategoryID column. In my case, I use a dropdown somewhere else on the page to map Name => ID, but you can use other means such as a hashtable.
I hope it makes sense...

Related

How to generate dynamically two arrays at same time with Formik

I working in a form who generate fields dynamically using formik with the <FieldArray />that acts as a functional component.
So, I have this object to send on submit to my backend:
{
"tapes": [
{
"name": "",
"elements": [
{
"propsOne": "",
"propsTwo": "",
"view": 0,
}
]
}
],
"typeOptions": []
}
As you can see, I have two arrays to working in this json object, one is Tapes who has Elements to work together and typeOptions who has values to send. My proposal is to send in a form with dynamic fields to my backend when whenever the user add new tapes and add new field props which will be rendered later in a mobile app.
Assuming the user added two new tapes and added two new elements for each one, the result of this json would be this:
{
"tapes": [
{
"name": "TapeOne",
"elements": [
{
"propsOne": one,
"propsTwo": two,
"view": 0,
},
{
"propsOne": 1,
"propsTwo": 2,
"view": 0,
}
]
},
{
"name": "TapeTwo",
"elements": [
{
"propsOne": one,
"propsTwo": two,
"view": 0,
},
{
"propsOne": 1,
"propsTwo": 2,
"view": 0,
}
]
}
],
"typeOptions": []
}
Well, so far so good, I can form a dynamic generation of Tapes and Elements according to the user's request, but there is a moment when the user asks for an option selector and the value I get in Elements is in the View, where number by the value of 1 is required.
{
"tapes": [
{
"name": "",
"elements": [
{
"propsOne": "",
"propsTwo": "",
"view": 1,
}
]
}
],
"typeOptions": [
{
"values": []
}
]
}
It is at this point that I need to retrieve the values that it passes on in these fields that will be dynamically generated and insert them into a new array that is called typeOptions in its values, the flow being like this:
The user will ask for a field who has multiple fields, like a selector.
This field will be generated in Tapes, Elements with the value being assigned to view with 1
The user will pass the values in these fields and they will be placed in typeOptions.values
So, the user add this new field and when this happens, I need pass the values that will be passed on fields will be generated for him.
The result needs be like this:
{
"tapes": [
{
"name": "",
"elements": [
{
"propsOne": "One",
"propsTwo": "Two",
"view": 1,
}
]
}
],
"typeOptions": [
{
"values": ["ValueOne", "ValueTwo", "ValueThree"]
}
]
}
Or when two Elements has the same view like 1:
{
"tapes": [
{
"name": "",
"elements": [
{
"propsOne": "One",
"propsTwo": "Two",
"view": 1,
},
{
"propsOne": "1",
"propsTwo": "2",
"view": 1,
}
]
}
],
"typeOptions": [
{
"values": ["ValueOne", "ValueTwo", "ValueThree"]
},
{
"values": ["Value1", "Value2", "Value3"]
}
]
}
I've already tried to use two <FieldArray /> having one inside the other, as I already do for Tapes and Elements, because with this I could use the map function to get the view value and add a conditional to permeate two new <FieldArray /> to add the values not only for each object selected inside the typeOptions, but also for each value that is inserted together.
What is the best way and best practice to do this?

Matching Object with 2 arrays with same key but different values from JSON API and compare to state values and return that Object

I've been trying for days to figure out the best approach and also how to make it work. I'm building a product page and each variation options is built dynamically (size, color) based on the GraphQL response it gets back. I'm used to simpler array checking but this one has me baffled and at a lost for even where to start.
So when a user clicks on "M" and "Red" it takes those values and stores them into state
// State Setup
const [variations, setVariations] = useState({});
// Set Variations State that was selected
function productVariationsHandler(event) {
setVariations({ ...variations, [event.target.name]: event.target.value });
}
// State Object after user has made their selections
{
"variations": {
"color": "Red",
"size": "XL"
}
}
I'm trying to take those user selected state variables ("Red", "XL") and match them to the GraphQL response in the variations object to get the databaseId and regularPrice
// GraphQL Response
{
"variations": {
"nodes": [
{
"attributes": {
"nodes": [
{
"attributeId": 237,
"name": "pa_color",
"value": "True Royal"
},
{
"attributeId": 220,
"name": "pa_size",
"value": "S"
}
]
},
"databaseId": 1846,
"regularPrice": "$24.95",
"salePrice": null,
"status": "publish"
},
{
"attributes": {
"nodes": [
{
"attributeId": 237,
"name": "pa_color",
"value": "Red"
},
{
"attributeId": 221,
"name": "pa_size",
"value": "XL"
}
]
},
"databaseId": 1847,
"regularPrice": "$24.95",
"salePrice": null,
"status": "publish"
}
]
}
}
I've tried using the find method and as well as map and even tried for as seen below but without avail
// My Failed Attempt
// Find the Variation based on user selected options
function findVariationProduct() {
for (const key in variations) {
product.variations.nodes.map((variation) => {
const variationProduct = variation.attributes.nodes.find(
({ value, name }) => {
if (name == `pa_${key}` && value == variations[key]) {
console.log(variationProduct);
}
}
);
});
}
}
Any help would be greatly appreciated!

How can I extract nested value in JSON and initialize to a JS variable

my question is above and I am trying like for 45mins to extract the value under "distance" below, but I fail at every try. I hope you guys can help me.
{
"destination_addresses": [
"XXXXXXXX 60, 13XXX Berlin, Germany"
],
"origin_addresses": [
"XXXXXXX Str. 67, 10XXX Berlin, Germany"
],
"rows": [
{
"elements": [
{
"distance": {
"text": "10.4 km",
"value": 10365
},
"duration": {
"text": "21 min",
"value": 1278
},
"status": "OK"
}
]
}
],
"status": "OK"
}
So I need the value under rows --> elements --> distance then value. I tried something like this in JavaScript:
var payload = JSON.parse(body)
console.log(payload.rows["elements"].distance.value)
Thanks! :)
Süleyman Demir
let distance = payload.rows[0].elements[0].distance.value
console.log(payload)
console.log(distance)
Please note that the data is a mix of nested arrays and objects, which are different data structures in javascript. You can access an object's property by typing its name followed by a dot and the name of the property (object_name.property_name). You can access an array's element by typing the element index in square brackets next to the array's name (array_name[element_number]).
In our case we access the property "rows" which is an array of the object "payload" - payload.rows. Then we access the element number [0] of this array by typing [0] next to the property's name - payload.rows[0]. We get another object which has the property "elements" in it - payload.rows[0].elements . This property stores another array and we access its first element again -
payload.rows[0].elements[0]. We get another object and access the property "distance" which returns finally return another object that holds the property "value" we are looking for - payload.rows[0].elements[0].distance.value
Source https://eloquentjavascript.net/04_data.html
Your question was not clear, I assume that you will have multiple rows and multiple elements. There is my solution according to what I understand.
payload.rows.forEach(x=> x.elements.forEach(y => console.log(y.distance.value)))
Try like below
var body = {
"destination_addresses": ["XXXXXXXX 60, 13XXX Berlin, Germany"],
"origin_addresses": ["XXXXXXX Str. 67, 10XXX Berlin, Germany"],
"rows": [{
"elements": [{
"distance": {
"text": "10.4 km",
"value": 10365
},
"duration": {
"text": "21 min",
"value": 1278
},
"status": "OK"
}]
}],
"status": "OK"
};
var payload = JSON.parse(JSON.stringify(body));
payload.rows.forEach(row => row.elements.forEach(elem => console.log("Distance : ", elem.distance.value)))
I don't know exactly what you want, but you can get the distance object with something like that:
const payload = {
"destination_addresses": [
"XXXXXXXX 60, 13XXX Berlin, Germany"
],
"origin_addresses": [
"XXXXXXX Str. 67, 10XXX Berlin, Germany"
],
"rows": [
{
"elements": [
{
"distance": {
"text": "10.4 km",
"value": 10365
},
"duration": {
"text": "21 min",
"value": 1278
},
"status": "OK"
}
]
}
],
"status": "OK"
}
let distance = payload.rows[0].elements.map(element => {
return {
distance: element.distance
}
});
// Map returns an array, so you can get the object using the index:
console.log(distance[0]);
// If you want only the value:
console.log(distance[0].distance.value);
If you want, you can also use Object.assign or something like that to avoid getting the value by the index.
Hope it helped!
var payload = JSON.parse(body);
console.log(payload.rows[0]["elements"][0].distance.value);

How to get specific array from JSON object with Javascript?

I am working with facebook JS SDK which returns user's information in JSON format. I know how to get the response like response.email which returns email address. But how to get an element from a nested array object? Example: user's education history may contain multiple arrays and each array will have an element such as "name" of "school". I want to get the element from the last array of an object.
This is a sample JSON I got:-
"education": [
{
"school": {
"id": "162285817180560",
"name": "Jhenaidah** School"
},
"type": "H**hool",
"year": {
"id": "14404**5610606",
"name": "2011"
},
"id": "855**14449421"
},
{
"concentration": [
{
"id": "15158**968",
"name": "Sof**ering"
},
{
"id": "20179020**7859",
"name": "Dig**ty"
}
],
"school": {
"id": "10827**27428",
"name": "Univer**g"
},
"type": "College",
"id": "9885**826013"
},
{
"concentration": [
{
"id": "108196**810",
"name": "Science"
}
],
"school": {
"id": "2772**996993",
"name": "some COLLEGE NAME I WANT TO GET"
},
"type": "College",
"year": {
"id": "1388*****",
"name": "2013"
},
"id": "8811215**16"
}]
Let's say I want to get "name": "some COLLEGE NAME I WANT TO GET" from the last array. How to do that with Javascript? I hope I could explain my problem. Thank you
Here is a JsFiddle Example
var json = '{}' // your data;
// convert to javascript object:
var obj = JSON.parse(json);
// get last item in array:
var last = obj.education[obj.education.length - 1].school.name;
// result: some COLLEGE NAME I WANT TO GET
If your json above was saved to an object called json, you could access the school name "some COLLEGE NAME I WANT TO GET" with the following:
json.education[2].school.name
If you know where that element is, then you can just select it as already mentioned by calling
var obj = FACEBOOK_ACTION;
obj.education[2].school.name
If you want to select specifically the last element, then use something like this:
obj.education[ obj.education.length - 1 ].scool.name
Try this,
if (myData.hasOwnProperty('merchant_id')) {
// do something here
}
where JSON myData is:
{
amount: "10.00",
email: "someone#example.com",
merchant_id: "123",
mobile_no: "9874563210",
order_id: "123456",
passkey: "1234"
}
This is a simple example for your understanding. In your scenario of nested objects, loop over your JSON data and use hasOwnProperty to check if key name exists.

Dropdown in kendo UI grid

I need a drop down for a Kendo-UI grid, and came across this example:
http://codepen.io/jordanilchev/pen/cnkih?editors=001
But in this example, both the key and the display text for the drop down are included in the data source of the grid as well, which seems very redundant. I looked at a similar example on Telerik's site, and it was the same there.
Here is the data source for the Type drop-down:
var types = [
{
"Type": "FB",
"Name": "Facebook"
},
{
"Type": "TW",
"Name": "Twitter"
},
{
"Type": "YT",
"Name": "YouTube"
},
{
"Type": "PI",
"Name": "Pinterest"
}
];
So far so good. But here is the data for the actual grid - notice how it also contains both Type and Name for every record:
var products = [{
"ProductID": 1,
"ProductName": "Chai",
"Type": {
"Type": "FB",
"Name": "Facebook"
}
}, {
"ProductID": 2,
"ProductName": "Chang",
"Type": {
"Type": "FB",
"Name": "Facebook"
}
}...
What I had expected is that only the Type would have to be in the data source of the grid - like this:
var products = [{
"ProductID": 1,
"ProductName": "Chai",
"Type": "FB",
}, {
"ProductID": 2,
"ProductName": "Chang",
"Type": "FB",
}...
Is there a way to use a drop-down in the Kendo UI grid without having to include both the key and the display text for every record in the data source of the grid? In other words, the grid would know to reference the datasource of the drop-down to get the display text for the cell.
Update 9/23/2014:
The solution proposed by CodingWithSpike works fine when the datasource for the drop down is a hard-coded / local array, but I am having difficulties getting it to work when loading the data for the drop down from a server. The issue seems to be that the grid gets initialized before the data source for the drop down has been read.
To "simulate" an $http call to populate the data source, I use a setTimeout:
$(document).ready(function () {
var categories = [];
setTimeout(function() {
categories = [{
"value": 1,
"text": "Beverages"
},{
"value": 2,
"text": "Condiments"
},{
"value": 3,
"text": "Confections"
}];
$('#grid').data('kendoGrid').dataSource.read(); // Just as a test, but not even this helps
$('#grid').data('kendoGrid').refresh(); // Just as a test, but not even this helps
}, 1000);
When the data is loaded as above (or via $http), the drop down fields now contain the value (id) instead of the text. Here is a plunker that shows this:
http://plnkr.co/edit/DWaaHGVAS6YuDcqTXPL8?p=preview
Note that the real app is an AngularJs app, and I would rather not use some jQuery hack to wait until the drop down data is available and then create the grid element.
How do I get this working with data from a server?
Take a look at the Kendo demo for "Foreign Key" columns. I think it is exactly what you want.
http://demos.telerik.com/kendo-ui/grid/foreignkeycolumn
They use a list of categories:
var categories = [{
"value": 1,
"text": "Beverages"
},{
"value": 2,
"text": "Condiments"
},{
"value": 3,
"text": "Confections"
},{
"value": 4,
"text": "Dairy Products"
},{
"value": 5,
"text": "Grains/Cereals"
},{
"value": 6,
"text": "Meat/Poultry"
},{
"value": 7,
"text": "Produce"
},{
"value": 8,
"text": "Seafood"
}];
The demo is a little deceiving because their data for the grid contains the entire "Category":
var products = [{
ProductID : 1,
ProductName : "Chai",
SupplierID : 1,
CategoryID : 1,
QuantityPerUnit : "10 boxes x 20 bags",
UnitPrice : 18.0000,
UnitsInStock : 39,
UnitsOnOrder : 0,
ReorderLevel : 10,
Discontinued : false,
Category : {
CategoryID : 1,
CategoryName : "Beverages",
Description : "Soft drinks, coffees, teas, beers, and ales"
}
}
However, if you look at the column definition:
{ field: "CategoryID", width: "200px", values: categories, title: "Category" },
The specified field is CategoryID not Category so the grid data item actually doesn't need to specify a "Category" property at all, and could just be:
var products = [{
ProductID : 1,
ProductName : "Chai",
SupplierID : 1,
CategoryID : 1, // <-- this is the important part!
QuantityPerUnit : "10 boxes x 20 bags",
UnitPrice : 18.0000,
UnitsInStock : 39,
UnitsOnOrder : 0,
ReorderLevel : 10,
Discontinued : false
}
I suspect the "Category" was in there just because this JSON file is shared by a few examples, so a different one may have needed it.
Update
Regarding the issue of the Grid loading before the "Category" (or whatever) FK table:
Use a deferred or the callback on the grid datasource to wait until the FK datasource is done loading before populating the grid data. Alternatively, you can init the grid, but set it to autoBind: false so that it doesn't actually read from its DataSource immediately.
Something like this (sorry for any errors, typing this off the top of my head):
(function () {
// for the foreign keys
var categoriesDataSource = new kendo.data.DataSource({
transport: {
read: {
url: "http://somewhere.com/categories"
}
}
});
// for the actual grid data
var gridDataSource = new kendo.data.DataSource({
...
});
// init the grid widget
var gridWidget = $("#grid").kendoGrid({
dataSource: gridDataSource,
autoBind: false, // <-- don't read the DataSource. We will read it ourselves.
columns: [ ... ]
});
// now we can read the FK table data.
// when that completes, read the grid data.
categoriesDataSource.fetch(function () {
gridDataSource.fetch();
});
});
I asked Telerik about this, and here is the solution they gave.
When the drop-down's data is available, use setOptions on the grid, like this (again, I use setTimeout instead of an Ajax call here, for simplicity):
setTimeout(function() {
categories = [{
"value": 1,
"text": "Beverages"
},{
"value": 2,
"text": "Condiments"
},{
"value": 3,
"text": "Confections"
}];
var grid = $('#grid').data('kendoGrid');
var cols = grid.columns;
cols[1].values = categories;
grid.setOptions({columns: cols});
$('#grid').data('kendoGrid').refresh();
}, 200);
Also, autoBind: false is not needed.
Here is an updated plunker:
http://plnkr.co/edit/ZjuK9wk3Zq80yA0LIIWg?p=preview

Categories