dwyl / phoenix-liveview-counter-tutorial

🤯 beginners tutorial building a real time counter in Phoenix 1.7.7 + LiveView 0.19 ⚡️ Learn the fundamentals from first principals so you can make something amazing! 🚀
https://livecount.fly.dev/
GNU General Public License v2.0
357 stars 35 forks source link

Feat: Split `counter.ex` into a basic `LiveView` Component #166

Closed nelsonic closed 11 months ago

nelsonic commented 11 months ago

At present the counter.ex file contains the following render function:

https://github.com/dwyl/phoenix-liveview-counter-tutorial/blob/949dc43c38499925013e64c0644021238a7f8518/lib/live_view_counter_web/live/counter.ex#L51-L64

Todo

image

Please Note: there's no urgent need to do this work ... The reason I'm doing this split here in the counter (the simplest possible LiveView app) is so that I can learn how LiveView Components work from first principals for: https://github.com/dwyl/mvp/issues/141#issuecomment-1647225832

nelsonic commented 11 months ago

Ref tests: https://github.com/dwyl/phoenix-liveview-counter-tutorial/blob/949dc43c38499925013e64c0644021238a7f8518/test/live_view_counter_web/live/counter_test.exs

nelsonic commented 11 months ago

New Phoenix default page:

image
nelsonic commented 11 months ago

liveview-counter-1 7 7

nelsonic commented 11 months ago

Two browsers, not connected: liveview-counter-not-connected

nelsonic commented 11 months ago

liveview-counter-4-windows

nelsonic commented 11 months ago

Reading: https://hexdocs.pm/phoenix_live_view/Phoenix.LiveComponent.html

nelsonic commented 11 months ago

Not quite there yet ...

[error] #PID<0.677.0> running Phoenix.Endpoint.SyncCodeReloadPlug (connection #PID<0.676.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: GET /
** (exit) an exception was raised:
    ** (KeyError) key :val not found in: %{
  id: "counter",
  socket: #Phoenix.LiveView.Socket<
    id: "phx-F3ZKa8Lfq1vDggPh",
    endpoint: CounterWeb.Endpoint,
    view: CounterWeb.Counter,
    parent_pid: nil,
    root_pid: nil,
    router: CounterWeb.Router,
    assigns: #Phoenix.LiveView.Socket.AssignsNotInSocket<>,
    transport_pid: nil,
    ...
  >,
  __changed__: %{id: true, flash: true},
  flash: %{},
  myself: %Phoenix.LiveComponent.CID{cid: 1}
}
        (counter 1.7.7) lib/counter_web/live/counter_component.ex:8: anonymous fn/2 in CounterComponent.render/1
        (phoenix_live_view 0.19.5) lib/phoenix_live_view/diff.ex:386: Phoenix.LiveView.Diff.traverse/7
        (phoenix_live_view 0.19.5) lib/phoenix_live_view/diff.ex:711: Phoenix.LiveView.Diff.render_component/9
        (phoenix_live_view 0.19.5) lib/phoenix_live_view/diff.ex:657: anonymous fn/5 in Phoenix.LiveView.Diff.render_pending_components/6
        (elixir 1.15.4) lib/enum.ex:2510: Enum."-reduce/3-lists^foldl/2-0-"/3
        (stdlib 5.0.2) maps.erl:416: :maps.fold_1/4
        (phoenix_live_view 0.19.5) lib/phoenix_live_view/diff.ex:629: Phoenix.LiveView.Diff.render_pending_components/6
        (phoenix_live_view 0.19.5) lib/phoenix_live_view/diff.ex:143: Phoenix.LiveView.Diff.render/3
        (phoenix_live_view 0.19.5) lib/phoenix_live_view/static.ex:252: Phoenix.LiveView.Static.to_rendered_content_tag/4
        (phoenix_live_view 0.19.5) lib/phoenix_live_view/static.ex:135: Phoenix.LiveView.Static.render/3
        (phoenix_live_view 0.19.5) lib/phoenix_live_view/controller.ex:39: Phoenix.LiveView.Controller.live_render/3
        (phoenix 1.7.7) lib/phoenix/router.ex:430: Phoenix.Router.__call__/5
        (counter 1.7.7) lib/counter_web/endpoint.ex:1: CounterWeb.Endpoint.plug_builder_call/2
        (counter 1.7.7) deps/plug/lib/plug/debugger.ex:136: CounterWeb.Endpoint."call (overridable 3)"/2
        (counter 1.7.7) lib/counter_web/endpoint.ex:1: CounterWeb.Endpoint.call/2
        (phoenix 1.7.7) lib/phoenix/endpoint/sync_code_reload_plug.ex:22: Phoenix.Endpoint.SyncCodeReloadPlug.do_call/4
        (plug_cowboy 2.6.1) lib/plug/cowboy/handler.ex:11: Plug.Cowboy.Handler.init/2
        (cowboy 2.10.0) /Users/n/code/phoenix-liveview-counter-tutorial/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
        (cowboy 2.10.0) /Users/n/code/phoenix-liveview-counter-tutorial/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
        (cowboy 2.10.0) /Users/n/code/phoenix-liveview-counter-tutorial/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
nelsonic commented 11 months ago

Got it working. Just had to pass the val={@val} into the LiveView Component.

nelsonic commented 11 months ago

But have the following warnings:

warning: conflicting behaviours found. function handle_event/3 is required by Phoenix.LiveComponent and Phoenix.LiveView (in module CounterComponent)
  lib/counter_web/live/counter_component.ex:1: CounterComponent (module)

warning: conflicting behaviours found. function render/1 is required by Phoenix.LiveComponent and Phoenix.LiveView (in module CounterComponent)
  lib/counter_web/live/counter_component.ex:1: CounterComponent (module)
ndrean commented 11 months ago

May I ask why you would create a "sub LiveView"? I understand that the counter is already a LiveView, the unique one, so it comes with its own state and handlers. Isn't it redundant to create a "sub LivevVew" with the exact same state? Or I might have not understood your intention!

nelsonic commented 11 months ago

@ndrean great question. Apologies if the context is not clear from the OP. The goal is not to complicate this project. I'm adding an optional section in this tutorial to introduce LiveView Components. The idea is to intro the topic in the most basic App, a counter. Then I intend to apply this knowledge over in the MVP where is relevant to split out the Components. 👌

ndrean commented 11 months ago

ok ok . Maybe the button could be a component? you could probably use <Button.display click="inc"/> in the LiveView for example with the Phoenix.Component

attr :click, :string, required: true
def display(assigns) do
 ~H""" <button phx-click={@click}><%= @click %></button> """
end
nelsonic commented 11 months ago

Totally. There are additional opportunities for splitting out out further. PRs welcome. 👌 I'm just wrapping up the docs on this and updating the links to the Fly.io App. 🔗

nelsonic commented 11 months ago

liveview-counter-4-windows-component

nelsonic commented 11 months ago

With presence:

dwyl-liveview-counter-presence-genserver-state