A somewhat of a documentation of hidden formulas [Added mechanics of nested buttons]

Yup, I’ve put it through a few beautifiers until I got a semi-readable code.
And each formula is defined as an object with a standard js function for getting the result.

And using the _Merge() formula to view the computed column results as json was the key to reading the obfuscated javascript.

1 Like

Fiddling with your research doc I found out about the $$ operator used to create references.

E.g. $$[cell:grid-ahmvaWqCgY:c-ni32haMaa_:i-pHC2juX1xr:false:false:Value]


Syntax goes [<type>:<tableId>:<columnId>:<rowId>:<bool>:<bool>:<displayName>]. Some params can be omitted depending on type. Not sure what the bools are for either.

Types are (click to see)
"canvas";
"cell";
"column";
"controlGrid";
"document";
"grid";
"row";
"rowColumn";
"variable";
"table";
"constraint";
"linkedColumnFormula";
"image";
"volatileFunction";
"fakeReference";
"documentObjectsReference";
"packReference";
"packConnectionReference"

You can also type $$[] anywhere in a formula to break down all references in it.

Still, haven’t found a practical use for it. Since it’s not a string and it needs to be typed, I guess a first step would be using your ParseJSON method to create the object.

My first idea was to programatically insert/get column values. If we just found a way to list columns (as the API does)… there’s a challenge for you @Filmos.

2 Likes

Dear @Filmos, @Dalmo_Mendonca, @Johg_Ananda and @Paul_Danyliuk

I felt so free to move your posts under “Developers Central” as your input is at a higher level what might confuse other users.
Keep up with your magical inputs :handshake:

2 Likes

@Filmos It might be an idea to have some documentation on these calls. Or have someone from Coda provide the missing details.

1 Like

Mechanics of nested buttons [Back to index]
Since 2019-09-12T22:00:00Z buttons can no longer be nested. There is a petition to bring this feature back, but for now all of the information below is deprecated.

With the Button() formula, you can put a button into a label of another button (which is in a label of another button and so on).
image
This kind of nested buttons have a few useful and interesting properties.

Action execution order
When you click on a nested button, it will firstly execute the action of the button you directly clicked.
Then it will execute the action of its parent, and then a parent of the parent, and so on until it reaches the root.

That means that if you were to click the blue button from the image above, action order would be
blue --> green --> yellow --> white, with white overwriting any colliding actions (like changing value of the same row and column).

There is just one big problem - those actions are asynchronous.
That means that they start executing in this order, but the changes may be applied in a completely different one, based on the complexity of the action.
This is fragile to such an extent that just wrapping a formula with RunActions() or adding an if statement (even if it is just If(true,"","")) will change this order.

That means that this order is only reliable when the actions are nearly identical (for example: ModifyRow() with static target, column and value).

Activating only the top button
Clicking only the top-most button can be useful in many cases and it can actually be achieved.
Because of the execution order, when a button action is executed you can check if any other button was clicked earlier. If it was, it means it was above this one.
To do that, you would need to wait (using _Delay()) until the previous action is executed and check if there were any changes using Modified().

While it is possible to create this for more than 2-deep buttons, the delay would stack up and make the buttons pretty slow and impractical. So I’m only going to explain how to achieve that with 2-deep buttons.

The problem is that we need to make sure the background button detects the top button being clicked, even if it has a slow formula.
To do that we can simply change the formula of the top button to

RunActions(
  ModifyRows(thisRow, [Button detection], "W"),
  <the actual formula>
)

This makes sure that the change will be detectable at the same time after the button is clicked, no matter what the complexity of the formula actually is.

Detecting the button change is also tricky.
Because of how the _Delay() formula works, you have to split the action into two buttons.
The first one (the one you actually click) should look like this:

_Delay([Actual action],300)

And the second one should look like this:

if(Now()-Modified(thisRow.[Button detection])<=Seconds(0.35),"",
<the actual action>)

You may be tempted to set the button detection column to Now() when clicking first button, but not only is that value rounded (and basically useless) it will also create a lot of other problems.

