[Open discussion]: What an action (add, delete, modify) should return?

Hi everybody.

The discussion started here (Why Doesn't This Formula Work? by @Connor_McCormick and followed by @Jason_Tamulonis ), but perhaps it’s a good thing to give full visibility to a topic I consider an interesting point.

Have you ever had the need to refer to a just created/modified/deleted row(s)?

As of now, functions AddRow(), AddOrModifyRows(), ModifyRows() and DeleteRows() return (output) an Action that - when run - will affect the selected rows.

My current perception is actually that those formula are Actions that return void.
My wish would be that those formulas would return the list of Row references affected by the Action.

The advantages I see are related to contextual and chained actions that would be more intuitive.

Think about something like:

// deleting/archiving
Tasks.Filter([Done]).DeleteRows().FormulaMap( // delete completed tasks
   Archive.AddRow(currentValue) // for each one, put a row into the archive
)

Note that there would be an implicit RunActions()

To achieve the same, now it should be:

Tasks.Filter([Done]).withName(done, // filter completed tasks
    RunActions(
        done.FormulaMap(
            Archive.AddRow(currentValue) // for each task, put it into archive
        ),
       done.DeleteRows() // delete the list
    )
)

Or the very common:

// open a just created row in another table
OpenWindow(Table.AddRow(...).ModalURL)

The current workaround leverages on a not fully consistent behaviour and it implies the need of a temporary column in the “generating” table.

I can provide more examples, if needed.

While I understand it’s not easy, I’d like to find out:

  • if others would welcome this different perspective
  • if it is realistically feasible from an implementation point of view
  • if there could be side effects in existing documents whose logic specifically relies on this model

Happy to know your opinion!
Thank you.

3 Likes

I’d also maybe add that the Activate() function, as used in this formula:

MyTable.AddRow().Activate()

Is pretty clearly referring to the row that’s created, not the action. To my knowledge it’s not meaningful to “activate” an action.

1 Like

I really don’t have the technical background for the discussion (:innocent:) but as an answer to

this scenario :point_down: is something I would personally use :blush:

Yes, please :blush: :pray: !

Sure, @Pch, think at this (simplified) scenario:

You have a table with a Check button that performs internal row checks (typically, data consistency) and - depending to the conditions - it performs some actions.
For instance, notify the user.

It could easily be:

Table.Filter(...).ModifyRows(...).Check

Now you should do:

Table.Filter(...).FormulaMap(
     RunActions
        currentValue.ModifyRows(...),
        currentValue.Check
    )
)

Or:

Table.Filter(...).withName(filtered,
    RunActions(
        filtered.ModifyRows(..., [set a modification flag]),
        filtered.Filter(modification flag).Check // this can be deferred to an automation
    )
)

As you can see, having a contextual instance of the affected row, would allow to immediately manipulate it.

I hope this is clear.
Cheers!

1 Like

I was typing a long answer here speculating why it’s not possible / not trivial to build this but then realized it wouldn’t benefit the discussion much.

I believe Coda engineers had very valid reasons to implement these the way it is. Action objects are basically Promises, and only a handful of these resolve to a value (AddRow, SendMail etc), and only a handful of actions can actually accept promises and unwrap their values to use in place of plain values (Activate(Row|Promise<Row>), .ModifyRows(col, Any|Promise<Any>, ...)). Making all functions accept promises would be an extra layer of abstraction (even though they already work well with delayed calculations like pack formulas); making all actions return a typed promise could potentially delay action execution (e.g. ModifyRows() would block until all rows are modified, and only then let the rest of your action run). Whatever’s built so far, I think, was just an interim implementation to support the “Open added row” and “Save action result into column” toggles on the UI :slight_smile:

This is all just speculation. I think we need some technical insights from folks like @Jason_Tamulonis .

To answer @Federico_Stefanato 's question, yeah, I often feel a need to do more with an added row than put it in a cell or activate. I don’t want to refer to deleted rows because I’d expect those to be invalid after they’re deleted. As for modified rows, it would be cool to get a List<ref> of these, e.g. for logging purposes, but not so much else. Besides I don’t know if that list should return me a state of rows pre-modification or post-modification. Post-modification sounds like the proper way, but I could probably query it myself anyway.

2 Likes

The thing is: you can always query it so long as you add some information to the newly added rows that will make them identifiable. Sometimes that’s not necessary nor desired.

If I’m adding 10 rows to a massive table, and then right after adding them I want to make them the default contents of a new row that I’m adding to another table, what criteria will I use to reliably look them up from the massive table?

Thanks for your thoughts, @Paul_Danyliuk!

As a matter of fact, deleted rows are already in use when deleting a looked up reference with even the ability to restore them.
I’d therefore expect the affected Row reference to behave consistently.

Anyway, my first intention is to find out if this is a useful approach from the usability and data manipulation perspective.
If it’s a convenient and intuitive way of approaching data for few users only, the second point (implementation complexity) doesn’t even make sense be considered, as you correctly pointed out.

1 Like

:+1: Agree—it would be helpful.

I have noticed that sometimes actions return “:zap: Action” and other times they return the row(s) in question. I have dreamed of taking advantage of this by, for example, storing actions in rows for later execution. But I have never found a use case for that.

What I have wanted to do is immediately retrieve and use modified or created rows, and workarounds like Table.Filter(Modified([currentvalue]) > Now() - Seconds(2)) are really annoying.

I might be doing something wrong, but I use
Table.Row.Last()
Is this not a good idea? I figure that if it is the very next thing in a formula, then its safe? I understand it could create issues if 1000’s of people were using the document at the same time - although in the cases that I’ve used this kind of formula, I only allow adding to the row via a button - so whichever button is currently in progress SHOULD get to the .last() filter first. Or something.

I realise this is more than a year after the last discussion on this topic, but it just so happens that this is a situation I’m dealing with right now…

I’m going to try and stuff up on the other ways you guys work

Hello @Brendan_Woithe ,

What you are doing, if I understand correctly what you are trying to do, is not going to work. Because your formula is going to show (leave out “row” though) the last record and will update continuously to show the last added record.

There are good button formulas that let you edit the added row:

Activate(AddRow([days-from-sample], [days-from-sample].Date, Today()))

With this code you add a row to the table “days-from-sample” and you fill the field “Date” with todays date.

The formula is created automatically if you use the button options “Add Row” and mark “Open for editing”.

You can do something similar for Modify Row.

Yes - I have re read the original thread and realised i misunderstood.

I do (!!) use the last() formula to manipulate newly created rows (especially when adding content about them to other rows) but would welcome some other method. I realise this is only a tiny part of what was being discussed.

For other instances, I end up storing data about the last used row (for modified rows) in user helper tables (one row per user). It’s not ideal.

Dealing with a similar situation now.

WithName(
Table1.AddRow(…),
NewRow,
MyFancyProcessingFormula
)

MyFancyProcessingFormula involves a .ForEach() loop that intends to copy NewRow into many other rows of a different table (its children so to speak). But Because NewRow returns an action, a new NewRow is created with each iteration.

Are there any workaround?

@Jason_Tamulonis : I recall you posted somewhere that this is an issue the team was working on :thinking:?

1 Like