LiveView Hooks in App layout in 1.7 rc2

I have this div in my app layout.

<div id="thing" class="w-10 h-10 bg-gray-400" phx-hook="thingChange">FOOBAR</div>

it has a hook thingChange

my Hooks look like so.

let Hooks = {}
Hooks.thingChange = {
  mounted() {
    document.getElementById("thing").addEventListener("click", () => { 
      document.getElementById("thing").classList.add("bg-teal-400")
      document.getElementById("thing").classList.remove("bg-gray-400")
    })
    console.debug(["mounted", document.getElementById("thing").classList.toString()])
  },
  beforeUpdate(){
    console.debug(["beforeUpdate", document.getElementById("thing").classList.toString()])
  },
  updated(){
    console.debug(["updated", document.getElementById("thing").classList.toString()])
  },
  destroyed() {
    console.debug(["distroyed", document.getElementById("thing").classList.toString()])
  },
  disconnected() {
    console.debug(["disconnected", document.getElementById("thing").classList.toString()])
  },
  reconnected() {
    console.debug(["reconnected", document.getElementById("thing").classList.toString()])
  },
}

I ran the auth gen
mix phx.gen.auth

I generated a super basic blog post
mix phx.gen.live Blog Post posts title:string

I put the routes for the post all in the same live_session

  scope "/", MyAppWeb do
    pipe_through [:browser]

    delete "/users/log_out", UserSessionController, :delete

    live_session :current_user,
      on_mount: [{MyAppWeb.UserAuth, :mount_current_user}] do

        live "/posts", PostLive.Index, :index
        live "/posts/new", PostLive.Index, :new
        live "/posts/:id/edit", PostLive.Index, :edit

        live "/posts/:id", PostLive.Show, :show
        live "/posts/:id/show/edit", PostLive.Show, :edit

      live "/users/confirm/:token", UserConfirmationLive, :edit
      live "/users/confirm", UserConfirmationInstructionsLive, :new
    end
  end

If I navigate the posts index liveview and see the gray foobar box and click it I see the hook on click function set the color to teal as expected. If I then navigate to edit or create a new post and the push_patch is used to show the modal the <div id="thing" looks to get pushed via the diff again though I can see it in the socket messages and the teal class that it once had is lost and is again gray with the original dom elm.

Looking at my console logs.

# Load
Array [ "mounted", "w-10 h-10 bg-gray-400" ]
# I click the div, turn it teal, then click new post
Array [ "beforeUpdate", "w-10 h-10 bg-teal-400" ]
Array [ "updated", "w-10 h-10 bg-gray-400" ]

I can see as a part of the update call back the teal class has been lost but again I can’t seem to see the dom diff in the message calls on the socket so I don’t know where or why this is happening. I though being in the same app layout and using push_patch that I would have not pushed anything new from my app layout?

