Xpages: Getting a sessionScope Array in CSJS - xpages

I am working on an application that uses Xpages and KendoUI. In one particular page I have a tool bar with a button with "Add Record". The button opens a window and the user will select one from a piece of data and that will create a new record.
The list of data are employee names and I must compute them. I do so in a sessionScope [could be viewScope] array in the beforePageLoad, like so:
<xp:this.beforePageLoad>
<![CDATA[#{javascript:viewScope.myArray = [];
viewScope.myArray.push("1st Val");
viewScope.myArray.push("2nd Val");
viewScope.myArray.push("3rd Val");}]]>
</xp:this.beforePageLoad>
The dropdown needs data in the following format:
var data = [
{ text: "Black", value: "1" },
{ text: "Orange", value: "2" },
{ text: "Grey", value: "3" }
];
For the life of me I cannot get the data into that format. It looks like a javascript object to me.
How do I get the array in the viewScope into the format I need?
var o = {};
o = "#{javascript:viewScope.get('myArray');";

All SSJS Array objects added to any of the scopes are converted to java.util.Vector (how to update a value in array scoped variable?) which can not be stringified with the standard toJson function. This can be circumvented by creating an intermediate object to store the array in:
viewScope.myData={myArray:[]};
viewScope.myData.myArray.push( { text : 'Mark' , value : '1' } );
viewScope.myData.myArray.push( { text : 'Bryan' , value : '2' } );
Also, I doubt your line of code returns the right data without any explicit JSON conversion. I would write it as:
var myArray = #{javascript:return toJson(viewScope.myData.myArray);};

When you set your client side js variable called o it's a string because the value is between "".
So just remove the double quotes and you'll get it as object.
o=#{javascript:viewScope.get('myArray')};
But be aware you might get an js client side error if the viewscope value is not a valid client side JS object !
You can also set the variable o to a string as you did in your example and use the eval clientside method to evaluate the string as an object.
o=eval("#{javascript:viewScope.get('myArray')}");
P.S. In your example there is a } missing is at the end of your SSJS code when you set your "o" variable.. ;-)

Related

Where is my error with my join in acumatica?

I want to get all the attributes from my "Actual Item Inventry" (From Stock Items Form) so i have:
PXResultset<CSAnswers> res = PXSelectJoin<CSAnswers,
InnerJoin<InventoryItem,
On<CSAnswers.refNoteID, Equal<Current<InventoryItem.noteID>>>
>
>.Select(new PXGraph());
But, this returns me 0 rows.
Where is my error?
UPDATED:
My loop is like this:
foreach (PXResult<CSAnswers> record in res)
{
CSAnswers answers = (CSAnswers)record;
string refnoteid = answers.RefNoteID.ToString();
string value = answers.Value;
}
... but i can not go inside foreach.
Sorry for the English.
You should use an initialized graph rather than just "new PXGraph()" for the select. This can be as simple as "this" or "Base" depending on where this code is located. There are times that it is ok to initialize a new graph instance, but also times that it is not ok. Not knowing the context of your code sample, let's assume that "this" and "Base" were insufficient, and you need to initialize a new graph. If you need to work within another graph instance, this is how your code would look.
InventoryItemMaint graph = PXGraph<InventoryItemMaint>.CreateInstance<InventoryItemMaint>();
PXResultset<CSAnswers> res = PXSelectJoin<CSAnswers,
InnerJoin<InventoryItem, On<CSAnswers.refNoteID, Equal<Current<InventoryItem.noteID>>>>>
.Select(graph);
foreach (PXResult<CSAnswers> record in res)
{
CSAnswers answers = (CSAnswers)record;
string refnoteid = answers.RefNoteID.ToString();
string value = answers.Value;
}
However, since you should be initializing graph within a graph or graph extension, you should be able to use:
.Select(this) // To use the current graph containing this logic
or
.Select(Base) // To use the base graph that is being extended if in a graph extension
Since you are referring to:
Current<InventoryItem.noteID>
...but are using "new PXGraph()" then there is no "InventoryItem" to be in the current data cache of the generic base object PXGraph. Hence the need to reference a fully defined graph.
Another syntax for specifying exactly what value you want to pass in is to use a parameter like this:
var myNoteIdVariable = ...
InventoryItemMaint graph = PXGraph<InventoryItemMaint>.CreateInstance<InventoryItemMaint>();
PXResultset<CSAnswers> res = PXSelectJoin<CSAnswers,
InnerJoin<InventoryItem, On<CSAnswers.refNoteID, Equal<Required<InventoryItem.noteID>>>>>
.Select(graph, myNoteIdVariable);
foreach (PXResult<CSAnswers> record in res)
{
CSAnswers answers = (CSAnswers)record;
string refnoteid = answers.RefNoteID.ToString();
string value = answers.Value;
}
Notice the "Required" and the extra value in the Select() section. A quick and easy way to check if you have a value for your parameter is to use PXTrace to write to the Trace that you can check after refreshing the screen and performing whatever action would execute your code:
PXTrace.WriteInformation(myNoteIdVariable.ToString());
...to see if there is a value in myNoteIdVariable to retrieve a result set. Place that outside of the foreach block or you will only get a value in the trace when you actually get records... which is not happening in your case.
If you want to get deep into what SQL statements are being generated and executed, look for Request Profiler in the menus and enable SQL logging while you run a test. Then come back to check the results. (Remember to disable the SQL logging when done or you can generate a lot of unnecessary data.)

