[Plugins for Beginners]: Accessing Input and Output Fields in Plugin Action Javascript Code

Hey everyone…

I’m not a professional programmer, but I can put together the basics in most languages given a few hours to remind myself of the syntax… which may make me quite a typical Bubble user. So I decided to write a plugin with a server action using some basic javascript code, thinking it would be quite easy. Boy have I just been round the houses on the most basic issue of how to read input fields. Before I forget the pain and the details, I want to document it here for all those who follow in my footsteps!

First, before you go anywhere on plugins, I recommend doing the wonderful Plugin Development course from the @copilot guys. Click here for the details.

Text Inputs - The Easy Bit
So text inputs are pretty easy. Define them like this:

And then in your code you can get the value you specified using the syntax:
properties.text_in

Text Output - For Action Running on Server
Define your “result” outputs like this:

And then in your javascript, write to the output with the line:

return {"text_out": myText};

where myText a text variable you have created within your code. Just don’t forget the quotes around “text_out” as I did for a short while.

Text Output - For an Element Action
In this case, you define an “Exposed State” of the element in the same style as shown above. The code to write to these outputs is:

instance.publishState("text_out", myText);

Custom Type Input - Not at All Obvious!
Wow, this part took me hours and hours to work out. I just couldn’t find one place to give me the answer, things I read were talking in slightly too technical javascript gobbledegook, and sometimes forum post I read were not quite accurate.

But YEAY, I have just worked it out. I’m so happy…

So this is for an input which is of absolutely any type you have defined yourself… so maybe a type called “Contact”, or “Event”, or “Invoice” for example.

The first thing is you need to define your input to be of type “any thing” like this:

Now the next part depends on which kind of action you are defining…

For an Action which runs on the Server…
Then you have to realise that when you try to access this input (properties.anything), it will probably be empty (null in javascript parlance), even though you specified a value in your workflow. That is because, as a user defined thing rather than just a piece of text, it needs getting from the database. So first you need the line:

properties.anything.get();

( For an Action which is part of an Element… you don’t need to do this - in fact the above line will throw an error.)

And to save yourself typing the “properties.” bit the whole time, it can be useful to define a variable for this:

var anything = properties.anything;

which will send the plugin off to the database to go and fetch it. There is a bit of an issue around the timing of that process and whether your function will run more than once, which you can read about in the manual - click here for all the details.

Now it exists in your code… but how on earth do you access its field values?

Well firstly, you need to run a very special function to see what the fields have been renamed to now they are in javascript land. Execute this line of code and send the result to a text output or the console:

x = anything.listProperties() + "\n";

