Architecting a Centralized Column Visibility Manager for Dynamic Perspective Tables

I am managing 20+ Perspective Table components across multiple views. Many of these tables are dynamic—their columns change based on query parameters or user selection. I need a scalable, centralized system that allows users to toggle column visibility via a popup, remembers their preferences for the session, and handles dynamic column generation without hardcoding.

Description: I currently use a custom Python module to dynamically generate table columns. I want to extend this system to include a "Column Manager" popup that works for any table in the project.

My Proposed Architecture:

  1. Centralized Storage: A Session Custom Property dictionary session.custom.ColumnPreferences that stores visibility states keyed by a unique tableId (e.g., {"TableA": {"Station": True, "Timestamp": False}}).
  2. Universal Popup: A single View containing a Table component. This table will display the column names and a boolean checkbox. It will be passed a tableId and the current columnNames list as parameters.
  3. The Logic:
  • A helper function applyColumnVisibility(generatedCols, tableId, prefs) that intercepts the column configuration before it is applied to the component.
  • It should default any "newly discovered" dynamic columns to Visible: True if they aren't yet in the user's preference dictionary.

Goal: I want to ensure this is the most performant way to handle this in Perspective. Specifically:

  • Should I be using onEditCellCommit on the popup table to write directly back to the Session prop?
  • Is there a better way to "discover" available columns for the popup than passing them from the parent table's current data?

I'm looking for feedback on this "Hub-and-Spoke" model or any tips on how others have handled persistent column states across large-scale projects.

The centralized storage method more or less lines up with what I have recommended to others in the past for holding similar user specific information, like personalization and such. You could expand it to hold additional data other than just column visibility. (Sorting preferences or similar). Just make sure the top level property is set to private.

One pitfall I could see is if a single table is used for different queries, you could run into a case where column A needs to be hidden for query A, but should be shown for query B. That means you might need to include additional identifier information in your config data to indicate if the column visibility is for the table in general or a specific query on that table.

Writing directly to the session props could leave you with orphaned key/value pairs if you change what columns are available for the table after saving a config, but that's a pretty negligible cost unless you have thousands of orphaned items. You could also run a cleanup operation on the storage table if you really needed/wanted to clean out stale keys.

Are you able to modify your existing function that generates your columns to take an optional userConfig argument and pass the config for the table through that?

Then when you are doing whatever logic to loop through and create the columns, you can check immediately to see if the column should be hidden or not. This would save you from looping over the list of columns again after their generation.

I would still keep the helper for any post generation changes. On submission of new config, pass the existing generated columns, new config, and table Id, and adjust the columns based on the new config.

1 Like

Thanks for the detailed feedback! You hit on a few points I hadn't fully considered yet—specifically the Query-specific identifier and the potential for orphaned keys.

Based on your suggestions, here is my updated plan:

  1. Query-ID Logic: I’ll move away from using just the Table.name as the key. I'll pass a specific configId (e.g., Overview_Alarms vs Overview_History) to the generation script. This ensures the same physical table component can hold different visibility states depending on the data it's currently displaying.
  2. Single-Loop Optimization: I'm refactoring my generateColumns function to accept the sessionPrefs as an optional argument. This allows me to set the visible property and skip unnecessary width calculations for hidden columns in a single pass, rather than looping over the column list twice.
  3. Sticky Persistence: I'll take your advice on using the Session Startup/Shutdown handlers to load/save the JSON from a DB. It seems much cleaner than trying to manage it on every single view.
  4. Orphaned Keys: I agree the overhead is negligible. I’ll just ensure my Column Manager popup only builds its UI based on the availableColumns actually present in the current dataset, which effectively ignores any stale keys in the session dictionary.

I appreciate the tip on setting the session property to private—definitely a 'best practice' I'll be following. Thanks again for the sanity check!