Table vs RG setup and performance

I’m stuck on whether/how to use the new Table element. I want to show a grid, which would seem to be a Table use case. However my grid has a variable size in both directions, both vertically (one row per player on a field) and horizontally (number of periods in the practice).

Annotated screenshot attached.

Background: the screen shows players in an afternoon sports practice and tells them what jersey color they should wear in each period. The club manager uses this to assign colors ahead of the practice and then publishes the grid to the players.

Database types I have:

Practice object

  • has details about the practice like location

Practice Row object, ie players that have opted into this practice

  • which player
  • which practice

Practice Cell object

  • which Practice Row
  • which Period (an option set of integers 1 to 12)
  • which Color (an option set with R / B / G / W / and empty)

Two questions.

Question 1 - should I be implementing a Table instead of two nested RGs?

I currently do one RG to create the rows. And then inside each, I create another RG to go horizontally for the cells. The horizontal RG uses an option group that has integers 1-12 so that it creates a full grid of cells even if no cell entries exist yet for each player.

I have a separate RG for the column headers.

This all seems like a perfect use case for a Table. Except I couldn’t figure out how to make the table columns repeating. Tables seem like they’re designed to be variable only in the rows direction.

Question 2: what workflow architecture to make performance not suck?

The user flow is the manager taps a cell to cycle between the color assignments. The problem is each tap takes >1 sec to complete because the workflow logic is so cumbersome. Right now the workflows are:

  • When clicked, do a search to see if the database has a value already for that cell, and if not make it R
  • When clicked, do a search to see if the database has an “R” value already for that cell, and if change that value to “B”
  • When clicked, do a search to see if the database has a “B” value already for that cell, and if so change that value to “G”
  • …etc, one for each color option

Each workflow is 3 database hits, and each click triggers 6 workflows, so it seems like a lot of things happening each click and thus a poor design.

Thank you in advace! Really loving Bubble so far. Coming from a django background.

Hi @twain!

With regards to question #2, it sounds like you could simplify things by creating an option set for shirt colors, which would be fetched exclusively on page load and stored client-side. No need to keep querying the database for something as lightweight as a static list of several letters.

I’d create an option set called “Shirt Colors” with options “None”, “Red”, “Blue”, “Green”, “White” as the displays. Then create two additional attributes, called “Letter” (text) and “Order” (number).

For the four color options, the “Letter” attribute values would be text fields with “R”, “B”, “G”, and “W”. The value for “None” would be “-”.

For the “Order” attribute, assign each option a value of 1-5, in whatever order you want them to cycle (it sounds like “None” would be “1”).

Quick aside: you could get even fancier if you wanted and add a “Swatch” attribute (image) for each option, and upload an image (eg: a plain blue/red/green/white square) so your grid actually displays the colors themselves in addition to the letters (set the image dynamically as the background for a Group element inside each cell, and put the letter’s Text element inside that Group).

Set up a new field on whatever data type you’re displaying in the cells where the field type is the “Shirt Color” option set, and set the default value to “None”. Then create a group inside each cell where the data type is “Shirt Color” and the Data Source is “This Cell’s Thing’s Shirt Color”. Set the text (and image, if you’ve done that) inside the group to be Parent Group’s Shirt Color’s Letter, Parent Group’s Shirt Color’s Swatch, etc.

Then, you really just need two workflows, which involve far fewer conditionals and are much easier to manage:

  1. When clicked, if cell’s Thing’s Shirt Color’s “Order” value < 5, set that cell’s Things’ Shirt Color to the option whose “Order” value is the current Shirt Color’s Order value +1
  2. When clicked, if cell’s Thing’s Shirt Color’s “Order” value = 5, set that cell’s Things’ Shirt Color to the options whose “Order” value is “1”

Even if you have to use advanced filtering to make these workflows function, the set of options/data they’d be computing are so tiny that it’s very unlikely to cause any performance hiccups. It’s exactly the sort of lightweight data manipulation for which client-side processing is most performant.

Outcome is: the user clicks on a cell, the “Shirt Color” option set value associated with that cell’s Thing iterates by 1 to the next option (or resets to the first), and the cell’s contents inherit the attributes of that option.

@josh50 thank you for the repsonse. It all makes sense until the last step:

  • Option set for colors, with index – check!
  • Displaying the current color – check! (Note: I’m doing the layout with a table, not a pair of nested RGs. I can’t figure out how to make the columns repeating if the rows are already repeating, so right now the columns are hardcoded “1” “2” etc for each period.)
  • Conditional workflow logic using “if index < X” – brilliant

