jqgrid - json - getting wrong value - json

I have the following JSON:
{
"wrapper": {
"table": {
"rows": [
{
"cells": [
{
"fname": "Jack",
"label": "fname",
"editable": false
},
{
"lname": "Kalra",
"label": "lname",
"editable": true,
"details": [
{
"industry": "music"
},
{
"industry": "media"
}
]
}
]
},
{
"cells": [
{
"fname": "Steven",
"editable": true,
"label": "fname"
},
{
"lname": "Martini",
"editable": true,
"label": "lname"
}
]
},
{
"cells": [
{
"fname": "Andrea",
"editable": true,
"label": "fname"
},
{
"lname": "Dmello",
"editable": true,
"label": "lname",
"details": [
{
"industry": "finance"
},
{
"industry": "HR"
},
{
"industry": "media"
}
]
}
]
},
{
"cells": [
{
"fname": "James",
"label": "fname",
"editable": false
},
{
"label": "lname",
"lname": "Diamond",
"editable": true,
"details": [
{
"industry": "music"
},
{
"industry": "media"
}
]
}
]
},
{
"cells": [
{
"fname": "Aba",
"label": "fname",
"editable": true
},
{
"label": "lanme",
"lname": "Duck",
"editable": true,
"details": [
{
"industry": "finance"
},
{
"industry": "technology"
},
{
"industry": "media"
}
]
}
]
},
{
"cells": [
{
"fname": "Henry",
"label": "fname",
"editable": true
},
{
"label": "lname",
"lname": "Hebert",
"editable": true,
"details": [
{
"industry": "finance"
},
{
"industry": "HR"
},
{
"industry": "media"
},
{
"industry": "IT"
}
]
}
]
}
]
}
}
}
All cells are editable.
I'm trying to loop through each row and then each cell to find out the number of properties in "details". In inline editing mode, it should be a text field and the value of text field should be the corresponding number of properties.
Example - for Marc Kalra, the details cell will be a text field with a value of 2.
Here's my code
loadComplete: function(data){
var x, y, cellProp;
for (x = 0; x < data.wrapper.table.rows.length; x++) {
var cellCount = data.wrapper.table.rows[x].cells.length;
for (y = 0; y < cellCount; y += 1) {
cellProp = data.wrapper.table.rows[x].cells[y];
var prop, listCount, cellLabel;
listCount = data.wrapper.table.rows[x].cells[y].details.length;
cellLabel = data.wrapper.table.rows[x].cells[y].label;
function gridCustomEdit (value, options){
var el = document.createElement("input");
el.type="text";
el.value = listCount;
return el;
}
for (prop in cellProp) { if (prop === "details"){
$("#jqgrid").setColProp(cellLabel, {
editable: true,
edittype: "custom",
editoptions: {
custom_element: gridCustomEdit
}
});
}
}
}
}
PROBLEM - is that el.value = listCount; in the above code always returns 4 as the number of properties for each row/cell.
Can someone point me my mistake?
UPDATED
loadComplete: function(data){
var x, y, cellProp;
for (x = 0; x < data.wrapper.table.rows.length; x++) {
var cellCount = data.wrapper.table.rows[x].cells.length;
for (y = 0; y < cellCount; y += 1) {
cellProp = data.wrapper.table.rows[x].cells[y];
var isEditable = cellProp.editable;
if (isEditable === false) {
$("#jqgrid").setColProp(cellProp.label, {
editable: false
});
}else {
var listCount, cellLabel;
listCount = data.wrapper.table.rows[x].cells[y].details.length;
cellLabel = data.form.summary.rows[x].cells[y].label;
$("#jqgrid").setColProp(cellLabel, {
editable: true,
editoptions: {
dataInit: function(elem){
$(elem).myPlugin({listCount: listCount})
}
}
})
}
}
}
}
PLUGIN CODE
(function( $ ){
$.fn.extend({
myPlugin: function(options){
var defaults = {
listCount: 0
};
var options = $.extend(defaults, options);
var o = options;
alert(o.listCount);
if (o.listCount === 3){
$(elem).html("<input type='text' value='" + o.listCount + "'>")
} else {
$(elem).html("<select>") **// this should be a dropdown with values = properties of `details`**
}
}
})
})
GRID CODE
$("#jqgrid").jqGrid({
datatype: "json",
colNames:['fname','lname'],
colModel:[
{name:'fname',index:'fname'},
{name:'lname',index:'lname'},
],
jsonReader: {
root: "wrapper.table.rows",
repeatitems: false
},
onSelectRow: function(id){
$(this).jqGrid('editRow',id);
},
})
If details exist + number of properties in details = 3, then lname is displayed as a text field in inline editing mode.
If details exist + number of properties in details > 3, then lname is displayed as a select field in inline editing mode.

The method setColProp set property for the column and not for the cell in the column. So if you set in the loop setColProp for the same column many times only the last setting will be applied. Because the last row (for "Love Hebert") in your data has 4 items in the details array only the value will be used.
The next error is that you define gridCustomEdit function which has reference to external variable listCount. In the case there are only one instance of the function with the last value of the variable. If you need to have many function instances with different values you should use closure.
In your case it seems to me you can implement all what you need even without the usage of closure and without custom editing (edittype:'custom' with custom_element and custom_value).
I suppose that all what you need to do is to set setColProp inside of onSelectRow (or before the call of editRow) and not inside of loadComplete. See the answer for more information. If you need to change the edittype of the column you can do this in the same way. So you can for example dynamically set edittype: "text" and set editoptions with dataInit which change the value of the element.
If it is needed you can even dynamically switch the edittype between edittype: "text" and edittype: "select" and set all editoptions which are required.
In the way you will receive simple and flexible code which don't use custom editing at all.

Related

How to configure everything into one or multiple configure files

I haven't used this since 2 years ago. I'm trying to figure out how to put everything into one json or it has to be mulitple jsons.
Using v11.9.46. The documentation is not clear as it references things that are for one version or another.
I'm moving items from ADOorg1 to ADOorg2. In this order I was going to setup the json
Areas and Iterations
Team setup
Shared Queries
Work Items (using a query to find select area)
This is what I had so far as I tried to figure out how to put it into one:
{
"ChangeSetMappingFile": null,
// "Endpoints": {
// "TfsTeamSettingsEndpoints": [
// {
// "Name": "TeamSettingsSource",
// "AccessToken": "",
// "Query": {
// "Query": "SELECT [System.Id], [System.Tags] FROM WorkItems WHERE [System.TeamProject] = #TeamProject AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan') ORDER BY [System.ChangedDate] desc"
// },
// "Organisation": "https://dev.azure.com/test1/",
// "Project": "test1",
// "ReflectedWorkItemIdField": "ReflectedWorkItemId",
// "AuthenticationMode": "AccessToken",
// "AllowCrossProjectLinking": false,
// "LanguageMaps": {
// "AreaPath": "Area",
// "IterationPath": "Iteration"
// }
// },
// {
// "Name": "TeamSettingsTarget",
// "AccessToken": "",
// "Query": {
// "Query": "SELECT [System.Id], [System.Tags] FROM WorkItems WHERE [System.TeamProject] = #TeamProject AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan') ORDER BY [System.ChangedDate] desc"
// },
// "Organisation": "https://dev.azure.com/test2/",
// "Project": "test2",
// "ReflectedWorkItemIdField": "ReflectedWorkItemId",
// "AuthenticationMode": "AccessToken",
// "AllowCrossProjectLinking": false,
// "LanguageMaps": {
// "AreaPath": "Area",
// "IterationPath": "Iteration"
// }
// }
// ]
// },
"Source": {
"$type": "TfsTeamProjectConfig",
"Collection": "https://dev.azure.com/test1/",
"Project": "test1",
"ReflectedWorkItemIDFieldName": "ReflectedWorkItemId",
"AllowCrossProjectLinking": false,
"AuthenticationMode": "Prompt",
"PersonalAccessToken": "",
"LanguageMaps": {
"AreaPath": "Area",
"IterationPath": "Iteration"
}
},
"Target": {
"$type": "TfsTeamProjectConfig",
"Collection": "https://dev.azure.com/test2/",
"Project": "Test2",
"ReflectedWorkItemIDFieldName": "ReflectedWorkItemId",
"AllowCrossProjectLinking": false,
"AuthenticationMode": "Prompt",
"PersonalAccessToken": "",
"LanguageMaps": {
"AreaPath": "Area",
"IterationPath": "Iteration"
}
},
"Endpoints": {
"InMemoryWorkItemEndpoints": [
{
"Name": "Source",
"EndpointEnrichers": null
},
{
"Name": "Target",
"EndpointEnrichers": null
}
]
},
"GitRepoMapping": null,
"LogLevel": "Information",
"Processors": [
{
"$type": "TfsAreaAndIterationProcessorOptions",
"Enabled": true,
"PrefixProjectToNodes": false,
"NodeBasePaths": null,
"ProcessorEnrichers": null,
"SourceName": "Source",
"TargetName": "Target"
},
{
"$type": "TfsTeamSettingsProcessorOptions",
"Enabled": false,
"MigrateTeamSettings": true,
"UpdateTeamSettings": true,
"PrefixProjectToNodes": false,
"Teams": null,
"ProcessorEnrichers": null,
"SourceName": "Source",
"TargetName": "Target"
},
{
"$type": "WorkItemMigrationConfig",
"Enabled": false,
"ReplayRevisions": true,
"PrefixProjectToNodes": false,
"UpdateCreatedDate": true,
"UpdateCreatedBy": true,
"BuildFieldTable": false,
"AppendMigrationToolSignatureFooter": false,
"WIQLQueryBit": "AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')",
"WIQLOrderBit": "[System.ChangedDate] desc",
"LinkMigration": true,
"AttachmentMigration": true,
"AttachmentWorkingPath": "E:\\temp\\WorkItemAttachmentWorkingFolder\\",
"FixHtmlAttachmentLinks": false,
"SkipToFinalRevisedWorkItemType": true,
"WorkItemCreateRetryLimit": 5,
"FilterWorkItemsThatAlreadyExistInTarget": true,
"PauseAfterEachWorkItem": false,
"AttachmentMaxSize": 480000000,
"CollapseRevisions": false,
"LinkMigrationSaveEachAsAdded": false,
"GenerateMigrationComment": true,
"NodeBasePaths": [
"Product\\Area\\Path1",
"Product\\Area\\Path2"
],
"WorkItemIDs": null
}
],
"Version": "0.0",
"workaroundForQuerySOAPBugEnabled": false,
"WorkItemTypeDefinition": {
"sourceWorkItemTypeName": "targetWorkItemTypeName"
}
// "Endpoints": {
// "InMemoryWorkItemEndpoints": [
// {
// "Name": "Source",
// "EndpointEnrichers": null
// },
// {
// "Name": "Target",
// "EndpointEnrichers": null
// }
// ]
// }
}
If you use this Azure DevOps Migration Tools, it can migrate Work Items, TestPlans & Suits, Teams, Shared Queries, & Pipelines from one Organization to another. You could refer to Processors: TfsAreaAndIterationProcessor, TfsTeamSettingsProcessor, TfsSharedQueryProcessor and Work Item Tracking Processor to create corresponding configuration Json files one by one.

How to fetch a particular value from JSON if it exist

I have a json and want to fetch some values if it exists or not null.
From below JSON, I want to extract "options" of "questionGroup". Challenge is at some places "questionGroup" is empty.
"surveyQuestions": [
{
"questionTitle": "Enter your name",
"questionType": "Text",
"questionGroup": {}
},
{
"questionTitle": "Enter your age",
"questionType": "Number",
"questionGroup": {}
},
{
"questionTitle": "Select your gender",
"questionType": "Single choice",
"questionGroup": {
"options": [
{
"optionText": "Male"
},
{
"optionText": "Female"
}
],
"showRemarksBox": false
}
}
]
Just check whether the options are undefined or not.
// Iterate over survey questions
this.surveyQuestions.forEach( question => {
// Check if present or not
if(question.questionGroup.options!=undefined){
console.log(question.questionGroup.options);
return question.questionGroup.options;
}
});
You can use filter method for this.
let data = {"surveyQuestions": [
{
"questionTitle": "Enter your name",
"questionType": "Text",
"questionGroup": {}
},
{
"questionTitle": "Enter your age",
"questionType": "Number",
"questionGroup": {}
},
{
"questionTitle": "Select your gender",
"questionType": "Single choice",
"questionGroup": {
"options": [
{
"optionText": "Male"
},
{
"optionText": "Female"
}
],
"showRemarksBox": false
}
}
]}
let result = Array.from(data.surveyQuestions.filter(o => o.questionGroup.options), ({questionGroup}) => questionGroup.options);
console.log(result);
surveyQuestions.filter(s => s.questionGroup && Object.keys(s.questionGroup).length)

Set next step for the waterfall dialogue in Microsoft BotBuilder NodeJS SDK

I am using Microsoft Bot Framework for my facebook messenger bot. I want to load the dialog data from json files instead of hard coding in the js file. I would like to configure the next step in the dialog, based on result from the "current" step, which is part of the json file configuration, something like this.
{
"name": "welcome",
"type": "waterfall",
"steps": [
{
"id": 0,
"data": [
{
"type": "text",
"value": "Hey, It's nice to meet you."
},
{
"type": "quickReplies",
"value": "What do you want to do next?",
"options": [
{
"text": "some option 1",
"value": "option1"
},
{
"text": "some option 2",
"value": "option2"
}
]
}
],
"next": [
{
"result": "option1",
"action": "goto step 2"
},
{
"result": "option2",
"action": "goto step 5"
}
]
}
]
}
I would like to process all the incoming messages and respond with correct dialog or correct step in the dialog for the user.
I am trying something like this;
handleMessage = function (session) {
var step = session.dialogData["BotBuilder.Data.WaterfallStep"] || 0;
// check response data from previou step and identify the next step.
// set the waterfall step id
session.dialogData["BotBuilder.Data.WaterfallStep"] = 2;
session.send("Hello");
}
var bot = new builder.UniversalBot(connector, function (session) {
handleMessage(session);
})
.set('storage',tableStorage);
With this code, I am always getting step as zero for session.dialogData["BotBuilder.Data.WaterfallStep"] even after setting this to a different number.
Also, as soon as I set the waterfall step number, all other state data that is stored in my table storage for this conversation is gone.
Storage data before setting waterfall step:
{
"BotBuilder.Data.SessionState": {
"callstack": [
{
"id": "*:/",
"state": {
"BotBuilder.Data.WaterfallStep": 0
}
},
{
"id": "*:welcome",
"state": {
"BotBuilder.Data.WaterfallStep": 1
}
},
{
"id": "BotBuilder:prompt-text",
"state": {
"options": {
"prompt": {
"type": "message",
"agent": "botbuilder",
"source": "facebook",
"address": {
"id": "mid.$cAAAlr-0LRH9niO21L1hV6hs83GuJ",
"channelId": "facebook",
"user": {
"id": "XXXX",
"name": "XXXX"
},
"conversation": {
"isGroup": false,
"id": "XX"
},
"bot": {
"id": "XXX",
"name": "XXX"
},
"serviceUrl": "https://facebook.botframework.com"
},
"text": "what do you want to next"
//ignored for simplicity
},
"promptAfterAction": true,
"libraryNamespace": "*"
},
"turns": 0,
"lastTurn": 1517594116372,
"isReprompt": false
}
}
],
"lastAccess": 1517594112740,
"version": 0
}
}
After I set the waterfall step:
{
"BotBuilder.Data.SessionState": {
"callstack": [
{
"id": "*:/",
"state": {
"BotBuilder.Data.WaterfallStep": 2
}
}
],
"lastAccess": 1517602122416,
"version": 0
}
}
Interestingly the step number is saved to the database (but in session state) but my "session" variable do not have this value anywhere. Also, even after configuring custom state service, the serviceUrl is still https://facebook.botframework.com which I thought is the default state service used if there is no state service set for the bot.
Per your code, as your bot actually contains only one waterfall step: handleMessage(session);, which raised your issue. You can consider to create multiple dialogs from json configration instead of complex waterfall steps.
Here is my quick test, for your information:
const json = `
[{
"name": "welcome",
"type": "waterfall",
"steps": [
{
"id": 0,
"data": [
{
"type": "text",
"value": "Hey, It's nice to meet you."
},
{
"type": "quickReplies",
"value": "What do you want to do next?",
"options": [
{
"text": "some option 1",
"value": "option1"
},
{
"text": "some option 2",
"value": "option2"
}
]
}
],
"next": [
{
"result": "option1",
"action": "dialog2"
},
{
"result": "option2",
"action": "dialog3"
}
]
}
]
},{
"name":"dialog2",
"type": "waterfall",
"steps": [
{
"data": [
{
"type": "text",
"value": "Hey, this is dialig2."
}]
}
]
},{
"name":"dialog3",
"type": "waterfall",
"steps": [
{
"data": [
{
"type": "text",
"value": "Hey, this is dialig3."
}]
}
]
}]
`;
const generateSignleStep = (step) => {
return (session, args, next) => {
step.forEach(sentence => {
switch (sentence.type) {
case 'quickReplies':
let choices = sentence.options.map(item => {
return item.value
});
let card = new builder.ThumbnailCard(session)
.text(sentence.value)
.buttons(sentence.options.map(choice => new builder.CardAction.imBack(session, choice.value, choice.text)))
let message = new builder.Message(session).addAttachment(card);
builder.Prompts.choice(session, message, choices);
break;
case 'text':
default:
session.send(sentence.value)
break;
}
})
}
}
const generatenextAction = (actions) => {
return (session, args, next) => {
const response = args.response;
actions.map(action => {
if (action.result == response.entity) {
session.beginDialog(action.action);
}
})
}
}
const generateWaterfallSteps = (steps) => {
let waterfall = [];
steps.forEach(step => {
waterfall.push(generateSignleStep(step.data));
if (step.next) {
waterfall.push(generatenextAction(step.next));
}
});
return waterfall;
}
var bot = new builder.UniversalBot(connector);
const jsonobj = JSON.parse(json);
jsonobj.forEach(dialog => {
bot.dialog(dialog.name, generateWaterfallSteps(dialog.steps))
.triggerAction({
matches: new RegExp(dialog.name, "g")
})
});
The result is:

Any smart JSON/JS Object counting packages?

I don't mean to be duplicating any questions, since I have read so many great answers here on StackOverflow.
Given the below JSON data:
[
{
“department”: “vice”,
“team”: [
{
"selected": "Yes"
},
{
“selected": “No”
}
],
“fund”: [
“team_a”
“team_c”
]
}
]
I want to return a count of selected, so from the above 'yes'=1 and 'no'=1.
I understand I can do it through for loop, using a simple countYes++ to return the answer, however I have 2 questions as below:
Is there any other way (i.e. using some npm package).
In my example the options are simple Yes and No, if I have a bigger list of things to count, how to count all unique ones > 0?
Thanks.
I tried to write using vanilla JS you can look for library like underscore, lodash for more options.
var data = [
{
"department": "vice",
"team": [
{
"selected": "Yes"
},
{
"selected": "No"
}
],
"fund": [
"team_a",
"team_c"
]
},
{
"department": "vice",
"team": [
{
"selected": "Yes"
},
{
"selected": "Yes"
}
],
"fund": [
"team_a",
"team_c"
]
},
{
"department": "vice",
"team": [
{
"selected": "Yes"
},
{
"selected": "No"
}
],
"fund": [
"team_a",
"team_c"
]
}
];
function decisionReducer(prev, next) {
var team = next.team;
if (team && Array.isArray(team)) {
team.forEach(function (row) {
if (row.selected === 'Yes') {
prev.yes += 1;
}
if (row.selected === 'No') {
prev.no += 1;
}
})
}
return prev;
};
var counter = data.reduce(decisionReducer, {yes: 0, no: 0})
console.log(counter);

Bootstrap table using JSON loading empty values

I'm using the bootstrap table plugin and can't seem to get it to load any values into the table. I've used it before with success, but can't figure out what I'm doing wrong here. The amount of rows is coming through, so I know the JSON data is being read by the bootstrap table, its just that the values are all empty in the table. I think the JSON is in the proper format, and the field names are all consistent. Any ideas?
Javacript
function populate_quote_table()
{
$('#quote_table').bootstrapTable({
onSearch: function (text) {
},
onLoadSuccess: function (data) {
console.log(data);
//$('#test').html(data);
},
url: "<?php echo $site_url?>/quotes.php",
striped: true,
search: true,
showRefresh:true,
showColumns:true,
pagination: true,
showFilter: true,
columns: [{
field: 'QID',
title: 'QID',
}, {
field: 'CID',
title: 'Customer ID',
}, {
field: 'Distance',
title: 'Distance',
}]
});
};
});
PHP
$i = 0;
$quotes[] = array();
if ($result) {
while($row = mysql_fetch_array($result)) {
$quotes[$i]['"QID"'] = $row['quote_id'];
$quotes[$i]['"CID"'] = $row['customer_id'];
$quotes[$i]['"Distance"'] = $row['distance'];
$i++;
}
}
echo json_encode($quotes);
JSON Output
[
{
"QID": "1",
"CID": "1",
"Distance": "1"
},
{
"QID": "2",
"CID": "2",
"Distance": "11"
},
{
"QID": "3",
"CID": "20",
"Distance": "5"
},
{
"QID": "4",
"CID": "21",
"Distance": "67"
}
]
Extra quotes were not needed
Original -
$quotes[$i]['"QID"'] = $row['quote_id'];
Working -
$quotes[$i]['QID'] = $row['quote_id'];