“T-SQL dot app” Multi-Step Forms Implementation Guide
Pizza order APP
Core Concepts
-
State Management
- Variables persist between steps using JSON
- Client-server synchronization via
sp_api_modal_get_value
- Step tracking through numerical indicators
-
Flow Control
- Each step contained in separate IF block
- Forward/backward navigation through modal restart
- Clean return statements prevent code bleed
Best Practices
- Always declare all variables at start
- Always use Unicode NVARCHAR variables for string concatenation
- Always use N'' for string literals
- Prepare variables before any concatenation or stored procedure calls
- Return after modal restart
- Use btn-success/btn-warning for toasts
Variable Management
-- Declare all variables at start
DECLARE @Step INT;
DECLARE @Input1 NVARCHAR(100);
DECLARE @Input2 NVARCHAR(100);
DECLARE @DisplayText NVARCHAR(MAX);
DECLARE @NextButton NVARCHAR(100);
DECLARE @JsonValues NVARCHAR(MAX);
-- Sync all required values
EXEC sp_api_modal_get_value @name='@Step', @value=@Step OUT;
EXEC sp_api_modal_get_value @name='@Input1', @value=@Input1 OUT;
State Transitions
-- Forward Navigation
SET @JsonValues = (
SELECT
[@Step] = @CurrentStep + 1,
[@Input1] = @Input1,
[@Input2] = @Input2
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
EXEC sp_api_modal_restart @values = @JsonValues;
-- Backward Navigation
SET @JsonValues = (
SELECT
[@Step] = @CurrentStep - 1,
[@Input1] = @Input1
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
EXEC sp_api_modal_restart @values = @JsonValues;
Error Handling
-- Validate step range
IF @Step < 1 OR @Step > 3
BEGIN
SET @Step = 1;
END
-- Validate required inputs
IF @Step = 2 AND @Name IS NULL
BEGIN
SET @Message = N'Please complete step 1 first';
EXEC sp_api_modal_text @text=@Message;
SET @JsonValues = (
SELECT [@Step]=1 FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
EXEC sp_api_modal_restart @values=@JsonValues;
RETURN;
END
Complete Example: Pizza Order Form
This example demonstrates a multi-step form with proper variable management, state transitions, and user feedback.
DECLARE @Step INT;
DECLARE @Size NVARCHAR(20);
DECLARE @Toppings NVARCHAR(500);
DECLARE @DeliveryNote NVARCHAR(200);
DECLARE @NextButton NVARCHAR(100);
DECLARE @BackButton NVARCHAR(100);
DECLARE @Title NVARCHAR(MAX) = N'🍕 Epic Pizza Adventure 🍕';
DECLARE @Message NVARCHAR(MAX);
DECLARE @StepTitle NVARCHAR(MAX);
DECLARE @StepText NVARCHAR(MAX);
DECLARE @JsonValues NVARCHAR(MAX);
DECLARE @Placeholder NVARCHAR(200);
EXEC sp_api_modal_get_value @name='@Step', @value=@Step OUT;
EXEC sp_api_modal_get_value @name='@Size', @value=@Size OUT;
EXEC sp_api_modal_get_value @name='@Toppings', @value=@Toppings OUT;
EXEC sp_api_modal_get_value @name='@DeliveryNote', @value=@DeliveryNote OUT;
IF @Step IS NULL SET @Step = 1;
IF @Step = 1
BEGIN
SET @StepTitle = @Title;
SET @StepText = N'Choose your pizza size (choose wisely, your stomach is watching!)';
SET @Placeholder = N'Small/Medium/ABSOLUTELY MASSIVE';
EXEC sp_api_modal_text @text=@StepTitle, @class='h3';
EXEC sp_api_modal_text @text=@StepText;
EXEC sp_api_modal_input @name='@Size', @value=@Size OUT, @placeholder=@Placeholder;
EXEC sp_api_modal_button @name='@NextButton', @value=N'To infinity... and beyond! ➡️', @valueout=@NextButton OUT;
IF @NextButton IS NOT NULL AND @Size IS NOT NULL
BEGIN
SET @Message = CASE @Size
WHEN N'Small' THEN N'Are you sure? You seem hungrier than that! 🤔'
WHEN N'Medium' THEN N'Playing it safe, I see! 👍'
WHEN N'ABSOLUTELY MASSIVE' THEN N'Now THAT''S what I''m talking about! 🦖'
ELSE N'Size recorded (but you''re being creative with the options! 😅)'
END;
EXEC sp_api_toast @text=@Message, @class='btn-success';
SET @JsonValues = (
SELECT [@Step]=2, [@Size]=@Size
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
EXEC sp_api_modal_restart @values=@JsonValues;
RETURN;
END
END
IF @Step = 2
BEGIN
SET @StepTitle = @Title;
SET @StepText = N'Time for toppings! (No pineapple judgment here... much 🍍)';
SET @Placeholder = N'Go wild! Mix and match!';
EXEC sp_api_modal_text @text=@StepTitle, @class='h3';
EXEC sp_api_modal_text @text=@StepText;
EXEC sp_api_modal_input @name='@Toppings', @value=@Toppings OUT, @type=N'textarea', @placeholder=@Placeholder;
EXEC sp_api_modal_button @name='@BackButton', @value=N'⬅️ Oops, wrong size!', @valueout=@BackButton OUT;
EXEC sp_api_modal_button @name='@NextButton', @value=N'These toppings are perfect! ➡️', @valueout=@NextButton OUT;
IF @BackButton IS NOT NULL
BEGIN
SET @JsonValues = (
SELECT [@Step]=1, [@Size]=@Size
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
EXEC sp_api_modal_restart @values=@JsonValues;
RETURN;
END
IF @NextButton IS NOT NULL AND @Toppings IS NOT NULL
BEGIN
SET @Message = CASE WHEN @Toppings LIKE N'%pineapple%'
THEN N'Brave choice with the pineapple! 🍍😅'
ELSE N'Toppings locked in! Looking tasty! 🤤'
END;
EXEC sp_api_toast @text=@Message, @class='btn-success';
SET @JsonValues = (
SELECT [@Step]=3, [@Size]=@Size, [@Toppings]=@Toppings
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
EXEC sp_api_modal_restart @values=@JsonValues;
RETURN;
END
END
IF @Step = 3
BEGIN
SET @StepTitle = @Title;
SET @StepText = N'Any special delivery instructions? (Wrong answers only! 😉)';
SET @Placeholder = N'E.g., Please have delivery person yodel the theme from Rocky...';
EXEC sp_api_modal_text @text=@StepTitle, @class='h3';
EXEC sp_api_modal_text @text=@StepText;
EXEC sp_api_modal_input @name='@DeliveryNote', @value=@DeliveryNote OUT, @type=N'textarea', @placeholder=@Placeholder;
EXEC sp_api_modal_button @name='@BackButton', @value=N'⬅️ Those toppings were a mistake!', @valueout=@BackButton OUT;
EXEC sp_api_modal_button @name='@NextButton', @value=N'Review this masterpiece! 🎨', @valueout=@NextButton OUT;
IF @BackButton IS NOT NULL
BEGIN
SET @JsonValues = (
SELECT [@Step]=2, [@Size]=@Size, [@Toppings]=@Toppings
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
EXEC sp_api_modal_restart @values=@JsonValues;
RETURN;
END
IF @NextButton IS NOT NULL
BEGIN
SET @JsonValues = (
SELECT [@Step]=4, [@Size]=@Size, [@Toppings]=@Toppings, [@DeliveryNote]=@DeliveryNote
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
EXEC sp_api_modal_restart @values=@JsonValues;
RETURN;
END
END
IF @Step = 4
BEGIN
SET @StepTitle = CONCAT(@Title, N' - Final Review');
SET @StepText = N'🎯 Your Epic Pizza Order:';
EXEC sp_api_modal_text @text=@StepTitle, @class='h3';
EXEC sp_api_modal_text @text=@StepText, @class='h4 mt-3';
SET @Message = CONCAT(N'Size: ', @Size,
CASE @Size
WHEN N'Small' THEN N' (Really? Just a small? 🤏)'
WHEN N'Medium' THEN N' (Classic choice! 👌)'
WHEN N'ABSOLUTELY MASSIVE' THEN N' (Now we''re talking! 🦕)'
ELSE N' (Creative size choice! 🎨)'
END);
EXEC sp_api_modal_text @text=@Message;
SET @Message = CONCAT(N'Toppings: ', @Toppings,
CASE WHEN @Toppings LIKE N'%pineapple%'
THEN N' (You brave, brave soul! 🍍✨)'
ELSE N' (Excellent choices! 🌟)'
END);
EXEC sp_api_modal_text @text=@Message;
SET @Message = CONCAT(N'Delivery Instructions: ', @DeliveryNote,
CASE WHEN LEN(@DeliveryNote) > 50
THEN N' (Our delivery person is practicing as we speak! 🎭)'
ELSE N' (Keeping it simple, eh? 📦)'
END);
EXEC sp_api_modal_text @text=@Message;
EXEC sp_api_modal_button @name='@BackButton', @value=N'⬅️ Wait, let me fix something!', @valueout=@BackButton OUT;
EXEC sp_api_modal_button @name='@NextButton', @value=N'Yes! Make this pizza reality! 🎨', @valueout=@NextButton OUT;
IF @BackButton IS NOT NULL
BEGIN
SET @JsonValues = (
SELECT [@Step]=3, [@Size]=@Size, [@Toppings]=@Toppings, [@DeliveryNote]=@DeliveryNote
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
EXEC sp_api_modal_restart @values=@JsonValues;
RETURN;
END
IF @NextButton IS NOT NULL
BEGIN
SET @Message = N'🎉 Order confirmed! Our pizza artists are crafting your masterpiece while practicing their delivery performance! 🎭🍕';
EXEC sp_api_toast @text=@Message, @class='btn-success';
EXEC sp_api_modal_clear;
RETURN;
END
END
Screenshot of realized app
See how this complete app is realized with only the few lines of code which we showed earlier!
Key Implementation Notes
-
Variable Preparation
- Declare all variables at start
- Set values before sp_api calls
- Use descriptive variable names
-
State Management
- JSON for state persistence
- Clear step progression
- Validation at each step
-
User Experience
- Consistent feedback
- Clear navigation
- Engaging messages
- Final review step
-
Error Prevention
- Input validation
- Step validation
- Clean state transitions