Library APIs ignore the Global setting of a variable

It looks like the string.replace() function, if used inside a Library API, does not respect the Global name of a variable. This problem may happen with other functions, but I only tested with “string.replace()”.

I created a short piece of code to reproduce this problem, and I observed the same behaviour using NodeJS and PHP server modes.

I am running Wappler 5.2.2 using Windows 10.

WHAT WORKS FINE

Here’s an example code that runs fine when used as a “normal” server action:

In steps that set the value of the variable myString, the Global Name is also set to “myString”.

And this is the result:

As you can see, at the end of the repeat, all tokens have been replaced.

WHAT DOES NOT WORK

If I create an API Library using the same steps as before and execute it from a normal API, the result is not what one would expect:

Effectively, each iteration of the repeat loop seems to have its own local version of myString, completely ignoring the Global setting and at the end, myString is not altered.

If this is not a bug but rather the intended behaviour of a Library API (it would be odd, but I don’t know), can you please guide me on how to address this problem?

Many thanks!

Upate:
I upgraded from 5.2.2 to 5.2.4 because I read that this problem
Variables with Global Names Stopped Updating in Library Actions, which looks similar to the one I reported, had been fixed in 5.2.4.

I just wanted to let you know that unfortunately, the problem that I reported still occurs in Wappler 5.2.4.

Thank you in advance for your prompt attention.

Alex

Also linked

What happens when you change the name of the setvalue action in the repeat, call it myStringInner and the global name myString.

Just to clarify, the Name of a variable is the local variable name, it is created within the current scope. The Global Name is the global variable name, it is being set on the global scope. When using a variable within an expression it will first look in the local scope and then go up the scopes up to the global scope until it finds a variable with that name.

For the exec action it creates a sub scope when you give it a name, you can see this as it creates a sub object in the output. The initial myString is a local variable in that sub scope and you set with the Global Name the global variable, but in the same time you set a local variable with the same name. In the After step it will return the local myString variable since it is the first one it finds and not the global.

Thx Patrick for your response, but I’m still observing a weird behaviour after doing the following:

In the API:
Named the local variable myString_API (Global Name: myString_API)

In the LIB:
Named the local variable myString_LIB (Global Name: myString_LIB)

There is no possible name conflict between the code in the API and the code in the Library.

The code in the API now looks like this:

And the code in the library looks like this:

To be clear, there are no parameters passed to the library and none of its outputs is consumed by the API. What you see below is inside the API and inside the Library.

When running the API, which as a last step invokes the library, the output looks like this:

Note the differences in the underlined text, showing that the API preserves the changes done by the previous step in the repeat (in green) while the LIB (in red) does not.

I hope this helps a bit more.

Just to be certain: the output that I see from the library is not what I should see, right? I am expecting to see a behaviour similar to that of the API…

Many thanks!

Alex

PS:
I also tried what you said in the other post: name the variable within the repeat differently, while preserving its Global value. The result was the same:

Hi @patrick ,
Do you have a date to fix this? I’m eagerly waiting :slight_smile:
Also, please let me know if there is anything I can do to further clarify.

Many thanks!

Alex

I am also experiencing the same issue. Were you able to solve it?

It is actually the expected behavior, so not a bug. Will have an other look at it if we can improve the behavior of it.

@Patrick,
I am Very confused on why the observed behaviour should be the expected behaviour. Why should variables defined as global should be treated differently by a repeat within a library and a repeat within an API?

The code is fundamentally the same. When running as an API, the code runs “well”: the global variable is edited and the edits are preserved from one loop to the next. When running as an API, the repeat ignores that the variable on which it is acting is defined as global and creates (or seems to create) a temporary one that is discarded in the next loop.

Maybe the question should be formulated differently then: What do I need to do (differently) in a Library so the repeat step preserves the modifications to the value of a variable done in each of its loops?

Many thanks,

I’ve just hit this one too. As with the question presented by @aschoijett above, is there any way to get global variables behaving the same way inside a Library module as they do in an API module (when accessing them inside a repeat)?

