Modal State Persistence Patterns
Introduction to Modal State Management
In TSQL.APP, actions often span multiple user interactions through modal interfaces. A common challenge is maintaining consistent state across these interactions, especially when business logic depends on preserving context between modal refreshes, user input, and navigation events.
This section outlines proven patterns to ensure reliable state management in modal-based TSQL.APP applications.
Pattern 1: Complete State Model
Key Concept
Store a complete representation of application state in JSON format during modal interactions rather than relying on individual variables.
Implementation
-- 1. Declare a JSON state variable
DECLARE @JsonState NVARCHAR(MAX);
-- 2. Create comprehensive state using FOR JSON
SET @JsonState = (
SELECT
[@PropertyA]=@VariableA,
[@PropertyB]=@VariableB,
[@PropertyC]=@VariableC
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
-- 3. Pass complete state during transitions
EXEC sp_api_modal_restart @values=@JsonState;
Benefits
- Atomic state updates prevent inconsistencies
- Complete context preservation between interactions
- Single source of truth for application state
- Simplified debugging (examine one JSON value instead of many variables)
When to Use
- Multi-step processes where context must be preserved
- Applications with interdependent data elements
- When state values must remain synchronized (e.g., original/transformed data pairs)
- Workflow applications with multiple possible paths
Pattern 2: State Synchronization Points
Key Concept
Define explicit points in your code where state is synchronized between variables, modal UI, and JSON persistence.
Implementation
-- 1. Retrieve all state at the beginning
DECLARE @p_name NVARCHAR(MAX);
SET @p_name = N'@state';
EXEC sp_api_modal_get_value @name=@p_name, @value=@JsonState OUT;
-- 2. Parse individual values from JSON if needed
-- (This can be done through direct variable synchronization instead)
SET @p_name = N'@PropertyA';
EXEC sp_api_modal_get_value @name=@p_name, @value=@VariableA OUT;
-- 3. Synchronize state at key transition points
-- After state-changing operations:
SET @JsonState = (
SELECT /* updated state values */
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
-- 4. Update state variable without full modal restart when appropriate
SET @p_name = N'@state';
SET @p_value = @JsonState;
EXEC sp_api_modal_value @name=@p_name, @value=@p_value;
Benefits
- Ensures UI and business logic remain synchronized
- Prevents "lost update" scenarios
- Maintains predictable application behavior
- Isolates state changes to well-defined points
When to Use
- Time-sensitive operations (e.g., countdown timers)
- Multi-value form validation
- Operations with automatic refreshes
- When modal state might change from multiple entry points
Pattern 3: Atomic State Transitions
Key Concept
Treat state transitions as atomic operations where the complete state is updated in a single operation, followed by immediate termination of the current procedure execution.
Implementation
-- Define transition with complete state update
IF @Condition = 1
BEGIN
-- 1. Prepare new state
SET @NewState = N'newState';
-- 2. Update all dependent variables
SET @VarA = @NewValueA;
SET @VarB = @NewValueB;
-- 3. Create complete JSON state
SET @JsonState = (
SELECT [@State]=@NewState, [@VarA]=@VarA, [@VarB]=@VarB
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
-- 4. Execute modal restart with complete state
EXEC sp_api_modal_restart @values=@JsonState;
-- 5. Terminate current execution to prevent further state changes
RETURN;
END
Benefits
- Eliminates partial state updates
- Prevents race conditions
- Creates clean, predictable transitions
- Simplifies debugging and testing
When to Use
- When transitioning between distinct application states
- For conditional navigation paths
- When generating new data that must be synchronized
- Whenever modal content needs to be completely refreshed
Pattern 4: Hidden State Variables
Key Concept
Use non-displayed variables to maintain complete context that's not directly visible to users but essential for application logic.
Implementation
-- Store complete context in hidden variable
DECLARE @p_name NVARCHAR(MAX);
DECLARE @p_value NVARCHAR(MAX);
-- Original/transformed data pair example
SET @OriginalData = N'SAMPLE';
SET @TransformedData = N'ELPMAS'; -- Reversed for display
-- Store both in state
SET @JsonState = (
SELECT
[@OriginalData]=@OriginalData,
[@TransformedData]=@TransformedData,
[@OtherState]=@OtherState
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
-- Store complete state in hidden variable
SET @p_name = N'@state';
SET @p_value = @JsonState;
EXEC sp_api_modal_value @name=@p_name, @value=@p_value;
-- Only display transformed data to user
SET @p_name = N'@displayData';
SET @p_value = @TransformedData;
EXEC sp_api_modal_input @name=@p_name, @value=@p_value;
Benefits
- Maintains complete application context
- Separates display concerns from data model
- Enables complex validation against hidden values
- Improves security by keeping sensitive data out of the UI
When to Use
- For data transformation/normalization scenarios
- When maintaining original/processed data pairs
- In validation scenarios where reference data should be hidden
- For maintaining comprehensive audit trail
Pattern 5: Race Condition Prevention
Key Concept
Identify and eliminate windows where state could become inconsistent due to timing issues or partial updates.
Common Race Conditions
- Display/Validation Mismatch: What user sees doesn't match what system validates against
- Lost Updates: Changes made by one process overwritten by another
- Partial State: Only some variables updated before an interruption
- Stale References: Using outdated references after state has changed
Implementation
-- INCORRECT: Race condition due to separate updates
IF @NewWord IS NULL
BEGIN
-- Generate new word
SELECT TOP 1 @NewWord = Word FROM @WordBank ORDER BY NEWID();
SET @ScrambledWord = REVERSE(@NewWord);
-- Problem: State could be interrupted here, leaving inconsistent state
END
-- Display scrambled word to user
EXEC sp_api_modal_text @text=@ScrambledWord;
-- CORRECT: Atomic update pattern
IF @NewWord IS NULL
BEGIN
-- Generate new word
SELECT TOP 1 @NewWord = Word FROM @WordBank ORDER BY NEWID();
SET @ScrambledWord = REVERSE(@NewWord);
-- Immediately update state and restart
SET @JsonState = (
SELECT [@NewWord]=@NewWord, [@ScrambledWord]=@ScrambledWord, [@OtherState]=@OtherState
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
EXEC sp_api_modal_restart @values=@JsonState;
RETURN; -- Critical: prevents further execution
END
-- Now display scrambled word with confidence it's synchronized
EXEC sp_api_modal_text @text=@ScrambledWord;
Benefits
- Eliminates timing-dependent bugs
- Creates predictable user experiences
- Prevents data corruption or inconsistency
- Simplifies debugging
When to Use
- When variables have dependencies on each other
- In time-sensitive operations
- For operations requiring multiple modal refresh cycles
- In any application with paired data (original/transformed)
Pattern 6: State Debugging Techniques
Key Concept
Implement debugging aids during development to visualize and verify state consistency.
Implementation
-- Option 1: Conditional state display
IF @DebugMode = 1
BEGIN
SET @p_text = CONCAT(N'Debug - State: ', @CurrentState,
N', Original: ', @OriginalData,
N', Transformed: ', @TransformedData);
SET @p_class = N'text-muted small';
EXEC sp_api_modal_text @text=@p_text, @class=@p_class;
END
-- Option 2: Commented debug statement for development
-- Uncomment during development/testing
/*
SET @p_text = CONCAT(N'Debug - Complete state: ', @JsonState);
SET @p_class = N'text-muted small';
EXEC sp_api_modal_text @text=@p_text, @class=@p_class;
*/
-- Option 3: Hidden state visualization that can be inspected in browser
SET @p_name = N'@debug_state';
SET @p_value = @JsonState;
EXEC sp_api_modal_value @name=@p_name, @value=@p_value;
Benefits
- Makes state issues visible during development
- Accelerates troubleshooting
- Confirms state validity at key points
- Helps identify synchronization issues
When to Use
- During initial development
- When troubleshooting state inconsistencies
- For complex multi-step processes
- When implementing new state transitions
Case Study: Word Scramble Game
The following example demonstrates these patterns in a game scenario where maintaining state consistency is critical for proper functionality:
-- Game state transitions with complete state model
IF @GameState = 'round' AND @CurrentWord IS NULL
BEGIN
-- Select a new word
SELECT TOP 1 @CurrentWord = Word FROM @WordBank ORDER BY NEWID();
-- Create scrambled version
SET @ScrambledWord = REVERSE(@CurrentWord);
-- Atomic state transition with complete state
SET @JsonState = (
SELECT
[@GameState]=@GameState,
[@CurrentWord]=@CurrentWord,
[@ScrambledWord]=@ScrambledWord,
[@Score]=@Score,
[@TimeLeft]=30
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
-- Restart with complete state before displaying anything
EXEC sp_api_modal_restart @values=@JsonState;
RETURN;
END
-- Handle answer submission with validation against stored state
IF @SubmitButton IS NOT NULL
BEGIN
-- Validate against STORED word (not a newly generated one)
IF UPPER(@Answer) = UPPER(@CurrentWord)
BEGIN
SET @IsCorrect = 1;
SET @Score = @Score + (@TimeLeft * 10);
END
ELSE
BEGIN
SET @IsCorrect = 0;
END
-- Transition to result state atomically
SET @GameState = 'result';
SET @JsonState = (
SELECT
[@GameState]=@GameState,
[@CurrentWord]=@CurrentWord,
[@ScrambledWord]=@ScrambledWord,
[@Answer]=@Answer,
[@IsCorrect]=@IsCorrect,
[@Score]=@Score,
[@TimeLeft]=@TimeLeft
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
EXEC sp_api_modal_restart @values=@JsonState;
RETURN;
END
Best Practices Summary
- Always store complete state in JSON format rather than relying on individual variables
- Implement atomic state transitions that update all related variables together
- Define clear synchronization points where state is synchronized between variables, UI, and persistence
- Terminate execution after state transitions using RETURN to prevent inconsistencies
- Use hidden state variables for maintaining complete context
- Implement debugging visualizations during development to verify state consistency
- Identify potential race conditions and eliminate them through atomic transitions
Following these patterns will result in more reliable, predictable TSQL.APP applications with fewer state-related bugs and improved maintainability.