Note that you don’t need to use those exact delays, those are just the ones I found reliable.

One more issue
There is still one small issue with this solution - if you click the bottom button fast enough after clicking the top button, the bottom button won’t be activated.

This can be easily fixed by changing the formula of the bottom button to

if(IsNotBlank(thisRow.[Button detection]) && 
   Now()-Modified(thisRow.[Button detection]) <= Seconds(0.35),
 ModifyRows(thisRow,thisRow.[Button detection],""),
 <the actual action>
)

Sandbox

[Back to index]

2 Likes

Is there any chance any Codans could tell us the likelihood that FormatDateTime() (and it’s siblings) will change or be removed? Formatting dates/times on the canvas is ridiculously verbose right now, and these functions make it a lot simpler.

2 Likes

@Ian_Nicholson you would need to ask @mallika about that.

I’m also curious what’s the status of hidden formulas and if it possible that those that are more stable (_Bold(), _Italicize() etc.) will be fully implemented soon.

1 Like

Unfortunately due to recent coda update nested buttons are no longer supported, which significantly limits possibilities of hidden formulas.
This includes making deep modifications nearly useless, as you can no longer hide the on-hover box.

1 Like

Seems like they patched it straight after your post…

That stuff went all a bit over my head since I wasn’t able to test it… I can certainly see the use for the async case, and that’d be great since the only way to run async actions is to manually click the buttons.

But then when you started adding delays, what are (were) the advantages of it over a simple RunActions?

The main advantage of using nested buttons (at least for me) was the ability to create more complex user interface. So that you can either click the big button for one action, or one of the smaller ones on it for a different one.

Example:
image

1 Like

Maybe it would help if you separate out the example in a shared document? The example you posted is still quite ambiguous.

1 Like

Actually, since I’m no longer going to work on this project using coda I’m going to share it whole.

And, in case anyone is interested in future development of this project, I’m going to post video entries about it on my youtube channel.

1 Like

Using RegexExtract to implement a GroupBy() function: Group By using Hidden Formula RegexExtract

@Filmos @Dalmo_Mendonca @Paul_Danyliuk Anyone maybe know how to get the current URL (of the section displayed) through formula?

I’m trying to use it for filters so I can filter using the current page as context without having to hardcode it.

1 Like

I found a solution of how to get rid of the popup and render the actual control object, not a control object wrapped in an object. You still provide it wrapped to ParseJson(), but then use _Deref_Object() to extract the control object:

ParseJson('{"0":' + YourJson + '}')._Deref_Object("0")

I used this in my latest demo to insert text into Tasks table not wrapped but perfectly editable:

2 Likes

Did you ever solve this?

Regarding nice ways to prevent document crashing when using undocumented functions:

I thought I would attempt to come up with highly convenient way to prevent a crash, by first checking in the current year was prior to 1970 using Today().Year(), and if so, skip the offending code. In principle this works by simply changing the current year on your host O/S… however, I then discovered that Coda servers did not like this technique when dealing with offline documents as presumably it messes up some authentication or sync stuff.

So, I came up with using the current user’s time zone (via CurrentTimezone().CurrentOffset which works brilliantly.
Basically, my usual timezone offset is 0 or 1. So, before I execute any undocumented function, I check the timezone offset. If it’s > 1, then I skip the function. This way, I can simply modify the timezone on my host machine, open the doc and fix the error.

An alternative to this would be to create an artificial user (say ‘Debugger’) and check if the current user = Debugger. If so, skip the offending code.

Is the idea such that if the doc crashes, you could log in with Debugger and then fix the problem and restore the doc?

Yes.

The real crux of the problem is that if you use an unofficial function, you could crash your doc and end up in a perpetual state of never being about to fix it because it crashes when you open it.

The workaround is to ONLY execute the unofficial function under specific conditions, or to NOT execute them if a certain condition exists e.g based on the current user.

So you need an if() as a minimum at every instance of the hidden formula? Maybe declare a canvas variable with a short name and then if(x,code,"") - that the idea?