*into
Summary
*into selects a data slot that receives the result of certain Sercrod directives that talk to the outside world. It is most often used together with *api, *upload, or *websocket to capture responses in a named field on the root data object.
*into has an alias n-into. Both forms behave the same.
Key points:
*intodoes not do anything by itself.- It is only consulted by directives that explicitly read it (for example
*api,*upload,*websocket). - Responses are also mirrored into built-in fields such as
$download,$upload, or$ws_last, so*intois an opt-in extra sink, not the only place where data goes.
Basic example
Capture an API response into user:
<serc-rod id="app" data='{"user": null}'>
<button
*api="'/api/user.json'"
method="GET"
*into="user"
>
Load user
</button>
<pre *if="user" *print="JSON.stringify(user, null, 2)"></pre>
</serc-rod>
Behavior:
- Clicking the button performs an HTTP GET request to
/api/user.json. - When the request succeeds, the parsed response body is stored in:
$download(because it is a GET request)user(because of*into="user")
- On the next finalize step,
$downloadanduserare reset tonull. If you want to keep the data, copy it to a long-lived field in your data.
Behavior
*into is a routing hint for Sercrod’s I/O helpers. It tells those helpers which top-level key on the root data object should receive their latest result.
Implemented behavior in this version:
-
*api/n-api:- Use
*intoon the same element to select a data field where the parsed response is stored. - Also maintain
$downloadand$uploadmetrics.
- Use
-
*upload:- Uses
*intoon the element that defines the upload behavior (see below) to decide where to place the server response.
- Uses
-
*websocket/n-websocket:- Treat
*intoas the destination for the last received message.
- Treat
No other directives read *into in the current implementation. Putting *into on an element that does not participate in these features has no effect.
Evaluation timing
*into is always read on the element that owns the directive which uses it.
-
For
*api:- During rendering,
*apireads*intoorn-intofrom the same element. - The network request is run when Sercrod decides to activate that
*api(for example on click or on initial render, depending on how*apiis used). - When the request resolves, the response body is passed to a small helper
place(value)which:- Updates
$downloador$upload. - Writes to the
*intotarget if one was provided.
- Updates
- During rendering,
-
For
*upload:- When an upload button is bound, Sercrod reads
*intofrom the original element that defines the upload behavior. - The value is stored on an internal helper and reused when the upload completes.
- The upload completion handler then writes the response body to the chosen key.
- When an upload button is bound, Sercrod reads
-
For
*websocket:- When a WebSocket connection is initialized, Sercrod reads
*intofrom the element that has*websocket/n-websocket. - The destination is stored together with the WebSocket instance and reused whenever messages arrive.
- Each incoming message is run through a lightweight parser and then written to the
*intotarget, if present.
- When a WebSocket connection is initialized, Sercrod reads
Execution model
At a high level, the execution model for *into looks like this for each supported directive.
-
With
*api:- Sercrod finds an element with
*apiorn-api. - It clones the element as a real DOM node and reads:
*api/n-apifor the URL expression.methodfor the HTTP method (default"GET").*into/n-intofor the destination key.
- It ensures
$download,$upload, and the*intotarget (if any) exist on the root data as keys withnullvalues. - It runs the HTTP request, either as JSON style or other depending on the options.
- On success:
- For GET requests, it stores the body into
$download. - For non-GET requests or file uploads, it stores the body into
$upload. - If
*intois present, it stores the same value intodata[into]and remembers that key for later clearing.
- For GET requests, it stores the body into
- It dispatches custom events to signal start, success, or error.
- Sercrod continues its normal update cycle. At the finalize step,
$download,$upload, and any remembered*intokeys are reset tonull.
- Sercrod finds an element with
-
With
*upload:- Sercrod normalizes upload options from an expression and element attributes.
- It creates or reuses a hidden
<input type="file">for the clickable element. - It reads
*into/n-intofrom the upload context element and stores this value on the hidden input. - When the user selects files and the upload completes, the server response body is stored into:
- A key derived from
*intoif it exists. $uploadas a generic metric.
- A key derived from
- That key is also added to the list of
*intotargets that will be cleared at finalize.
-
With
*websocket:- Sercrod resolves the WebSocket specification from:
- The
*websocket/n-websocketattribute. - The current data scope.
- The
- It reads
*into/n-intoand constructs a holder{ ws, into, el }. - When messages arrive:
- The last payload is stored into
$ws_last. - Sercrod tries to push it into
$ws_messagesif that array exists. - If
intois non-empty, the value is also written todata[into]and the key is added to the list for later clearing.
- The last payload is stored into
- Sercrod resolves the WebSocket specification from:
Variable creation and scope layering
*into does not create new variables for expressions. Instead, it targets the root data object:
-
The value of
*intois taken as a key on the root data object (this._data). -
Sercrod writes directly to
this._data[into]. -
Nested path semantics (for example
"user.profile") are not interpreted. Such a string is treated literally as a property name. -
The stored values can later be read by any directive that uses the normal scope, for example:
*print="uploadResult"*if="user && user.name"*let="latest = wsMessage"
There is no special $into variable or local binding created by *into itself.
Scope layering and parent access
Because *into writes to the root data object, scope behaves as follows:
- The new key is visible as a normal data field.
- Inside the same
serc-rodhost, it is available through:- The implicit root scope.
$data, if you refer to the unwrapped data.$root, if you are inside nested hosts.
- If a child host defines its own
data, it sees the parent field through$parentor higher-level references, depending on how you structure your templates.
*into does not introduce any extra scope layers by itself.
Use with *api
*into is most directly tied to the *api / n-api logic.
Key behaviors:
-
For every
*apirequest:$downloadis always updated for GET style requests.$uploadis always updated for non-GET style requests and file uploads.- If
*intois given a non-empty string, that key receives the same value.
-
The destination key is initialized to
nullif it did not exist yet. -
At the finalize step, that key is set back to
nullso that stale responses do not remain in data forever.
Typical patterns:
<!-- GET: store response into "user" and also into $download -->
<button *api="'/api/user.json'" method="GET" *into="user">
Load user
</button>
<!-- POST: store result into "result" and also into $upload -->
<button
*api="'/api/save'"
method="POST"
body="{ name: form.name }"
*into="result"
>
Save
</button>
If you need to keep the data beyond one update cycle, copy it to a stable field in an event handler or effect, instead of relying on the short-lived *into slot.
Use with *upload
For uploads, *into is read from the element that defines the upload behavior and is then used on the hidden file input.
Behavior:
-
If
*intois present:- The response body is written to
data[into]. - The same value is mirrored into
$uploadif$uploadwas previously empty. - The
intokey is added to the list of keys to be cleared at finalize.
- The response body is written to
-
If
*intois not present:- The response body is written to
$upload. $uploadis also added to the list of keys to be cleared at finalize.
- The response body is written to
Example pattern:
<serc-rod
data='{
"uploadResult": null
}'
>
<button
*upload="{ url: '/api/upload', accept: 'image/*' }"
*into="uploadResult"
>
Upload image
</button>
<p *if="uploadResult">
Uploaded: <span *print="uploadResult.fileName"></span>
</p>
</serc-rod>
In this pattern:
- Clicking the button opens a file picker.
- The upload happens via
fetchorXMLHttpRequest, depending on the upload options. - The server’s JSON response is stored in
uploadResultand$upload. - After finalize,
uploadResultand$uploadare reset tonull.
Use with *websocket
*into is also supported on WebSocket hosts.
Behavior:
-
On an element with
*websocketorn-websocket, Sercrod:- Resolves a WebSocket specification that includes a URL and optional extra options.
- Reads
*into/n-intofrom the same element. - Connects to the WebSocket and stores the connection in an internal map together with the
intokey.
-
On incoming messages:
$ws_lastreceives the last payload.$ws_messages(if present and array-like) collects all payloads.- If an
intokey is present,data[into]is updated with the latest payload and added to the list of keys cleared at finalize.
If the *websocket expression resolves to an object with an into property, Sercrod also respects that, but a markup *into on the same element takes priority.
Example:
<serc-rod id="ws-app" data='{"lastMessage": null}'>
<section
*websocket="'wss://example.com/live'"
*into="lastMessage"
>
<p>Connection status: <span *print="$ws_ready ? 'ready' : 'connecting'"></span></p>
<p *if="lastMessage">Last message: <span *print="JSON.stringify(lastMessage)"></span></p>
</section>
</serc-rod>
Best practices
-
Choose stable, descriptive keys:
- Use names like
user,profile,uploadResult, orwsPayload. - Avoid reusing the same key for completely unrelated APIs in the same host, unless you deliberately want to share a single slot.
- Use names like
-
Treat
*intoslots as transient:- Values in
$download,$upload, and*intodestinations are cleared at the finalize step. - Copy values you want to keep into long-lived fields in your data model.
- Values in
-
Keep the value simple:
- The full response body is stored as is.
- If you only need part of it, consider extracting that part into another field inside your own code and leaving the
*intoslot for debugging or inspection.
-
Avoid overloading
*intoon the same element:- Do not expect
*intoto work with directives that do not reference it. - Keep one main I/O directive per element that uses
*intoso it is obvious which operation writes to that field.
- Do not expect
Examples
Capture download status separately for GET and POST:
<serc-rod
data='{
"user": null,
"saveResult": null
}'
>
<button *api="'/api/user'" method="GET" *into="user">
Load user
</button>
<button
*api="'/api/user'"
method="POST"
body="{ user }"
*into="saveResult"
>
Save user
</button>
<section *if="user">
<h2>User</h2>
<pre *print="JSON.stringify(user, null, 2)"></pre>
</section>
<section *if="saveResult">
<h2>Last save result</h2>
<pre *print="JSON.stringify(saveResult, null, 2)"></pre>
</section>
</serc-rod>
Use *into with WebSocket messages:
<serc-rod data='{"ticker": null}'>
<div
*websocket="{ url: 'wss://example.com/ticker', into: 'ticker' }"
>
<p *if="ticker">
Price: <span *print="ticker.price"></span>
</p>
</div>
</serc-rod>
Notes
*intoandn-intoare aliases. Use one style consistently in your project.- In this version,
*intois only read by:*api/n-api*uploadbindings*websocket/n-websocket
*intotargets operate on the root data object. The value is written asdata[into]without interpreting dots or bracket notation.- The same response is also placed into
$download,$upload, or$ws_lastdepending on the directive. You can always inspect those fields even when you do not use*into. - After the finalize step:
$downloadand$uploadare reset tonull.- All keys that were used as
*intodestinations are reset tonull.
- If you need persistent storage of responses, copy them out of the short-lived
*intoslots into your own fields before they are cleared.