sercrod

*ws-send

Summary

*ws-send sends a message through the active WebSocket connection managed by the same Sercrod host. It is an action directive for regular elements (for example <button>, <a>, or <input>). The directive has an alias n-ws-send and supports an optional *ws-to attribute that selects a specific WebSocket URL using Sercrod’s text expansion.

Use *ws-send when you already have one or more WebSocket connections open (via *websocket or the websocket helper) and want a declarative way to send messages in response to clicks.

Basic example

A basic setup with a host-level WebSocket and a button that sends a simple message:

<serc-rod id="app"
         data='{"wsUrl":"wss://example.com/ws","message":"hello"}'
         *websocket="wsUrl">
  <button *ws-send="message">
    Send
  </button>
</serc-rod>

Behavior:

Behavior

Clickability and event wiring

The directive decides whether to attach its own click handler based on the cloned element’s tag and type:

For those clickable elements:

This means:

Expression and payload evaluation

The attribute value of *ws-send is an expression that is evaluated at click time, not at render time:

Payload conversion:

Target selection with the *ws-to attribute

*ws-send can send messages either to a specific WebSocket URL or to the first open connection it finds.

Important notes:

Evaluation timing

*ws-send participates in the rendering pipeline as follows:

Execution model

Conceptually, the directive behaves like this:

  1. Rendering:

    • Detect *ws-send or n-ws-send on a node work.

    • Clone the element without children: el = work.cloneNode(false).

    • Append the clone to the parent.

    • Read the raw *ws-to value once:

      • const toRaw = work.getAttribute("*ws-to") || "";
    • Compute clickability based on el’s tag and type.

    • If clickable, attach a click listener:

      • On click:

        • Evaluate the expression in the attribute to obtain payload.
        • Resolve the *ws-to attribute by calling _expand_text(toRaw, scope, work) to obtain targetUrl (which may be empty).
        • Call _ws_send(targetUrl, payload).
    • For each attribute whose name starts with the event prefix (for example @), bind the corresponding event handler to el.

    • Render all original child nodes into el using the usual rendering rules.

  2. Sending:

    • _ws_send(urlOrEmpty, payload) selects a target WebSocket:

      • If urlOrEmpty is non-empty, look up that URL in the internal map of connections.
      • Otherwise, scan for the first open WebSocket.
    • If no open target is found, return false and do not send anything.

    • Convert payload to a string as described above.

    • Call ws.send(out) on the selected WebSocket.

    • Return true if the send operation succeeds, false if it throws.

The return value from _ws_send is not used by the directive itself but is exposed through the this.websocket.send(...) helper for more advanced, imperative control from scripts.

Scope layering and variables

*ws-send does not create new scope variables of its own, but its expression runs inside the normal Sercrod scope:

Use with *websocket and WebSocket metrics

*ws-send is designed to be used together with *websocket and the WebSocket status fields maintained in the data object:

Practical pattern:

Best practices

Additional examples

Sending a structured JSON payload:

<serc-rod id="chat"
         data='{
           "wsUrl": "wss://example.com/chat",
           "draft": ""
         }'
         *websocket="wsUrl">
  <input type="text"
         :value="draft"
         @input="draft = $event.target.value">
  <button *ws-send="{ type: 'chat', text: draft }"
          :disabled="!$ws_ready || !draft">
    Send message
  </button>
</serc-rod>

Selecting a specific connection with *ws-to:

<serc-rod id="multi"
         data='{
           "primaryUrl": "wss://example.com/primary",
           "secondaryUrl": "wss://example.com/secondary"
         }'
         *websocket="primaryUrl">
  <button *ws-send="{ type: 'ping' }">
    Ping (default connection)
  </button>

  <button *ws-send="{ type: 'ping-secondary' }"
          *ws-to="%secondaryUrl%">
    Ping secondary
  </button>
</serc-rod>

In this pattern, you would typically have an element-level *websocket somewhere that connects to secondaryUrl. *ws-send simply routes messages to the appropriate connection based on *ws-to.

Notes