sreyansjain

sreyansjain

Does Hologram support two way data binding?

I have a state in Hologram lets say %{age: “40”}

I have an input like so

<input type="text" name="age" value={@age} />

I want to bind the value of input to the age.

I can do this

<input type="text" name="age" value={@age} $change="update_age" />

And then I can do

def action(:update_age, %{event: %{value: age}}, component) do
    put_state(component, age: age)
end

Is there another way to do this?
Does/will Hologram support two way data binding?

Marked As Solved

bartblast

bartblast

Creator of Hologram

There’s an important limitation to be aware of in v0.5.0. The issue you’re experiencing is that text-based input DOM elements are stateful and prioritize user-typed input over value attribute updates (this is a general DOM/HTML behavior, not specific to Hologram).

This is actually the same problem that Phoenix LiveView users encounter, as discussed in this Elixir Forum thread: HTML <input value doesn't reflect update in assign. When a user has already typed anything into an input field, you can’t change the input value through the value attribute programmatically - the browser maintains its own internal state.

Good news though! This limitation is being addressed in v0.6.0, which is already implemented on the dev branch. You can try it out by updating your mix.exs:

{:hologram, git: "https://github.com/bartblast/hologram.git", ref: "84867a2869fa4f58a01e3849eba3b7525773350d"}

With this version, your current code will essentially work like true two-way data binding (no code changes needed in your example). The framework will handle the DOM state management properly, so when you update the component state through your $change event handler, the input value will update correctly.

Eventually, I’m planning to add some sugar syntax for this similar to how Vue or Svelte handle it, but for now, the manual approach (like in your example) works well.

Also Liked

bartblast

bartblast

Creator of Hologram

Form handling is such a fundamental feature that I think we need to nail down clear, consistent terminology for how Hologram approaches it. It’s one of the most common client-side use cases and probably what trips up LiveView users the most - as discussed in this BlueSky thread. I’m planning a dedicated “Forms” page for the website since good and consistent docs are crucial for good DX.

I totally agree that most developers won’t care about implementation details and just want value={@my_value} to work. That abstraction is perfect for them. However, some developers will want to compare Hologram with other frameworks, and having consistent naming for this pattern will be helpful in discussions and documentation.

Based on the feedback here, I’ll definitely avoid “two-way binding” or “bidirectional binding” since these don’t accurately describe what Hologram does and have historical baggage.

I’m considering these alternatives:

  • “Controlled Inputs” - familiar to React developers
  • “State-Synchronized Inputs” - descriptive for newcomers
  • “Input Synchronization” - focuses on the key benefit

(or “Form Elements” instead of “Inputs”)

The core concept is that Hologram maintains unidirectional data flow while solving the DOM synchronization problem that LiveView can’t address.

What do you think would be the clearest terminology? I want to get this right since it’ll be used throughout the documentation and forum discussions.

bartblast

bartblast

Creator of Hologram

Looking at this discussion, I think there’s an important distinction to make about scope and intended use cases that clarifies the architectural differences.

You’re right that Hologram is still a separate stateful system next to the DOM, and that external DOM manipulation (browser plugins, console commands) can create inconsistencies that won’t be corrected until the next reconciliation cycle. That’s a fair technical critique for specific cases that intentionally avoid the intended use through the API.

However, I’d argue this is similar to saying “Linux isn’t secure because you can modify /dev/mem as root” - while technically true, it misses the practical scope of what the system is designed to solve.

Where Hologram Does Prevent Inconsistencies

For user-driven interactions - which represent 99.9% of real web app usage - Hologram genuinely does prevent inconsistencies rather than just correct them:

<input $change="update_name" value={@name} />

def action(:update_name, %{event: %{value: name}}, component) do
  put_state(component, :name, name)
end

This creates a closed synchronous loop:

  1. User types → DOM event fires immediately
  2. Hologram handler executes in same event loop tick
  3. State updates immediately
  4. VDOM reconciliation happens immediately

