View Source Love.View and Love.Component

Test Status Module Version Hex Docs License

Love provides a sprinkle of functionality on top of Phoenix LiveView to improve developer ergonomics.

It doesn't replace the LiveView way of doing things, but instead augments it with conventions. Love can be added to any view or component without affecting existing functionality, and features can be opted-in as they are needed.

LiveViews and LiveComponents both gain:

  • State assigns which are defined in the module
  • Reactive functions that are automatically invoked upon state changes
  • Universal event handling via the handle_message/4 callback on a LiveView or LiveComponent
  • Runtime checks with developer-friendly error messages

LiveComponents also gain additional functionality:

  • Props which are assigns that are strictly passed in to the component
  • Slot props to represent component slots
  • Event props to represent events that can be raised by the component and universally handled via handle_message/4

Here are a couple examples showing off these features. For complete details, please reference the docs.

love-view-example

Love.View Example

defmodule MyAppWeb.ProfileIndexLive do
  use Phoenix.LiveView
  use Love.View

  state :profiles, default: load_profiles()
  state :profile_count
  state :selected_profile_id, default: nil

  def handle_message(:on_selected, {MyAppWeb.UserProfileComponent, _id}, profile_id, socket) do
    put_state(socket, selected_profile_id: profile_id)
  end

  @react to: :profiles
  defp put_profile_count(socket) do
    put_state(socket, profile_count: length(socket.assigns.profiles))
  end

  defp load_profiles, do: # ...

  # ...
end

The :profiles, :profile_count, and selected_profile_id assigns are state. They can be modified throughout the LiveView lifecycle, and reactive functions can respond to their changes.

The handle_message/4 callback is part of the Love.View behaviour. It's handling the event emitted by UserProfileComponent, which is wired up via its :on_selected event prop (see below).

The put_profile_count/1 callback is a reactive function that is automatically invoked as soon as any changes occur to the :profiles state.

love-component-example

Love.Component Example

defmodule MyAppWeb.UserProfileComponent do
  use Phoenix.LiveComponent
  use Love.Component

  prop :profile
  prop :show_avatar?, default: false

  state :age
  state :expand_details?, default: false

  slot :inner_block

  event :on_selected

  def handle_event("toggle-details", _, socket) do
    {:noreply, put_state(socket, socket, expand_details?: not socket.assigns.expand_details?)}
  end

  def handle_event("select", %{"profile_id" => profile_id}}, socket) do
    {:noreply, emit(socket, :on_selected, profile_id)}
  end

  @react to: :profile
  defp put_age(socket) do
    age = trunc(Date.diff(Date.utc_today(), socket.assigns.profile.birthday) / 365)
    put_state(socket, age: age)
  end

  # ...
end

The :profile assign is a required prop. If it is not passed in, a helpful runtime error will occur.

The :id assign is also a required prop, but it is implicitly defined by use Love.Component, because every LiveComponent requires an :id.

The :show_avatar? assign is an optional prop that defaults to false when unspecified.

The :expand_details? and :age assigns are state. They can be modified via put_state/2.

The :inner_block assign is a slot prop. It is required but can be made optional with the required?: false option.

The :on_expanded assign is a event prop. Events raised via emit/3 can be handled by any Love.View or Love.Component that implements the universal handle_message/4 callback. Pass in a pid to send a message to a Love.View, or {module, id} to send a message to a Love.Component.

The put_age/1 function is a reactive callback. It is automatically evaluated whenever the value of the :profiles state changes. The function can react to prop changes and state changes.

installation

Installation

The package can be installed by adding love_ex to your list of dependencies in mix.exs:

def deps do
  [
    {:love_ex, "~> 0.2.0"}
  ]
end