I ran an additional test case on this and it looks like global variables ARE accessible via While loops, but not via Repeat blocks. That’s quite annoying, but does mean there’s a work around where you can use a While loop with a count instead of the Repeat block.

Here’s how the two test cases look. Failing Repeat loop case is at the top with “global_repeat_total” being a global variable. The working While loop case is at the bottom. I couldn’t work out how to get the size of the loop_values array (.length() is giving an incorrect value as shown), so I put it into a Wappler Array object and calculated its size from there. Not the most efficient way of doing it, but it worked. Both “count” and “global_while_total” are global variables.

The proof in the pudding is the values for “final_global_repeat_total” and “final_global_while_total” which show the failing Repeat and the working While.

REMINDER: The below screenshot of a Library module which is being called via an Exec action. If the same code was run in a standard API module then the issue with Repeat would not exist.

Here’s the values that are outputted. The increment_value is output in the Repeat loop just to show that the correct value in the array is accessed on each iteration and therefore there’s no reason why the global total shouldn’t be correct (other than global variables not working in Repeats).

image

@patrick any thoughts on the above?

Make sure your local variable name and global variable name are different. If local and global name are the same it will update only the local variable and not the global.

What you have can be compared with the following JavaScript code:

function flow() {
  var global_repeat_total, loop_values, repeat, final_global_repeat_total;

  global_repeat_total = 0;
  loop_values = [1,2,3,4,5];

  repeat = loop_values.map(function($value) {
    var increment_value, global_repeat_total;

    increment_value = $value;
    global_repeat_total = global_repeat_total + increment_value;

    return { increment_value, global_repeat_total };
  });

  final_global_repeat_total = global_repeat_total;

  return { global_repeat_total, loop_values, repeat, final_global_repeat_total };
}

The JavaScript code will have NaN as values within the repeat since our expressions handle undefined/null values different then normal JavaScript. But the result is further the same as with the flow/server action.

Setting Name in the SetValue will create a local variable with that name.
Setting Global Name will set the value in the global scope.

When you use a variable in an expression it will first lookup the name in the local scope and then go to the parent and as last it will check the global scope. So when it finds a variable with the same name in an other scope it will use that value instead of the global value.

Back to the loop, setting the Name of the SetValue inside the repeat to local_repeat_total and set the additional Global Name to global_repeat_total should fix the error. The sample JavaScript code should look then like:

function flow() {
  var global_repeat_total, loop_values, repeat, final_global_repeat_total;

  global_repeat_total = 0;
  loop_values = [1,2,3,4,5];

  repeat = loop_values.map(function($value) {
    var increment_value, local_repeat_total;

    increment_value = $value;
    local_repeat_total = global_repeat_total + increment_value;
    global_repeat_total = global_repeat_total + increment_value;

    return { increment_value, local_repeat_total };
  });

  final_global_repeat_total = global_repeat_total;

  return { global_repeat_total, loop_values, repeat, final_global_repeat_total };
}
1 Like

Hi @patrick, yup you’re right.

I renamed the Set Value in the Repeat to local_repeat_total, BUT ALSO had to rename the Set Value at the very beginning of the flow to local_repeat_total. All the while, they both have global_repeat_total as their Global Name.

Changes visible here:

Correct result set is here:

image

Having got it working, I guess the one bit of feedback I’d give is that from a user experience perspective, the scope of local and global variables could be a bit more transparent in the editor.

Thanks for the quick response. It’s really appreciated.

Scott

If I had to recreate the code for the flows now I would have done it different, but can’t change it now without breaking backwards compatibility. We can perhaps make the docs or UI a bit better for the current Set Value Properties to make it clearer to the user what the Name and Global Name does.

3 Likes

I just use session variables for anything I need to be global… it just feels much easier and more predictable!

You have to watch out with session variables. The global variable is only for the current request while the session variable is for the user session. Having 2 parallel requests from a user updating the same session variable can cause some unpredictable behaviors when you only wanted to count from a local loop.

Thank you for those details @patrick… I’ll make sure the names are pretty unique!