*save
Summary
*save exports the host data (or its staged view) as a JSON file in the browser. It is typically used on a button inside a <serc-rod> host. When clicked, *save collects the host’s current data, builds a JSON string, and starts a download such as Sercrod-20251205-093000.json.
By default, *save exports the entire host data. If you provide a list of property names, only those top-level properties are included.
Basic example
Save the entire host data:
<serc-rod id="profile" data='{"name":"Alice","age":30}'>
<button *save>Download profile JSON</button>
</serc-rod>
Behavior:
- The
<button>is cloned and given a click handler by Sercrod. - When the button is clicked, Sercrod takes the host’s current data and serializes it to JSON.
- A file named like
Sercrod-YYYYMMDD-HHMMSS.jsonis generated and downloaded by the browser.
Behavior
-
*saveattaches a click handler to the element it is placed on. -
The handler runs in the context of the surrounding Sercrod host and serializes:
this._stageif it exists, otherwisethis._data.
-
No network request is sent by
*saveitself. -
The resulting JSON is written into a Blob, and a temporary
<a download>element is used to trigger the browser’s download dialog. -
After the download is initiated, a
CustomEvent("sercrod-saved")is dispatched from the host for application-specific hooks.
Alias:
*saveandn-saveare aliases and behave identically.
Data source and property selection
Data source:
-
*savealways uses host-level data, not per-element scope variables. -
On click:
- If the host has a staged buffer (
_stage),*savereads from_stage. - Otherwise, it reads from the committed data object
_data.
- If the host has a staged buffer (
Property selection:
-
Without an attribute value:
*saveexports the entire data object (_stageor_data).
-
With a value:
- The attribute is treated as a whitespace-separated list of top-level property names.
- These names are not expressions; they are taken as-is and are not evaluated.
- Only properties that exist on the data object are copied into a new object.
Example (selective save):
<serc-rod id="settings" data='{
"user": { "name": "Alice", "age": 30 },
"theme": { "mode": "dark" },
"debug": true
}'>
<!-- Only save "user" and "theme" from the host data -->
<button *save="user theme">Download user+theme</button>
</serc-rod>
In this example:
-
srcishost._stage ?? host._data. -
If the
*saveattribute is"user theme", Sercrod builds:data = { user: src.user, theme: src.theme }(if those properties exist).
-
The JSON file contains only
userandthemeat the top level. -
Nested paths (such as
user.name) are not supported by*savedirectly.
Evaluation timing
Render-time:
-
When Sercrod renders the host, it looks for elements with
*saveorn-save. -
For each such element:
- Sercrod clones the element.
- Attaches a click handler on the clone.
- Appends the clone to the parent.
- Returns from the element renderer without recursing into the children of that clone.
Click-time:
-
When the user clicks the
*savebutton:-
Sercrod reads the
*save/n-saveattribute value and parses it into a property list, if present. -
Sercrod selects
src = this._stage ?? this._datafrom the host. -
It builds a plain object:
- Entire
srcif no property list was provided. - A subset object if a property list was provided.
- Entire
-
It serializes that object with
JSON.stringify(data, null, 2). -
It creates a Blob, a temporary
<a>element, and triggers a download with a timestamped name. -
It dispatches the
sercrod-savedevent from the host.
-
Because the JSON is built at click time, *save always reflects the current state of _stage or _data at the moment of the click.
Execution model
Internally, *save behaves as follows (conceptually):
-
During render, Sercrod finds an element
workwith*saveorn-save. -
Sercrod clones
workintoel. -
Sercrod attaches:
el.addEventListener("click", () => { /* build JSON and download */ }).
-
Sercrod appends
elto the parent node and returns, without processingel’s children for further Sercrod directives.
On click, the handler:
-
Reads the attribute value to obtain a property list (or
nullif empty). -
Selects
src:src = host._stage ?? host._data.
-
Builds
data:- If there is a property list,
datais a new object populated only with properties present insrc. - Otherwise,
dataissrcitself.
- If there is a property list,
-
Serializes
datawith a pretty-printedJSON.stringify(data, null, 2). -
Creates a Blob of type
application/json. -
Creates an
ObjectURLand a temporary<a>element with:href = url.download = "Sercrod-YYYYMMDD-HHMMSS.json"(in the local time of the browser).
-
Programmatically clicks the anchor to prompt download.
-
Cleans up (removes the anchor from the DOM and revokes the
ObjectURL). -
Dispatches
CustomEvent("sercrod-saved", { detail: { ... } })from the host.
Use on nested elements and scope
*savemust live inside a Sercrod host to be meaningful, since it reads from the host’s_stageor_data.*savedoes not use per-element scope; it only uses the host’s data object.- Placing
*saveon a deeply nested element is allowed, but it still always saves the surrounding host’s data, not a subset scoped by*foror*each.
In other words:
- The location of the
*savebutton in the DOM tree does not change the data source. - It only changes where the button appears in the layout.
Events
After starting the download, *save dispatches a bubbling, composed CustomEvent from the host:
-
Event type:
"sercrod-saved"
-
Event detail structure:
detail.stage:"save"(a simple tag for the workflow stage).detail.host: the Sercrod host element (<serc-rod>instance).detail.fileName: the file name used for the download (for example"Sercrod-20251205-093000.json").detail.props: the property list array if provided;nullif no list was specified.detail.json: the JSON string that was generated.
Example hook:
document.addEventListener("sercrod-saved", (evt) => {
const { host, fileName, props, json } = evt.detail;
console.log("Saved from host:", host.id);
console.log("File name:", fileName);
console.log("Props:", props);
console.log("JSON preview:", json.slice(0, 200));
});
You can use this event to:
- Mirror the saved JSON to another storage (for example IndexedDB, localStorage, or a custom API).
- Show a toast notification after the download is triggered.
- Log or audit save operations.
Best practices
-
Treat
*saveelements as simple buttons:- Because the renderer does not recursively process children of
*savehosts after cloning, avoid placing other Sercrod directives inside the same element. - Use plain text or static markup inside the button where possible.
- Because the renderer does not recursively process children of
-
Use property lists for focused exports:
-
If your host data is large, consider exposing smaller subsets via:
*save="profile settings"*save="chart filters"
-
-
Keep the root data export-friendly:
- Plan your top-level keys (
user,settings,rows,config, and so on) so that it is easy to export meaningful subsets by name.
- Plan your top-level keys (
-
Combine with
*loadfor round trips:- Use
*saveto export JSON snapshots. - Use
*loadto re-import and merge them later into the same or a different host.
- Use
-
Use
sercrod-savedfor integration:- Attach listeners to
"sercrod-saved"if you want to route the JSON elsewhere instead of or in addition to the download.
- Attach listeners to
Advanced - Using *save with *stage, *apply, *restore, *load, and *post
*save is part of a broader data management workflow:
-
*stage:- Enables a staged buffer
_stagefor the host (a working copy of the data). - When
_stageexists,*saveprefers_stageover_data. - This lets you export the staged view without committing it.
- Enables a staged buffer
-
*apply:- Copies
_stageinto_dataand updates the host. - Subsequent
*saveclicks, after*apply, will see the committed state in_data.
- Copies
-
*restore:- Rolls back
_stageto the last snapshot, or to_dataif no snapshot is available. - After a restore,
*saveagain sees whatever_stagecurrently holds.
- Rolls back
-
*load:- Reads JSON from an
<input type="file">and merges it into_stageor_data. - You can use
*loadto import a JSON file previously exported by*save.
- Reads JSON from an
-
*post:- Sends host data to a server as JSON over HTTP.
*saveis complementary to*post: one saves locally as a file, the other sends over the network.
The core rule is:
*savealways targets “the current data view” of the host, prioritizing_stagewhen present.- This makes it safe to stage edits with
*stage, try them out, export via*save, and later apply or restore as needed.
Notes
*saveandn-saveare aliases.- The value of
*saveis parsed as plain text and split by whitespace; it is not evaluated as an expression. - When no property list is provided, the entire
_stage ?? _dataobject is serialized. - When a property list is provided, only the listed top-level properties are included if they exist.
- The file name is generated as
"Sercrod-YYYYMMDD-HHMMSS.json"using the browser’s local time. *saveitself does not change_stageor_data; it is a read-only export operation.- There are no special structural restrictions specific to
*savebeyond the general behavior described above; it can be combined with directives such as*ifon the same element, as long as you keep in mind that*saveturns that element into a “save button” whose children are not further processed by Sercrod.