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

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).

Related

How to turn String Value into Number - Google Tag Manager

Im setting up my Google Tag Manager and I want to grab the purchase value from my Data Layer. This works fine, but Facebook doesn't recognize this value as its a "String" and not a number.
Therefore my question is: How can I convert this String (Return Type) to a Number (Return Type) in order for Facebook to pick it up? If that helps, its a currency.
Create JavaScript Variable named "String2Number Convertor"
function(){
return function(s){
return +s;
}
}
Create another JavaScript Variable named "Facebook Purchase Value"
function(){
return {{String2Number Convertor}}({{DataLayer Conversion Value}})
}
I assume you have your purchase value in data layer variable I called in this example as DataLayer Conversion Value
With this approach you can convert any other variables into number.

Why does this Context.Sync not work?

Why does this code snippet not write the values back to Excel unless I un-comment the range.values=range.values line?
$('#run').click(function() {
invokeRun()
.catch(OfficeHelpers.logError);
});
function invokeRun() {
return Excel.run(function(context) {
var range = context.workbook.worksheets.getItem("Sheet1").getRange("A1:B3");
range.load('values');
return context.sync()
.then(function() {
range.values[1][1]=99;
console.log(JSON.stringify(range.values));
//range.values=range.values
return context.sync();
});
});
}
Array properties are special. I have added a page on my website to describe the topic: Reading and writing array properties.
Summarizing from there, the way that the proxy-object model works, whenever you set a property on an object, the Office.js runtime has a hook into the setter and getter, which is used to intercept the call and add the command to the queue.
Let's take an example of a regular property first. Per the above, whenever you set something like range.format.fill.color = "red", the setter for the color property intercepts the request and internally adds a command into the queue to set the range fill color to red (to be dispatched with the next context.sync)
On the other hand, if all you had was var color = range.format.fill.color
(after a load and a sync, of course), the getter would fire instead of the setter, and the color variable would get the range's current fill color.
Now, that was regular properties. Whenever you set an element of the array, you are effectively accessing the array value as a getter. From a runtime perspective, this line is no different from a slightly more verbose version:
var array = range.values;
array[r][c] = '-';
Because the getter for range.values returns a perfectly plain JS array object, accessing it and then setting its value does nothing to propagate it back to the original Range object.
If you want the values to get reflected back, the best thing is to get a reference to the array right after the sync (i.e., var array = range.values, just as above), then set the values on the array as needed, and then finally set it back to the object: range.values = array.
It means you could also modify the values array in place, and then assign the values property back to itself at the completion of the loop (range.values = range.values). However, this looks awkward, as if it’s a no-op, whereas in reality it is not. So personally, I prefer to retrieve the array at the beginning and assign it to its own variable, then do any necessary modifications, and finally set the full array back.
UPDATE to clarify the above:
To be very clear, the arrays returned by accessing the .values, .formulas, etc., ARE pure vanilla JS arrays. That's actually the crux of the problem: that in order for Office.js to return pure objects, it means that those pure objects can't be "spiked" with the ability to reflect changes.
For what it's worth, we actually have an upcoming feature that should be rolling out in a month or two, where we will be introducing an object.set syntax, as in:
range.set({
values: [[1, 2], [3, 4]],
format: {
fill: {
color: "purple"
}
}
}
This will make it more convenient to set multiple properties on the same object, but it might also make the array properties easier to deal with.

Google Sheets Script Parameter for e.g. Button

Is it possible to pass parameters on a signed script?
I have a spreadsheet and made a couple of buttons signing functions to each.
Sample function:
function foo(){
return "bar";
}
Calling this function on a cell
=foo()
returns me bar, signing this function to a button
foo
returns me nothing but of course it works. I mean the script cant return a string to an image (whats actually my button). Anyways...
Sample function with parameter:
function foo(bar){
return bar;
}
calling the script in a cell
=foo('hello world')
returns me hello world as expacted
Signing this to my button
foo('hello world')
causes an error because the button cant find the script. He's searching for a function name foo('hello world') but (...) has non to do with the function.
So how can I pass params when signing script?
At the moment I have 26 functions (all the same, only 1 param changes) to solve this. But with passing a parameter I could do all this with 2 functions instead of 26.
You can't pass parameters when using a custom function via assigning it to a drawable image. It only works when the custom function is called from a cell.
Possible workarounds is to store your parameters on certain sheets and cells then retrieve them via
getRange() and getDisplayedValue().
Other possible workarounds are posted in a Google product help forum which can be found here
I found the best option to create separate helper methods for each individual button, which then pass the correct parameter to the generic method.
In the below example you would bind to the buttons the city specific method, for example for "Helsinki" button "updateAvailabilityInfoHelsinki".
function updateAvailabilityInfoHelsinki() {
updateAvailabilityInfo("Helsinki");
}
function updateAvailabilityInfoPorvoo() {
updateAvailabilityInfo("Porvoo");
}
function updateAvailabilityInfo(citySheet) {
// Do something with citySheet parameter
}

Binding an edit box within a custom control to a form field programatically

I have a notes form with a series of fields such as city_1, city_2, city_3 etc.
I have an XPage and on that XPage I have a repeat.
The repeat is based on an array with ten values 1 - 10
var repArray = new Array() ;
for (var i=1;i<=10;i++) {
repArray.push(i) ;
}
return(repArray) ;
Within the repeat I have a custom control which is used to surface the fields city_1 through city_10
The repeat has a custom property docdatasource which is passed in
It also has a string custom property called cityFieldName which is computed using the repeat
collection name so that in the first repeat row it is city_1 and in the second it is city_2 etc..
The editable text field on the custom control is bound using the EL formula
compositeData.docdatasource[compositeData.cityFieldName]
This works fine but each time I add new fields I have to remember to create a new custom property and then a reference to it on the parent page.
I would like to be able to simply compute the data binding such as
compositeData.docdatasource['city_' + indexvar]
where indexvar is a variable representing the current row number.
Is this possible ? I have read that you cannot use '+' in Expression Language.
First: you wouldn't need an array for a counter. Just 10 would do (the number) - repeats 10 times too. But you could build an array of arrays:
var repArray = [];
for (var i=1;i<=10;i++) {
repArray.push(["city","street","zip","country","planet"]) ;
}
return repArray;
then you should be able to use
#{datasource.indexvar[0]}
to bind city,
#{datasource.indexvar[1]}
to bind street. etc.
Carries a little the danger of messing with the sequence of the array, if that's a concern you would need to dig deeper in using an Object here.
compute to javascript and use something like
var viewnam = "#{" + (compositeData.searchVar )+ "}"
return viewnam
make sure this is computed on page load in the custom control
I was never able to do the addition within EL but I have been very successful with simply computing the field names outside the custom control and then passing those values into the custom control.
I can send you some working code if you wish from a presentation I gave.

Setting value of a Radio Button Group Client Side

I need to set the value of a Radio Button Group that has two possible values. I'm able to accomplish this with SSJS, but having issues setting it via CSJS. Any help is appreciated.
I use something like this:
function setRadioValue(value)
{
var elements = document.getElementsByName ("#{id:radioGroup1}");
for(i=0;i<elements.length;i++) {
if (elements[i].value == value) {
elements[i].checked = true;
}
}
}
Then you can just call setRadioValue("This is teh value of the Radio Button I want to set")
Thanks
You need to get the the server-side generated name of the radio button group using the #{id:} method. Example:
var radioButtonGroup = XSP.getElementById("#{id:radioButtonGroupName}");
You can then use client-side Javascript to manipulate the radioButtonGroup element. I believe you need to loop over the radio buttons in the elements until you find the radio button with the desired value. You can then set its checked value to true.

Resources