Here is my update call object.

  "3": {
    "2": {
      "0": {
        "0": " id=\"post-modal\"",
        "1": " phx-mounted=\"[[&quot;show&quot;,{&quot;display&quot;:null,&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal&quot;,&quot;transition&quot;:[[],[],[]]}],[&quot;show&quot;,{&quot;display&quot;:null,&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal-bg&quot;,&quot;transition&quot;:[[&quot;transition-all&quot;,&quot;transform&quot;,&quot;ease-out&quot;,&quot;duration-300&quot;],[&quot;opacity-0&quot;],[&quot;opacity-100&quot;]]}],[&quot;show&quot;,{&quot;display&quot;:null,&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal-container&quot;,&quot;transition&quot;:[[&quot;transition-all&quot;,&quot;transform&quot;,&quot;ease-out&quot;,&quot;duration-300&quot;],[&quot;opacity-0&quot;,&quot;translate-y-4&quot;,&quot;sm:translate-y-0&quot;,&quot;sm:scale-95&quot;],[&quot;opacity-100&quot;,&quot;translate-y-0&quot;,&quot;sm:scale-100&quot;]]}],[&quot;add_class&quot;,{&quot;names&quot;:[&quot;overflow-hidden&quot;],&quot;time&quot;:200,&quot;to&quot;:&quot;body&quot;,&quot;transition&quot;:[[],[],[]]}],[&quot;focus_first&quot;,{&quot;to&quot;:&quot;#post-modal-content&quot;}]]\"",
        "2": " phx-remove=\"[[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal-bg&quot;,&quot;transition&quot;:[[&quot;transition-all&quot;,&quot;transform&quot;,&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;],[&quot;opacity-0&quot;]]}],[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal-container&quot;,&quot;transition&quot;:[[&quot;transition-all&quot;,&quot;transform&quot;,&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;,&quot;translate-y-0&quot;,&quot;sm:scale-100&quot;],[&quot;opacity-0&quot;,&quot;translate-y-4&quot;,&quot;sm:translate-y-0&quot;,&quot;sm:scale-95&quot;]]}],[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal&quot;,&quot;transition&quot;:[[&quot;block&quot;],[&quot;block&quot;],[&quot;hidden&quot;]]}],[&quot;remove_class&quot;,{&quot;names&quot;:[&quot;overflow-hidden&quot;],&quot;time&quot;:200,&quot;to&quot;:&quot;body&quot;,&quot;transition&quot;:[[],[],[]]}],[&quot;pop_focus&quot;,{}]]\"",
        "3": "post-modal",
        "4": "post-modal",
        "5": "post-modal",
        "6": {
          "0": " id=\"post-modal-container\"",
          "1": " class=\"hidden relative rounded-2xl bg-white p-14 shadow-lg shadow-zinc-700/10 ring-1 ring-zinc-700/10 transition\" phx-click-away=\"[[&quot;navigate&quot;,{&quot;href&quot;:&quot;/posts&quot;,&quot;replace&quot;:false}],[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal-bg&quot;,&quot;transition&quot;:[[&quot;transition-all&quot;,&quot;transform&quot;,&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;],[&quot;opacity-0&quot;]]}],[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal-container&quot;,&quot;transition&quot;:[[&quot;transition-all&quot;,&quot;transform&quot;,&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;,&quot;translate-y-0&quot;,&quot;sm:scale-100&quot;],[&quot;opacity-0&quot;,&quot;translate-y-4&quot;,&quot;sm:translate-y-0&quot;,&quot;sm:scale-95&quot;]]}],[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal&quot;,&quot;transition&quot;:[[&quot;block&quot;],[&quot;block&quot;],[&quot;hidden&quot;]]}],[&quot;remove_class&quot;,{&quot;names&quot;:[&quot;overflow-hidden&quot;],&quot;time&quot;:200,&quot;to&quot;:&quot;body&quot;,&quot;transition&quot;:[[],[],[]]}],[&quot;pop_focus&quot;,{}]]\" phx-key=\"escape\" phx-mounted=\"[[&quot;show&quot;,{&quot;display&quot;:null,&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal&quot;,&quot;transition&quot;:[[],[],[]]}],[&quot;show&quot;,{&quot;display&quot;:null,&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal-bg&quot;,&quot;transition&quot;:[[&quot;transition-all&quot;,&quot;transform&quot;,&quot;ease-out&quot;,&quot;duration-300&quot;],[&quot;opacity-0&quot;],[&quot;opacity-100&quot;]]}],[&quot;show&quot;,{&quot;display&quot;:null,&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal-container&quot;,&quot;transition&quot;:[[&quot;transition-all&quot;,&quot;transform&quot;,&quot;ease-out&quot;,&quot;duration-300&quot;],[&quot;opacity-0&quot;,&quot;translate-y-4&quot;,&quot;sm:translate-y-0&quot;,&quot;sm:scale-95&quot;],[&quot;opacity-100&quot;,&quot;translate-y-0&quot;,&quot;sm:scale-100&quot;]]}],[&quot;add_class&quot;,{&quot;names&quot;:[&quot;overflow-hidden&quot;],&quot;time&quot;:200,&quot;to&quot;:&quot;body&quot;,&quot;transition&quot;:[[],[],[]]}],[&quot;focus_first&quot;,{&quot;to&quot;:&quot;#post-modal-content&quot;}]]\" phx-window-keydown=\"[[&quot;navigate&quot;,{&quot;href&quot;:&quot;/posts&quot;,&quot;replace&quot;:false}],[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal-bg&quot;,&quot;transition&quot;:[[&quot;transition-all&quot;,&quot;transform&quot;,&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;],[&quot;opacity-0&quot;]]}],[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal-container&quot;,&quot;transition&quot;:[[&quot;transition-all&quot;,&quot;transform&quot;,&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;,&quot;translate-y-0&quot;,&quot;sm:scale-100&quot;],[&quot;opacity-0&quot;,&quot;translate-y-4&quot;,&quot;sm:translate-y-0&quot;,&quot;sm:scale-95&quot;]]}],[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal&quot;,&quot;transition&quot;:[[&quot;block&quot;],[&quot;block&quot;],[&quot;hidden&quot;]]}],[&quot;remove_class&quot;,{&quot;names&quot;:[&quot;overflow-hidden&quot;],&quot;time&quot;:200,&quot;to&quot;:&quot;body&quot;,&quot;transition&quot;:[[],[],[]]}],[&quot;pop_focus&quot;,{}]]\"",
          "2": "post-modal-container",
          "3": {
            "0": " phx-click=\"[[&quot;navigate&quot;,{&quot;href&quot;:&quot;/posts&quot;,&quot;replace&quot;:false}],[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal-bg&quot;,&quot;transition&quot;:[[&quot;transition-all&quot;,&quot;transform&quot;,&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;],[&quot;opacity-0&quot;]]}],[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal-container&quot;,&quot;transition&quot;:[[&quot;transition-all&quot;,&quot;transform&quot;,&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;,&quot;translate-y-0&quot;,&quot;sm:scale-100&quot;],[&quot;opacity-0&quot;,&quot;translate-y-4&quot;,&quot;sm:translate-y-0&quot;,&quot;sm:scale-95&quot;]]}],[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#post-modal&quot;,&quot;transition&quot;:[[&quot;block&quot;],[&quot;block&quot;],[&quot;hidden&quot;]]}],[&quot;remove_class&quot;,{&quot;names&quot;:[&quot;overflow-hidden&quot;],&quot;time&quot;:200,&quot;to&quot;:&quot;body&quot;,&quot;transition&quot;:[[],[],[]]}],[&quot;pop_focus&quot;,{}]]\"",
            "1": " aria-label=\"close\"",
            "2": {
              "0": {
                "0": " aria-hidden=\"true\" class=\"h-5 w-5 stroke-current\" fill=\"currentColor\" viewBox=\"0 0 24 24\"",
                "1": {
                  "0": "<path fill-rule=\"evenodd\" d=\"M5.47 5.47a.75.75 0 011.06 0L12 10.94l5.47-5.47a.75.75 0 111.06 1.06L13.06 12l5.47 5.47a.75.75 0 11-1.06 1.06L12 13.06l-5.47 5.47a.75.75 0 01-1.06-1.06L10.94 12 5.47 6.53a.75.75 0 010-1.06z\" clip-rule=\"evenodd\"/>",
                  "s": [
                    "",
                    ""
                  ]
                },
                "s": [
                  "<svg xmlns=\"http://www.w3.org/2000/svg\"",
                  ">\n  ",
                  "\n</svg>"
                ]
              },
              "s": [
                "",
                ""
              ]
            },
            "3": "post-modal",
            "4": "",
            "5": {
              "0": 1,
              "s": [
                "\n  ",
                "\n"
              ]
            },
            "6": "",
            "s": [
              "\n          <div class=\"absolute top-6 right-5\">\n            <button",
              " type=\"button\" class=\"-m-3 flex-none p-3 opacity-20 hover:opacity-40\"",
              ">\n              ",
              "\n            </button>\n          </div>\n          <div id=\"",
              "-content\">\n            ",
              "\n            ",
              "\n            ",
              "\n          </div>\n        "
            ]
          },
          "4": "post-modal-container",
          "s": [
            "<div",
            " phx-hook=\"Phoenix.FocusWrap\"",
            ">\n  <span id=\"",
            "-start\" tabindex=\"0\" aria-hidden=\"true\"></span>\n  ",
            "\n  <span id=\"",
            "-end\" tabindex=\"0\" aria-hidden=\"true\"></span>\n</div>"
          ]
        },
        "s": [
          "<div",
          "",
          "",
          " class=\"relative z-50 hidden\">\n  <div id=\"",
          "-bg\" class=\"fixed inset-0 bg-zinc-50/90 transition-opacity\" aria-hidden=\"true\"></div>\n  <div class=\"fixed inset-0 overflow-y-auto\" aria-labelledby=\"",
          "-title\" aria-describedby=\"",
          "-description\" role=\"dialog\" aria-modal=\"true\" tabindex=\"0\">\n    <div class=\"flex min-h-full items-center justify-center\">\n      <div class=\"w-full max-w-3xl p-4 sm:p-6 lg:py-8\">\n        ",
          "\n      </div>\n    </div>\n  </div>\n</div>"
        ]
      },
      "s": [
        "",
        ""
      ]
    }
  },
  "c": {
    "1": {
      "0": {
        "0": "",
        "1": {
          "0": "New Post",
          "s": [
            "\n    ",
            "\n    "
          ]
        },
        "2": {
          "0": {
            "s": [
              "Use this form to manage post records in your database."
            ]
          },
          "s": [
            "<p class=\"mt-2 text-sm leading-6 text-zinc-600\">\n      ",
            "\n    </p>"
          ]
        },
        "3": "",
        "s": [
          "<header class=\"",
          "\">\n  <div>\n    <h1 class=\"text-lg font-semibold leading-8 text-zinc-800\">\n      ",
          "\n    </h1>\n    ",
          "\n  </div>\n  <div class=\"flex-none\">",
          "</div>\n</header>"
        ]
      },
      "1": {
        "0": {
          "0": " method=\"post\" errors=\"\" id=\"post-form\" phx-change=\"validate\" phx-submit=\"save\" phx-target=\"1\"",
          "1": "",
          "2": "",
          "3": {
            "0": {
              "0": {
                "0": " phx-feedback-for=\"post[title]\"",
                "1": {
                  "0": " for=\"post-form_title\"",
                  "1": {
                    "0": "Title",
                    "s": [
                      "",
                      ""
                    ]
                  },
                  "s": [
                    "<label",
                    " class=\"block text-sm font-semibold leading-6 text-zinc-800\">\n  ",
                    "\n</label>"
                  ]
                },
                "2": " type=\"text\"",
                "3": " name=\"post[title]\"",
                "4": " id=\"post-form_title\"",
                "5": "",
                "6": "border-zinc-300 focus:border-zinc-400 focus:ring-zinc-800/5 mt-2 block w-full rounded-lg border-zinc-300 py-[7px] px-[11px] text-zinc-900 focus:outline-none focus:ring-4 sm:text-sm sm:leading-6 phx-no-feedback:border-zinc-300 phx-no-feedback:focus:border-zinc-400 phx-no-feedback:focus:ring-zinc-800/5",
                "7": "",
                "8": "",
                "s": [
                  "<div",
                  ">\n  ",
                  "\n  <input",
                  "",
                  "",
                  "",
                  " class=\"",
                  "\"",
                  ">\n  ",
                  "\n</div>"
                ]
              },
              "s": [
                "\n    ",
                "\n    "
              ]
            },
            "1": {
              "d": [
                [
                  {
                    "0": {
                      "0": "",
                      "1": "",
                      "2": " phx-disable-with=\"Saving...\"",
                      "3": {
                        "s": 0
                      },
                      "s": 1
                    },
                    "s": 2
                  }
                ]
              ],
              "p": {
                "0": [
                  "Save Post"
                ],
                "1": [
                  "<button",
                  " class=\"phx-submit-loading:opacity-75 rounded-lg bg-zinc-900 hover:bg-zinc-700 py-2 px-3 text-sm font-semibold leading-6 text-white active:text-white/80 ",
                  "\"",
                  ">\n  ",
                  "\n</button>"
                ],
                "2": [
                  "\n      ",
                  "\n    "
                ]
              },
              "s": [
                "<div class=\"mt-2 flex items-center justify-between gap-6\">\n      ",
                "\n    </div>"
              ]
            },
            "s": [
              "\n  <div class=\"space-y-8 bg-white mt-10\">\n    ",
              "\n    ",
              "\n  </div>\n"
            ]
          },
          "s": [
            "<form",
            ">\n  ",
            "\n  ",
            "\n  ",
            "\n</form>"
          ]
        },
        "s": [
          "",
          ""
        ]
      },
      "s": [
        "<div>\n  ",
        "\n\n  ",
        "\n</div>"
      ]
    }
  },
  "t": "New Post"
}

I can’t see the bg-gray-400 as a part of the diff anywhere.

Example app.

The diff includs only the dynamic parts of a template, not the static parts. Phoenix then uses both parts to create the full template on the client and gives that to morphdom to apply. The layout also is part of the top level LiveView responsible for the page.

So really the behaviour seems expected. The page updated and LV is unaware of and will overwrite client side changes.

1 Like

Thanks for clarifying.