Firing the $OnRemove action when a note is deleted

Hi all,

I notice that the $OnRemove action of a container (or adornment) fires when a note in the container is dragged out, but not when a note in the container is deleted. I think I get why (namely because OnRemove actions have to run on a note, and the note to run it on has just been deleted), but it makes it annoyingly difficult to maintain state within a container without resorting to an agent.

For example, I have an expense prototype, and it would be great to be able to nest expenses so that the amount of the expense is the sum of the amount of its children. I could do this with an agent, of course, but itā€™s even simpler to do with OnAdd and OnRemove actions. However, this gets messed up if you delete a nested expense rather than moving it out of the container. Is there a nice way to do this, or do I just have to use an agent?

How feasible would it be to support firing OnRemove on the note to be deleted just before deleting it?

Thanks!

I should also mention that one of the reasons I would like this functionality is that I like to use ā€œexpense adornmentsā€ which set $DisplayExpression to the sum of the expenses within the adornments. Adornments cannot be queried by agents, and I have not been able to find a group note designator that designates all the notes in an adornment. Is there one?

Given that there are no group note designators that for adornments, and you can use agents to query adornments, and OnAdd and OnRemove have this fragility, is there any way to add this functionality to an adornment?

Thanks!

Iā€™m not sure that remove and delete are the same intent, even if treating them thus solves the particular problem. IOW, if taken forward, Iā€™d suggest ā€˜OnDeleteā€™ being a separate event/attribute rather then be part of ā€˜OnRemoveā€™. Otherwise youā€™d have to check and remove any $OnRemove before you could safely delete a note without firing unwanted code.

Sidenote: ā€˜OnCreateā€™ and ā€˜OnAddā€™ are essentially the same thing albeit the only way to set a create event for root notes is to set the doc default for $OnAdd, though you could use an if (if($OutlineDepth==1){$Color="red"}).

  1. To compute the current some of $Amount in a container of Expenses, define a rule on Expenses or use an agent.

  2. Weā€™ll look into firing OnRemove before the note is deleted.

  3. To find all the notes atop an adornment: find(inside(/path/to/TheAdornment))

1 Like

Hmm, my thought is that deleting is just another way to remove a note from a container, so it seems strange to separate them, though what you describe would be necessary to maintain the current behavior. Out of curiosity, can you think of an example when you wouldnā€™t want $OnRemove code to fire when deleting a note?

Yes but one results in firing the OnAdd of another container and one doesnā€™t. So, it all depends on context. If the in/out actions only ever affect the containerā€™s values (counts, tag lists, etc.) then the two might be similar. However, one might use action where on containersā€™s OnRemove is balanced by an OnAdd action in the receiving container. Tinderbox is an open ended tool and itā€™s useful to consider if the result of fixing the problem at hand makes other things more difficult, e.g. someone having problems because deletions are firing a remove action when thatā€™s not appropriate. Having separate Remove and Delete actions would fix your problem, just in a more flexible way.

In the meantime, hereā€™s a workaround for your delete problem. Make yourself a discard container, e.g. at path ā€˜/binā€™. Now make a stamp with the action:

$Container = "/bin";

Now, when you would delete an expense item, select it and use the stamp, which moves the note and thus fires OnRemove. Then, as often as necessary, select the bin container and delete the contents

Considerations in the design here:

  • Using a root-level disposal container moves the notes out of any other part of the general outline (except if you use a root level map) and thus likely out of scope of inside() or descendedFrom() queries.
  • The ā€˜binā€™ has an OnAdd via which you might want to apply further cleaning actions. For instance, you might want the removed note to lose its prototype and to reset $Price as either might otherwise meet existing queries. Thus an $OnAdd code might be: $Prototype=;$Price=;
1 Like

Hi Mark ā€” thanks for the tip.

Do you have any idea why setting an adornmentā€™s $HoverExpression to count(find(inside(/path/to/adornment))) would show the correct count of items in the adornment, but setting it to count(find(inside($Path))) would always show 0? I.e., hardcoding the path works, but using the $Path attribute doesnā€™t?

Thanks!

Thanks, Mark ā€” this is also just nice for preventing accidental deletions. Unfortunately I feel itā€™s insufficient for the expenses example, because in a proper expense application there should be no way for the totals to be wrong, and there is still the possibility of the container or adornmentā€™s total to get into a funky state, if I forget to use the ā€œmove to /binā€ stamp, or simply delete an expense by accident. Looks like Iā€™ll have to handle this with agents or rules to ensure consistency.

inside() doesnā€™t allow another layer of indirection; it expects a designator but not an expression for its argument. This is a question of efficiency.

If this is the hover expression for the adornment, the designator that will be bound to the adornment. So that will take care of the issue.

1 Like

Thanks to all for the responses here!

I ended up (correctly, I think) abandoning the $OnAdd and $OnRemove approach for my ā€œexpense adornments,ā€ and using the following $Rule on the adornments instead:

$MyNumber=sum(find(inside(that)),$MyNumber);

This seems much less error prone, and is nice and simple.

In the course of coming up with this, I came across some strange behavior that strikes me as a bug, but Iā€™ll post about that in another thread so as to not confuse matters further.

Thanks again!