lustre-labs / lustre

A Gleam web framework for building HTML templates, single page applications, and real-time server components.
https://hexdocs.pm/lustre
MIT License
1.21k stars 78 forks source link

Server component applying attributes to the wrong elements in a list of children #195

Closed JonasGruenwald closed 4 weeks ago

JonasGruenwald commented 1 month ago

Hello – I encountered this issue in my app with server components applying attributes to the wrong DOM elements in a list of children.

Steps

/// The added part is:
/// * A div with a number of children that **changes dynamically based on the model** 
/// * The last child is always the same and has an attribute applied (just a simple style)
/// * The other children have no attributes applied
fn view(model: Model) -> Element(Msg) {
  let styles = [#("width", "100vw"), #("height", "100vh"), #("padding", "1rem")]
  let count = int.to_string(model)
  ui.centre(
    [attribute.style(styles)],
    ui.stack([], [
      ui.button([event.on_click(Incr)], [element.text("+")]),
      html.slot([]),
      html.p([attribute.style([#("text-align", "center")])], [
        element.text(count),
      ]),
      ui.button([event.on_click(Decr)], [element.text("-")]),

      // --- ADDDED THIS PART vvv
      html.div(
        [],
        list.range(0, model)
          |> list.map(fn(n) {
            html.div([], [
              html.text("Unstyled item number " <> int.to_string(n)),
            ])
          })
          |> list.append([
            html.div([attribute.attribute("style", "color:red")], [
              html.text("This item alone should be styled red"),
            ]),
          ]),
      ),
      // --- UNTIL HERE ------^^^
    ]),
  )
}

What happens

Screenshot 2024-10-21 at 19 01 24

Additional Note / Workaround

In this particular case, if all children have the attribute present, the issue will not occur, so this:

// --- ADDDED THIS PART vvv
html.div(
  [],
  list.range(0, model)
    |> list.map(fn(n) {
      html.div([attribute.attribute("style", "color:black")], [
        html.text("Unstyled item number " <> int.to_string(n)),
      ])
    })
    |> list.append([
      html.div([attribute.attribute("style", "color:red")], [
        html.text("This item alone should be styled red"),
      ]),
    ]),
),
// --- UNTIL HERE ------^^^

Will make the dynamic children appear as they should.

hayleigh-dot-dev commented 4 weeks ago

Thank you, after a bit of digging this turned out to be a super simple fix. I was accidentally indexing into a string i thought was an array, so the patching logic was all off!