Blockly: How to obtain value of a Dropdown or Checkbox Block

I´m new to Blockly and can not find a way to obtain field value of a dropdown or checkbox.
Lets consider following scenario (generated with blockly-dev-tools):
Blockly.Blocks['feature'] = {
init: function () {
this.appendDummyInput()
.appendField("Feature") // just for label
.appendField(new Blockly.FieldDropdown([["manufacturer", "feature_manufacturer"], ["profile", "feature_profile"], ["glas", "feature_glas"]]), "category"); // for dropdown values
this.appendValueInput("feature_name")
.setCheck("String")
.setAlign(Blockly.ALIGN_RIGHT)
.appendField("Name");
this.appendValueInput("feature_prio")
.setCheck("Number")
.setAlign(Blockly.ALIGN_RIGHT)
.appendField("Priorität");
this.appendDummyInput()
.setAlign(Blockly.ALIGN_RIGHT)
.appendField("Versteckt")
.appendField(new Blockly.FieldCheckbox("TRUE"), "hidden");
now obtaining values from value inputs is not a problem, you can get thouse like this:
const featureName = element.getInputTargetBlock("feature_name");
if (featureName) {
console.log(featureName.getFieldValue("TEXT"));
}
const featurePrio = element.getInputTargetBlock("feature_prio");
if (featurePrio) {
console.log(featurePrio.getFieldValue("NUM"));
}
but dummy inputs hosting dropdowns or checkboxes have no methods to provide selected values.
It might be that this is my conceptual error to use dummy inputs to host the elements, but when using value input, you have always those nipples to the right, which are obsolate, since the values are provided by the checkbox or dropdown.
You should be able to skip the middleman and use element.getFieldValue. For example, to get the value from the checkbox field named "hidden", you could use element.getFieldValue("hidden").
P.S. You can also skip the element.getInputTargetBlock middleman and use Blockly.JavaScript.valueToCode (I.E., to get the value of the block in the "feature_name" input, you could use Blockly.JavaScript.valueToCode(element, "featureName", Blockly.JavaScript.ORDER_ATOMIC) or what have you). If you use a different generator than JavaScript, replace JavaScript with the generator you use (e.g. Python or whatever).

Can't modify/remove a field from an ActivityNode using sbt

I created an ActivityNode (an Entry) and I can add custom fields with the
setFields(List<Field> newListField)
fonction.
BUT
I am unable to modify these fields. (In this case I try to modify the value of the field named LIBENTITE)
FieldList list = myEntry.getTextFields();
List<Field> updatedList = new ArrayList<Field>();
//I add each old field in the new list, but I modify the field LIBENTITE
for(Field myField : list){
if(myField.getName().equals("LIBENTITE")){
((TextField)myField).setTextSummary("New value");
}
updatedList.add(myField);
}
myEntry.setFields(updatedList);
activityService.updateActivityNode(myEntry);
This code should replace the old list of fields with the new one, but I can't see any change in the custom field LIBENTITE of myEntry in IBM connections.
So I tried to create a new list of fields, not modifying my field but adding a new one :
for(Field myField:list){
if(!myField.getName().equals("LIBENTITE")){
updatedList.add(myField);
}
}
Field newTextField = new TextField("New Value");
newTextField .setFieldName("LIBENTITE");
updatedList.add(newTextField );
And this code is just adding the new field in myEntry. What I see is that the other custom fields did not change and I have now two custom fields named LIBENTITE, one with the old value and the second with the new value, in myEntry.
So I though that maybe if I clear the old list of Fields, and then I add the new one, it would work.
I tried the two fonctions
myEntry.clearFieldsMap();
and
myEntry.remove("LIBENTITE");
but none of them seems to work, I still can't remove a custom field from myEntry using SBT.
Any suggestions ?
I have two suggestions, as I had (or have) similar problems:
If you want to update an existing text field in an activity node, you have to call node.setField(fld) to update the field in the node object.
Code snippet from my working application, where I'm updating a text field containing a (computed) start time:
ActivityNode node = activityService.getActivityNode(id);
node.setTitle(formatTitle()); // add/update start and end time in title
boolean startFound = false;
// ...
FieldList textfields =node.getTextFields();
Iterator<Field> iterFields = textfields.iterator();
while (iterFields.hasNext()) {
TextField fld = (TextField) iterFields.next();
if (fld.getName().equals(Constants.FIELDNAME_STARTTIME)) {
fld.setTextSummary(this.getStartTimeString()); // NOTE: .setFieldValue does *not* work
node.setField(fld); // write updated field back. This seems to be the only way updating fields works
startFound=true;
}
}
If there is no field with that name, I create a new one (that's the reason I'm using the startFound boolean variable).
I think that the node.setField(fld) should do the trick. If not, there might be a way to sidestep the problem:
You have access to the underlying DOM object which was parsed in. You can use this to tweak the DOM object, which finally will be written back to Connections.
I had to use this as there seems to be another nasty bug in the SBT SDK: If you read in a text field which has no value, and write it back, an error will be thrown. Looks like the DOM object misses some required nodes, so you have to create them yourself to avoid the error.
Some code to demonstrate this:
// ....
} else if (null == fld.getTextSummary()) { // a text field without any contents. Which is BAD!
// there is a bug in the SBT API: if we read a field which has no value
// and try to write the node back (even without touching the field) a NullPointerException
// will be thrown. It seems that there is no value node set for the field. We
// can't set a value with fld.setTextSummary(), the error will still be thrown.
// therefore we have to remove the field, and - optionally - we set a defined "empty" value
// to avoid the problem.
// node.remove(fld.getName()); // remove the field -- this does *not* work! At least not for empty fields
// so we have to do it the hard way: we delete the node of the field in the cached dom structure
String fieldName = fld.getName();
DeferredElementNSImpl fldData = (DeferredElementNSImpl) fld.getDataHandler().getData();
fldData.getParentNode().removeChild(fldData); // remove the field from the cached dom structure, therefore delete it
// and create it again, but with a substitute value
Field newEmptyField = new TextField (Constants.FIELD_TEXTFIELD_EMPTY_VALUE); // create a field with a placeholder value
newEmptyField.setFieldName(fieldName);
node.setField(newEmptyField);
}
Hope that helps.
Just so that post does not stay unanswered I write the answer that was in a comment of the initial question :
"currently, there is no solution to this issue, the TextFields are read-only map. we have the issue recorded on github.com/OpenNTF/SocialSDK/issues/1657"

Can I convert a JSON string to a Flex ArrayCollection in without changing the item order?

Regarding this issue, I have generated dynamically string from Java .each time string format change , for example String format is
[{"BranchName":"Corporate Office","Date":"08\/03\/2013","SPName":"Pharmacy","SPAmount_5-00%":"100.00","SPVATAmount_5-00%":"15.00","SPOtherCharges_5-00%":"30.00","SPAmount_14-50%":"200.00","SPVATAmount_14-50%":"39.00","SPOtherCharges_14-50%":"71.00","SPColTPA":"100.00","SPColChequeDD":"50.00","SPHdfcCC":"100.00","SPIdbiCC":"100.00","SPColCash":"50.00","Difference":"55.00"},
But when I convert array collection with following code .
var rawData:String = String(event.result);
var arr:Array = (JSON.decode(rawData) as Array);
var dp:ArrayCollection = new ArrayCollection(arr);
but array collection order changed as default sort like [Branch, Date, Difference,.. ] . But I want same as string format order. So How can I prevent Default order.
Actually, what you've described here is an array of objects (your example just includes one object). In the JSON parsing to array, each object is indeed in the order in which it is declared; but in the OBJECTS that are created, the properties may not be listed in the same order.
For example:
'[ {"Branch":"Corporate", "Department":"Finance", "Cost":"10000", "Attended":"true"},' +
'{"Branch":"Las Vegas", "Department":"Hospitality", "Cost":"20100", "Attended":"false"},' +
'{"Branch":"San Diego", "Department":"Banking", "Cost":"11023", "Attended":"true"}]'
Parsing would return arr[0] as the Corporate object, arr[1] as the Las Vegas object, etc. Iterating through the properties I got:
var obj:Object = dp.getItemAt(0);
for (var prop:String in obj) {
trace(prop + ' is ' + obj[prop]);
}
Department is Finance
Attended is true
Branch is Corporate
Cost is 10000

How can I get the properties from a property Group of a Custom Control as an Object?

I'm working on a Custom Control that displays markers on Google Maps. I have a couple of properties like "maptype" , "zoom", etc. It is easy to access them in Javascript: I can use #{javascript:compositeData.zoom} to get the value of the zoom property.
Now this is my problem: I use a group of properties for each marker.
The name of the group is "marker" and a marker has 6 properties: "title", "layer", "infotext", "icon", "address" and "animation".
If I try to access the group with
var markers = #{javascript:compositeData.marker};
I'm getting an error in firebug:
missing : after property id var markers = [{layer=2,
address=Oldenzaal, animation=DROP, icon=/ogo_notes.png...
an arrow is pointing to the first = between layer and 2
(I am not allowed to put in an image in stackoverflow)
If I use
var markers = #{javascript:'"' + compositeData.marker + '"'};
markers is an Object, but each Object contains a string with all the propperties of the marker.
I know I can do some coding to make an object of each string, but this is not easy if not all propperties are required. If a propperty is not required than it will not show up in the string.
I guess that there must be a more easy way to get each marker as an object so I can get the value of the icon with code like:
var icon = marker.icon
How can I do this?
You can use compositeData.marker.icon to get the property icon inside the group marker. If you have checked "Allow multiple instances" for the group then to get the properties you will have to go:
compositeData.marker[0].icon
compositeData.marker[1].icon
and so on...
Update 26-Apr-2012 (Naveen)
To use it with client side javascript you can try to put the value in a hidden input field like this:
<xp:inputHidden id="hdnIcon">
<xp:this.defaultValue><![CDATA[#{javascript:var value = new Array();
for (var i=0 ; i<compositeData.marker.length ; i++) {
value.push(compositeData.marker[i].icon);
}
return #Implode(value, ",");}]]></xp:this.defaultValue>
</xp:inputHidden>
The value of this hidden input field can be read through client-side javascript like this:
var value = document.getElementById("#{id:hdnIcon}").value.split(",");
for (var i=0 ; i<value.length ; i++) {
<YOUR CODE>
}
Another way to do this can be to convert compositeData.marker and its contents to a JSON string and then run the client-side javascript on it.

Resources