Actions

Actions in Hologram are client-side operations that allow you to:

They are typically executed in response to user interactions, enabling dynamic and interactive web applications.

Defining Actions

Actions are defined as functions in your page or component modules using the following syntax:

def action(name, params, component) do
  # Action logic here
end

For example:

def action(:update_count, params, component) do
  put_state(component, :count, params.new_count)
end

Action Parameters

Actions receive three arguments:

Action Results

Actions must return a %Component{} struct. This struct not only reflects changes to the component's state but also includes instructions for what happens next through functions that can be called on it. These functions are pure - they don't cause any side effects and simply return a new modified %Component{} struct. Since these functions return the modified %Component{} struct, they can be chained together using the pipe operator |>, allowing for a clean and composable way to combine multiple state changes and behaviors.

Within an action, you can:

Update Component State

By setting the value for a single key:

put_state(component, :key, value)

With multiple key-value pairs using a keyword list:

put_state(component, key_1: value_1, key_2: value_2)

With multiple key-value pairs using a map:

put_state(component, %{key_1: value_1, key_2: value_2})

Update a nested value by specifying a path of keys:

put_state(component, [:path_key_1, :path_key_2], value)

Trigger a Server Command

Command with the specified name without any parameters:

put_command(component, :my_command)

Command with the specified name and additional parameters provided as a keyword list:

put_command(component, :my_command, param_1: value_1, param_2: value_2)

Command with a specified name, target, and parameters using a map for the parameters (in this longhand version, both params and target are optional):

put_command(component, name: :my_command, target: "other_component", params: %{key: value})

Command using a %Command{} struct:

put_command(component, %Command{name: :my_command})

See also: Commands.

Navigate to Another Page

Page without params:

put_page(component, ProductsPage)

Page with params:

put_page(component, ProductPage, product_id: 123)

Update Emitted Context

By setting a value for a single key:

put_context(component, :key, value)

Using a tuple for namespacing, allowing different components to use the same key without conflict:

put_context(component, {MyModule, :key}, value)

See also: Context.

Chain Another Action

Action with the specified name without any additional parameters:

put_action(component, :my_action)

Action with the specified name and additional parameters provided as a keyword list:

put_action(component, :my_action, param_1: value_1, param_2: value_2)

Action with a specified name, target, and parameters using a map for the parameters (in this longhand version, both params and target are optional):

put_action(component, name: :my_action, target: "other_component", params: %{key: value})

Using an %Action{} struct:

put_action(component, %Action{name: :my_action})

Triggering Actions

Actions can be triggered using event attributes in templates. There are several syntax options:

Text Syntax

<button $click="my_action">Click me</button>

Expression Shorthand Syntax

Without params:

<button $click={:my_action}>Click me</button>

With params:

<button $click={:my_action, a: 1, b: 2}>Click me</button>

Expression Longhand Syntax

Trigger an action named :my_action, targeting component with CID "other_component" and passing parameters a and b with values 1 and 2, respectively (both target and params are optional):

<button $click={action: :my_action, target: "other_component", params: %{a: 1, b: 2}}>Click me</button>

Action Targets

By default, actions are executed on the component that contains them. If the component is stateless (doesn't have a CID specified), the closest stateful component higher in the hierarchy is used. You can specify a different component for execution using the target parameter:

<button $click={action: :my_action, target: "other_component"}>Click me</button>

Valid targets include:

Putting It All Together

Here's a simple example that demonstrates how actions work in Hologram. The counter component shows:

defmodule MyApp.Components.Counter do
  # ...
      
  def template do
    ~H"""
    <div>
      <p>Count: {@count}</p>
      <button $click={:increment, step: 1}> +1 </button>
    </div>
    """
  end

  def action(:increment, params, component) do
    new_count = component.state.count + params.step
  
    component
    |> put_state(:count, new_count)
    |> put_command(:save_count, new_count)
  end
  
  # ...
end