OK here’s the challenge, hopefully minor. It’s the “set that cell’s Things’ Shirt Color” step. There doesn’t appear to be a “This Thing’s” option: (see screenshot)

Here is what the parent setup looks like. Each cell is a group with a text element inside it. The group’s value is set using a search that inherits the row, and looks for a hardcoded period number (in this case 1). The hardcoding makes me cringe but I don’t see a way around it when using the table element yet.

@twain Yes, minor issue, and my apologies for not thinking far enough ahead to anticipate it!

Instead of using a regular Group inside your table cells, you’ll want to create a Reusable Element, which will have all of the workflows embedded. Make the RE’s Type of Content your Shirt Color options set, and then create an additional property on the element with a dynamic value and a content type of “Player” (or whatever you’re calling this Thing you want to modify). You won’t assign it a value now, but when you embed it in the table, you’ll dynamically assign it to inherit that row’s player.

The Reusable Element gives you the best of both worlds. It essentially functions as its own page with its own workflows/rules, no matter were you plop it on another page, but it can also communicate with the page it’s placed on.

Here’s what the element set-up and workflows might look like:

The workflow would trigger when the Reusable Element in a cell is clicked. And to reiterate: you would build this workflow on the Reusable Element’s workflow page, NOT on the page with your table.


When you place the element in your table’s cells, THEN you’ll have the option to assign the dynamic value of the Player property to that row’s Player, as well as fiddle with the sizing/layout until it fills the cell in the manner that ensures the user’s tapping action will register and kick off the workflow.

I think that should get you all the way there, but let me know if you run into any other issues, and good luck with the build!

@josh50 thank you for sticking with me on this, super helpful

Fascinating - I didn’t know about Reusable Elements yet.
So, before I do the workflow to change assigned colors, I think I need to get them just showing the colors first. But I’m a bit stuck on the exact type to use when embedding the RE.

When making the RE: I think the type for the RE should be a PracticeCell, not a Color, correct? The text inside the RE can then reference the parent’s color. And is this where I put the other attributes like Player and which period it is?

Embedding the RE: what should the “data source” be when using an RE? I can do a search for PracticeCell, where player = that row’s player, period = that column’s period, etc. But that seems redundant with what’s happening with the attributes on an RE. Not sure what roles these are supposed to play.

@twain There’s always more than one way to do things in Bubble. Giving the RE the type “Practice Cell” is definitely a workable solution. If your “Practice Cell” Data Type already includes the Player, Period, and Color as fields, then you don’t need to specify them as properties on the Reusable Element at all.

Creating the Reusable Element:

  • Set the Type of content to “Practice Cell”
  • Put a text inside that displays “Parent group’s Color”
  • Build the “ordinal color iteration when clicked” workflow that we’ve already discussed.

Implementing the Reusable Element:

  • Place it in the table (once in each Period column)
  • Data source = do a search for PracticeCell, where player = that row’s player, period = that column’s period, etc

That ought to do it. However…

I know we’re focusing on the current functionality issue right now, but if this app is a long-term project for you, I’d actually suggest going back and reconsidering your database structure. The Data types you’ve created to represent “junctions” of two things would be better handled by just creating an additional field on the first thing. For example, why have a “Practice Row” data type that specifies a junction of players with one specific practice, when you could simply create a field on the Practice thing called “Players” that is a list of type Player. When you create a table or RG where you want to show players for that practice, you just give “This Practice’s Players” as the data source, rather than doing an entirely separate database search. Similarly, you could create a field of type “Practice” on the Player thing, to easily recall all the practices in which they’re scheduled to participate.

The only true “junction” data type you need to create is what I would call something like “Practice Assignments” (the role your “Practice Cell” is sort of covering now). That Data type should have fields for the Practice and Player Things, a field for Period (create an Option Set “Period” where the options are integers from 1-12), and a field for your existing Shirt Color option set). Then, your whole interface on the page for a given practice could be a single table or RG that just displays data type “Practice Assignments” where the source is "do a search for Practice Assignments where Practice is the parent group’s practice.

I think this restructuring would set you up for a much easier development process moving forward, though I realize it may involve some short term pains given everything you’ve already built. Just didn’t want to leave that issue unspoken.