Statful component must have a static html in root issue

Hello! I am writing a simple modal component with help of live view stateful components. I got an unexpected error(in the title of this topic). So my first idea was to implement a div block around all my HTML code in the component and it solved the problem. And I was curious about what is root HTML. I decided to move some properties of my div to root div:

@impl Phoenix.LiveComponent
  def render(assigns) do
  ~H"""
  <div>
  <div id={"#{@id}"} phx-hook="Modal" x-data={"{ open: #{@show} }"}  x-init={"() => {
    $nextTick(() => $refs.modalRightButton.focus())
    $watch('open', isOpen => {
      console.log('WATCH OPEN = ' + isOpen)
      if (!isOpen) {
        modalHook.modalClosing(200)
      }
    })
  }"}
  @keydown.escape.window="if (connected) open = false"
  x-show="open"
  x-cloak>

...

</div>
</div>

and make something like that:

 <div id={"#{@id}"} phx-hook="Modal" x-data={"{ open: #{@show} }"}  x-init={"() => {
    $nextTick(() => $refs.modalRightButton.focus())
    $watch('open', isOpen => {
      console.log('WATCH OPEN = ' + isOpen)
      if (!isOpen) {
        modalHook.modalClosing(200)
      }
    })
  }"}
  @keydown.escape.window="if (connected) open = false"
  x-show="open"
  x-cloak>
  <div>

And there is still no errors arraised! I wonder why is that. It is also crucial to me cause it solves my problem with modal closing - as you can see, I got js to watch the modal and send event to component when modal is closed. Before moving all properties to outer div I was getting event modal_closed two time.(I am still curious about that cause I can`t understand the purpose of phx-target :frowning: ) Back to my question: what is root HTML element is needed for live component. And I would be thankful if you know some articles about phx-target).

P.S. I learned a lot about modal creating thanks to this guide: https://blog.pthompson.org/liveview-tailwind-css-alpine-js-modal

Here you can find out about the phx-target tag: Targeting Component Events .

Without phx-target all events from the LiveComponent are send to the LiveView containing it by default. If you want your LiveComponent to handle it (or send it somewhere else), you need to set phx-target.

2 Likes

I think the root element of the component must be a static tag i.e. the component can’t switch its root between div and span, or div and nothing. But its attributes can be dynamic.

hmm…but when the component was embedded in

<div id={"#{@id}"} phx-hook="Modal" x-data={"{ open: #{@show}}"}  x-init={"() => {
    $nextTick(() => $refs.modalRightButton.focus())
    $watch('open', isOpen => {
      console.log('WATCH OPEN = ' + isOpen)
      if (!isOpen) {
        modalHook.modalClosing(200)
      }
    })
  }"}
  @keydown.escape.window="if (connected) open = false"
  x-show="open"
  x-cloak>

Phoenix argued that I must specify the root element… And still, I don’t understand how, but adding

block inside fixes an error :thinking:

Did you close the div? Your example earlier had <div>...<div>, and this one has <div> but no </div>.

As an aside, id={"#{@id}"} is just id={@id}.

2 Likes

Yeah, that was the reason
Now I got the reason for this error thanks to you!
And thanks for this shortcut (cause my way looks terrible)