(If you don’t have the + “\n” part concatenated, you may need to convert the type to a string, so with the line:
x = toString(anything.listProperties());

This gives you a big comma separated list. It is easier to read with each field name on a new line, which with a bit of javascript digging around, can be done like this:

t = x.replace(/,/g,'\n');

which globally replaces the commas with a new line. Then you end up with a usable list of the field names, which will look something like:

first_name_text
deleted_boolean
number_number
start_date3_date

(note that the names may not match your Bubble field names if you have been renaming some of your Type’s field names in Bubble)

And then… you can access the values of the individual fields with the syntax:

t = anything.get("first_name_text") + "\n";

(assuming you defined the variable
var anything = properties.anything;
as shown above).

Wow, what a journey… and one that I hope will save lots of time for many enthusiastic Bubblers in the future.

Good luck! :grinning:

But wait… there is more…

As I start to apply this, here are more of my learnings…

Specifying the Thing on the Action in Your Workflow
So when you call up your nice new server action in a workflow, you have to be really careful. What is the difference between these two?

Plugins%20(4) Plugins%20(5)

Yes, it is the word “value”. For some unknown reason, when calling up a plugin server action, Bubble lets you specify the name of an input, as well as the name of the input’s value. In all other areas of Bubble, you can’t do this! Beware, as without the important “value” part, you will not pass a value on to your plugin and the get() function will always fail!

Working with Lists of type “any thing”
With a list of type “any thing”, you first need to get the whole list from the database… and it is best to store this in a variable to make access easier later on. So if your input field is called “anythinglist”, then you need the line:

var anythinglist = properties.anythinglist.get(0, properties.anythinglist.length());

(note two things - the () after length, and the fact that we use length() rather than length()-1 )

Then to access the field of a specific item within the list, you can use the standard javascript array syntax [i] on anythinglist, followed by the .get() function:

t = anythinglist[i].get("first_name_text") + "\n";

(and this coding is the same whether you are creating an Action or an Element Action)

Protecting Against Getting Something That Isn’t Specified
You also need to be careful to test in your code that there actually is a value on the input before you do the get function. Do this by testing that the input field exists in the first place:

if (properties.anything) {properties.anything.get();}

Passing Values Back Out - Server Side
The plugin I have been working on has server side outputs defined as follows:

To write to these, use the code:

var my_title_out, my_email_out, my_messaging_out;
// lots of data processing...
return {"title_out":my_title_out, "email_out":my_email_out, "messaging_out":my_messaging_out, "error_out":false};

Passing Values Back Out - Element Based Client Side
I have created a similar element based client side plugin, so it has the same outputs (or “Exposed States” in this case) defined as follows:

To write to these, use the code:

var my_title_out, my_email_out, my_messaging_out;
// lots of data processing...
instance.publishState("title_out", my_title_out);
instance.publishState("email_out", my_email_out);
instance.publishState("messaging_out", my_messaging_out);
instance.publishState("error_out", false);

Performance of Server Actions
Expect Server Actions to take a while longer to run than client side actions, as they have to be packaged up and send off to an AWS Lambda environment to be run…

46 Likes

Wow, thanks for sharing, @antony, and for your dogged persistence in working out the details! :+1:

Would you mind sharing a screenshot of how your custom “thing“ is defined in the database? I’m thinking that might shed some light on how field names are transformed in JavaScript code.

Hey @sudsy, it’s a pleasure.

I just edited the post for clarity to answer your question. I added the line:

So this is for an input which is of absolutely any type you have defined yourself… so maybe a type called “Contact”, or “Event”, or “Invoice” for example.

So yes, basically any thing and every thing you have defined to be of a custom type!

I’m realising there is a pattern to the naming structure, but it isn’t reliable. That is because the name is based on the original name you gave the field of your type. If like me you have changed the names during your development, the javascript name is a closer relation to the original name than its current one!

I hope that makes sense,
Best wishes,
Antony.

1 Like

Hello! Something I can not. I do everything as it is written, but after the line properties.anything.get (); - I get the error: Error: Invalid from: undefined. Something is wrong with the line: properties.anything.get ();

Hey @vasanbo, thanks for your input… yes, I have just realised I missed out a line in my explanation. I just edited the post to fix it.

So from what you read, you need to add the following line for my code utilising the get() function to work:

var anything = properties.anything;

Or, you could have put on your get() line:

t = properties.anything.get("first_name_text") + "\n";

Sorry about that!

1 Like

Hello @antony, thank you for the good work. You have killed it for me with “text_out” thing.

I have a question that may be more of Javascript than of Bubble. I have been trying to build a base64 encoding part of a plugin. I had managed to crack a few syntax but I am getting stuck with concatenation of some parameters and the actual encoding in Bubble.

Below is my code:

function(properties, context) {
var timestamp = properties.timestamp;
var passkey = properties.passkey;
var passcode = properties.passcode;

var myString = passcode+passkey+timestamp

var encodedString = btoa(myString)

return {“text_out”:encodedString}

}

This is the error I am getting when I run the plugin:
image

Note that exactly same code is working fine when I test it with String.prototype.concat() - JavaScript | MDN

I would like you assistance in looking through it and advise where i am going wrong?

The function btoa isn’t defined.

Try window.btoa.

Hello @vini_brito, Just did, got similar error.

image

Is that client or server side?

Edit: If it’s server side, try:

Apparently this will do it:

(function () {
  "use strict";
 
  var btoa = require('btoa');
  var bin = "Hello, 世界";
  var b64 = btoa(bin);
 
  console.log(b64); // "SGVsbG8sIBZM"
}());

I am running it on server side

Yeah, so no browser functions there.

Try:

Apparently this will do it:

(function () {
  "use strict";
 
  var btoa = require('btoa');
  var bin = "Hello, 世界";
  var b64 = btoa(bin);
 
  console.log(b64); // "SGVsbG8sIBZM"
}());

This

var btoa = require('btoa');

may be the line you need.

1 Like

Still a struggle, @vini_brito.

I added the line. Then I was informed that there is a module “btoa” which could not be found. After following the link provided by bubble on addition of module json. This is what I added but the error is still there.
{
“name”: “btoa”,
“homepage”: “https://git.coolaj86.com/coolaj86/btoa.js.git”,
“description”: “btoa for Node.JS (it’s a one-liner)”,
“repository”: {
“type”: “git”,
“url”: “git://git.coolaj86.com/coolaj86/btoa.js.git”
},
“keywords”: [
“btoa”,
“browser”
],
“author”: “AJ ONeal coolaj86@gmail.com (https://coolaj86.com)”,
“engines”: {
“node”: “>= 0.4.0”
},
“bin”: {
“btoa”: “bin/btoa.js”
},
“main”: “index”,
“license”: “(MIT OR Apache-2.0)”,
“version”: “1.2.1”
}

Apparently more needs to be done in order to load a Node module.

Take a read at this post, it says we need to check “uses node modules” and then write our own “package.json”.

1 Like

Hi @vini_brito,

return (Buffer.from(myString).toString(‘base64’)) worked for me without need for dependencies

1 Like

That’s great! Thanks for the feedback!

@timothymugendi @vini_brito In the interests of making this a little easier to use, we’ve just added a feature to automatically create a package.json from your require statements.

[New Feature] Server side actions now support node modules

2 Likes

Awesome! I confess that I overlooked it because of me being used to just require stuff. Thanks for reducing our manual labor!

Thanks Marca, tried it and it works fine.

Hello @marca ,

I am trying to add var request = require(‘request’);

but i am getting an “undefined” error
image

Other times i get a time our error
image

What do I need to do right?

This was a bug on our end, now fixed.