I have an app that - based on user input - create and show many ‘cards’ (GridPanel) that contains a main component (for instance a TextArea component), a side menu (Linear Panel) of actions that can be done to the card (delete, move up etc) and a sub-menu (Flex container) with buttons like ‘translate the text’.
Everything is working. For instance, when users click the button ‘translate’ on a card the function that is linked to the event knows the button and use parent and get_components() to get to the current text in the textbox on the same card. It looks something like this:
btn_clicked = event_args['sender']
sub_menu= button_clicked.parent
card=sub_menu.parent
text_box=card.get_components()[0]
text=text_box.text
# (and now, having captured the text on the card that was clicked, I can translate it and return it)
Although this works, it seems overly complex and vulnerable to bugs. I have to remember the positioning of the different elements of the card and the nested structure. And if I change the structure when I build the card, the function no longer works. And it gets even worse for some buttons that are deeply nested on the card.
So: I must have overlooked something? There must be an easier way? Or?
Thank you! Yes, messaging is great (Finding anvil extras was like finding a hidden treasure!), but I am not sure it solves this specific problem.
The translate button does not know the text on the card and there are mutiple cards with text that may change all the time (a text area). So when the button on a card is clicked it has to examine the current state of the textbox on that specific card. And that seems to require nesting back.
Let me think about it. Yes, I could open a communication channel that emits the text from a card (every time it is changed?) and the button may have access to this (but is still has to know which card to translate. Every card has a translate button and only the text from that card should be translated when it is clicked.)
You could use a tag on each button to link it to the relevant card.
A radically different solution - impossible now I think - would be to get a flattened list of all components given n-parent steps up in the container tree - and then be able to get a component using a name (if components had reference names - and these names did not necessarily have to be unique).
Wait! I guess this could be implemented using tags on components and a few simple functions.
- A function that recursively keeps taking .parent on a given component until it hits a component that has ‘card’ as its tag.card_type (as opposed to side_menu or sub_menu card_types)
- A function that creates a flattened list of all the (nested) components on the card and identifies the card with the tag we seek (main_content, text or whatever we specify).
The tag solution could work! And it is more general, flexible, less confusing compared to the current ‘remember-the-nested-structure-and-position-and-never-change-it’-approach of parent and get_components().
Thank you for making me think.
How are the cards and other components created?
If you create them from code, then you could keep track of them in a global dictionary, so you can find them, no matter where you are.
Otherwise you could setup events. We would need more details to see if events could be setup.
The cards and all components are created in code.
Using tags improved the situation. It is still not optimal, but good enough. A global dictionary could also work. Thank you!
By the way: A deeper source of the problem is, perhaps, that I have experimented with a design approach that would require as little ‘tracking’ as possible.
For instance, I placed the cards in a LinearPanel which meant that each card could have a delete button that did not require the cards to have a unique id. When a the delete button on a card was pressed, the app could identify the card that was clicked by the index position in the LinearPanel (using event_args[‘sender’], .parents, get_components() and index).
While this saved quite a lot of hassle and boilerplate, it created other problems - like the one described above. Using tags, id, and keeping track in a global dictionary and so on is probably a better idea in the long term.
1 Like
I just added a paragraph to the test-driven development page about my approach to rendering the form.
It may be too late for your app at this point, but I mention anyway, it could help you in future apps.
With my approach components don’t need to know each other because they all show what’s in the global singleton. So in your example, when you type something on a textbox, the textbox immediately updates a property of the singleton. When you click on the translate button, the button calls the translate()
method of the singleton, then refreshes the whole form. When the form refreshes reads the data from the singleton that has already been translated.
1 Like
This sounds like a variation of the Whiteboard metaphor.
Imagine a big problem, and a bunch of experts tasked to solve it. Each expert alone can’t solve the whole problem, but they can solve a piece of it. Each subproblem’s solution stimulates one or more experts to contribute, and push closer to a total solution, filling in other missing pieces. In this way, they work out their solution together, on the whiteboard, by taking turns. When they’re done, a “referee” collects the finished solution.
This is generally done when the sequence of collaboration isn’t known in advance.
You might now want to look at the brand new atomic module in Anvil Labs
2 Likes
This look very promising and useful. I am impressed!
2 Likes