Home Posts Post Search Tag Search

LiveView 17 - Chapter 6: Your Turn
Published on: 2025-12-30 Tags: elixir, Blog, LiveView, Html/CSS, Phoenix

Your Turn

    You have started the process to have a user build their demographic and then start to have the user rate products. 

    Give It a Try
        Build a new component within the component.ex with a heading and configurable message. What are the strengths and weaknesses of this approach?
            This approach is nice as it allows you to have any title message as one set standard. You can add in the component and it will all look the same. All you will need to do is set the header and message when using the component.
            The weakness here might be that you will only be able to use a header and message where there might be times that you want to add in a list or something else. Also if you make any change to the component any place in your site it will change.
                    attr(:heading, :string, required: true)
                    attr(:message, :string, required: true)

                    def heading_message(assigns) do
                        ~H"""
                        <div class="hero bg-gradient-to-r from-green-400 to-white-300">
                        <div class="hero-content text-center py-16">
                            <div class="max-w-md">
                            <h1 class="mb-5 text-5xl font-bold">{@heading}</h1>
                            <div class="mb-5 text-lg">{@message}</div>
                            </div>
                        </div>
                        </div>
                        """
                    end 
        Build a function component that will render and HTML list item. Then build an other component that will use that component list item to render an entire list. Can you configure the components to render any given list of items? Can you think of ways that you could use this to build live views in a more organized and layered way?
            So for this I created 2 components that will render an item and then the list of items. I added in some defaults and a way to send your own style to the list.
                    attr(:style, :string, default: "text-blue-500")
                    slot(:inner_block, required: true)

                    def list_item(assigns) do
                        ~H"""
                        <li class={@style}>{render_slot(@inner_block)}</li>
                        """
                    end

                    attr(:list, :list, required: true)
                    attr(:item_style, :string, default: "text-blue-500")
                    attr(:list_style, :string, default: "border border-black-500 rounded p-2")

                    def list(assigns) do
                        ~H"""
                        <ul class={@list_style}>
                        <%= for item <- @list do %>
                            <.list_item style={@item_style}>
                            {item}
                            </.list_item>
                        <% end %>
                        </ul>
                        """
                    end
            You can use it like so long as @list is a list of items
                    <Component.list> {@list} </Component.list>
            This is nice as again you only have to send it a list and it will put all the items and you don't need to worry about how its structured as there are defaults. 

            You might even be able to use this to print entire pages within a box.

        Update the table function in CoreComponents to contain a new attribute that allows you to set the style of the first column of the table however you like.
            For this I had to add in a Enum.with_index to be sure that we had the right column that we are using. I also didn't want to mess with the already existing structure of the .table so I added in a attr that doesn't need a value and has a default. I'll just add in the new code and the logic for the first column.
                    attr(:first_col_style, :string, default: "")

                    <td
                        :for={{col, index} <- Enum.with_index(@col)}
                        phx-click={@row_click && @row_click.(row)}
                        class={[@row_click && "hover:cursor-pointer", index == 0 && @first_col_style]}
                    >

                # You get to call it with
                    <.table
                        id="products"
                        rows={@streams.products}
                        row_click={fn {_id, product} -> JS.navigate(~p"/products/#{product}") end}
                        first_col_style="hover:text-red-500"
                    >
            Keep in mind that you will still need to have the :col for the columns that you build.

        Work on using the pento_web.ex to set the live_view function with the layout already each page that uses use Pento.Web, :live_view
            I think this is the new way that live view works. I really should just add the nav bar to the root. but I feel like it works better where it is. I also think that I would need to have the toggle for the light/dark theme on every page and that is part of the Layouts.app.

            Ill see how it looks to add in the nav bar to the root.html.heex
                    <body>
                        <ul class="menu menu-horizontal w-full relative z-10 flex items-center gap-4 px-4 sm:px-6 lg:px-8 justify-end">
                        <%= if @current_scope do %>
                            <li>
                            {@current_scope.user.email}({@current_scope.user.username || "no username"})
                            </li>
                            <li>
                            <.link href={~p"/users/settings"}>Settings</.link>
                            </li>
                            <li>
                            <.link href={~p"/users/log-out"} method="delete">Log out</.link>
                            </li>
                        <% else %>
                            <li>
                            <.link href={~p"/users/register"}>Register</.link>
                            </li>
                            <li>
                            <.link href={~p"/users/log-in"}>Log in</.link>
                            </li>
                        <% end %>
                        <li>
                            <.theme_toggle />
                        </li>
                        </ul>
                        <div class="flex items-center gap-4 px-4 sm:px-10 lg:px-16 justify-center border rounded p-2">
                        <div>
                            <a href="/guess" class="btn btn-ghost"> Guessing Game
                            </a>
                            <a href="/survey" class="btn btn-ghost"> Survey
                            </a>
                            <a href="/products" class="btn btn-ghost"> Products
                            </a>
                            <a href="/questions" class="btn btn-ghost"> FAQ
                            </a>
                            <a href="/promo" class="btn btn-ghost"> Promo
                            </a>
                            <a href="/search" class="btn btn-ghost"> Search
                            </a>
                        </div>
                        </div>
                        {@inner_content}
                    </body>