JavaScript’s event loop guarantees that the event handler runs atomically and completely before any other queued tasks can execute. This means the DOM event processing and state update happen in true lock-step, with no inconsistency window where they can diverge.

The Key Architectural Difference

The fundamental issue is that latency creates an unavoidable distributed systems challenge for LiveView:

  • LiveView: User input → network roundtrip → server processing → network response → DOM reconciliation (with race conditions, ordering issues, connection failures)
  • Hologram: User input → immediate local state update → immediate DOM sync (atomic)

External DOM Manipulation Is An Edge Case

Interestingly, even direct input manipulation demonstrates Hologram’s robustness: if someone does document.getElementById("my-input").value = "hacked" and the input has a $change event handler connected to the state, Hologram reconciles automatically and immediately when the programmatic change occurs.

Yes, someone could do document.getElementById("my-div").innerHTML = "hacked content" as you mentioned and break VDOM synchronization until the next reconciliation cycle - but this is equivalent to:

  • Using dangerouslySetInnerHTML in React and complaining about inconsistencies
  • Manually editing database files and complaining about corruption
  • Writing kernel memory directly and complaining Linux crashed

These break the intended API contract and represent edge cases, not fundamental architectural limitations.

The Real-World Impact

For the scenarios developers actually encounter (form interactions, user events, programmatic state updates through the framework), Hologram’s architecture eliminates the consistency problems that LiveView cannot solve due to network latency.

While no abstraction is perfect when you deliberately bypass it, the practical advantage for intended use cases is significant - as @garrison noted with his example of two-input forms that “literally require hooks” in LiveView but work naturally in Hologram.

When developers use Hologram’s event system as intended, it prevents inconsistencies from occurring. When they bypass this system with direct DOM manipulation, inconsistencies can happen - but that’s breaking the API contract.

So… to sum up… given that DOM event handlers are guaranteed to execute atomically without interruption from other queued JavaScript, I think it’s fair to say the distinction I made earlier that you quoted holds true within the framework’s intended scope of user-driven interactions.

bartblast

bartblast

Creator of Hologram

@sreyansjain I need to correct my previous advice about using the dev branch.

I shouldn’t have instructed you to use the head of the dev branch (branch: "dev") because there’s currently some incomplete work on CSRF protection that will probably prevent your app from sending commands properly. And generally you shouldn’t rely on the head of the dev branch either. The framework is actively being developed, so sometimes the dev branch can be in a transitional state.

Instead, use the last “green” commit from the dev branch:

{:hologram, git: "https://github.com/bartblast/hologram.git", ref: "84867a2869fa4f58a01e3849eba3b7525773350d"}

This commit has the two-way data binding improvements I mentioned, but without the breaking CSRF changes that are still in progress. Your original code example should work correctly with this specific commit.

Sorry for the confusion!

PS: original post amended…

LostKobrakai

LostKobrakai

This is a topic full of footguns though. The value attribute of inputs in html is used to set the default value of that input, the value reverted to e.g. when you reset a form (<input type="reset">). The value of the input itself is the input.value property of the dom node (vs. input.getAttribute("value")). You can ignore that distinction – LV by my understanding does so – but it’ll mean you might break expectations for people who understand and use that distinction.

LostKobrakai

LostKobrakai

That’s one path to take for sure. LV does the same. I’m using solid.js at work and they recently had a discussion on the design of v2, where they were looking for removing such special cases – which is the less brittle option. But my point was less in favor of one or another solution, but the fact that one needs to be aware of that stuff as a maintainer for an abstraction over html and where there’s deviation from the norm (usually those templates do not set properties) it needs to be well documented.

Where Next?

Popular in Questions Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
kostonstyle
Hi all I want to have a unix time, from the current time plus 1 hour. DateTime.now + 1 hour How to get it in elixir? Thanks
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

Other popular topics Top

srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New

We're in Beta

About us Mission Statement