An action script keeps no variables between runs. Every click starts a fresh batch, so anything you need on the next run has to travel there explicitly. TSQL.APP gives you four routes, and choosing the right one is most of the work.
| You need the value… | Use |
|---|---|
| on the next run of the same dialog | a named control — it travels automatically |
| on a fresh run of the same action | sp_api_modal_restart @values = … |
| in a different action | sp_api_modal_restart @action_name = …, @values = … |
| next week, for the same user | sp_api_modal_set / sp_api_modal_get |
And one route you never have to build: the context variables. @id, @ids, @card_name,
@user and the others are already set when your script starts. Never declare them yourself.
1. Within a dialog: named controls
Every control with a @name sends its value back on the next run. Reading it is one call:
EXEC sp_api_modal_get_value @name = N'@Quantity', @value = @Quantity OUT;
This covers most needs. The name is the key: give every control its own name and its own variable.
Pickers are the exception. sp_api_modal_select, sp_api_modal_select_table and
sp_api_modal_choose read their own posted value when they are drawn and return it through
their @value OUT parameter — the chosen id, not the raw posted data. For those, the call that
draws the control is the synchronisation.
2. Across a restart: @values
When the script restarts itself — typically to move a wizard forward — the values to carry go in a JSON object. Each key is the name the next run will read:
SET @Json = (
SELECT [@Step] = 2, [@CustomerId] = @CustomerId, [@OrderDate] = @OrderDate
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
);
EXEC sp_api_modal_restart @values = @Json;
RETURN;
On the next run, sp_api_modal_get_value @name = N'@CustomerId' returns the value, exactly as if
the user had typed it. Only what you put in the JSON comes along; everything else starts empty.
3. Into another action: @action_name
Add @action_name and the restart starts a different action, seeded with your values. This
lets you split a long process into small, separately testable actions.
The sending action — a picker that hands the chosen product to an editor:
DECLARE @ProductId NVARCHAR(MAX);
DECLARE @OpenButton NVARCHAR(MAX);
DECLARE @Json NVARCHAR(MAX);
EXEC sp_api_modal_get_value @name = N'@OpenButton', @value = @OpenButton OUT;
EXEC sp_api_modal_text @text = N'Choose a product', @class = N'h4';
-- A picker reads its own posted value: no sp_api_modal_get_value for @ProductId
EXEC sp_api_modal_select @card_name = N'products', @name = N'@ProductId', @value = @ProductId OUT;
EXEC sp_api_modal_button @name = N'@OpenButton', @value = N'Edit',
@valueout = @OpenButton OUT, @class = N'btn-primary';
IF @OpenButton IS NULL OR @ProductId IS NULL RETURN;
SET @Json = (SELECT [@ProductId] = @ProductId FOR JSON PATH, WITHOUT_ARRAY_WRAPPER);
EXEC sp_api_modal_restart @action_name = N'product_edit', @values = @Json;
RETURN;
The receiving action, product_edit, reads @ProductId like any other value:
DECLARE @ProductId NVARCHAR(MAX);
DECLARE @Message NVARCHAR(MAX);
EXEC sp_api_modal_get_value @name = N'@ProductId', @value = @ProductId OUT;
IF @ProductId IS NULL
BEGIN
SET @Message = N'Open this dialog from the product picker.';
EXEC sp_api_modal_alert @alert_text = @Message;
RETURN;
END
-- ... draw the edit form for this product ...
Two habits keep this robust:
- The receiver checks its input. It can also be started some other way, so it should never assume the value is there.
- Keep the payload small — ids rather than whole records. The receiver looks up the rest itself, which also means it always shows current data.
4. Across sessions: sp_api_modal_set and sp_api_modal_get
For a value that should outlive the dialog — the last warehouse a user picked, a preferred date range — store it per user:
DECLARE @Warehouse NVARCHAR(MAX);
EXEC sp_api_modal_get @key = N'preferred_warehouse', @value = @Warehouse OUT; -- read
EXEC sp_api_modal_set @key = N'preferred_warehouse', @value = @Warehouse; -- write
EXEC sp_api_modal_set @key = N'preferred_warehouse', @value = NULL; -- forget
These values belong to the logged-in user and persist until you overwrite them, so they suit preferences, not temporary hand-overs. For a value shared by the whole application, use App Settings instead.