mssantosdev

mssantosdev

Hey, everyone.

I’ve been working with Phoenix Components for a few weeks, and they are great. One thing I use a lot when building custom components is the global attribute with default values set. It’s a good option for building reusable elements without repeating stuff every time.

However, I’ve noticed a “pattern” when creating components with class modifiers.

Suppose I have a <.paragraph />:

attr :rest, :global, default: %{class: "paragraph"}
slot :inner_block, required: true

def paragraph(assigns) do
  ~H"""
  <p {@rest}><%= render_slot(@inner_block) %></p>
  """
end
<.paragraph>My paragraph</.paragraph>
<p class="paragraph">My paragraph</p>

The problem is that I have modifiers for my paragraph, like paragraph--medium, paragraph--small, or even other types of classes like is-hidden, is-animateable etc. To use them, I need to repeat its base class.

<.paragraph class="paragraph paragraph--with-modifier">
  This is a paragraph with a modifier
</.paragraph>
<p class="paragraph paragraph--with-modifier">My paragraph</p>

Of course, changing how things currently work because I don’t want to repeat a class is not a good idea, and I understand the default value is not the best option for what I’m trying to do. But things can get more complex as we have more than a single base class.

Also, I don’t think creating additional components to consider all the modifiers they could have is a good idea. It’s just simpler to use classes.

Now, to the suggestion: what if we had something like an initial property for the class in global attributes? Values from it would be combined with the values passed to the component automatically.

attr :rest, :global, initial: %{class: "paragraph is-animateable is-i-dont-know"}
slot :inner_block, required: true

def paragraph(assigns) do
  ~H"""
  <p {@rest}><%= render_slot(@inner_block) %></p>
  """
end
<.paragraph class="paragraph--with-modifier">
  This is my paragraph with modifiers
</.paragraph>
<p class="paragraph is-animateable is-i-dont-know paragraph--with-modifier">This is my paragraph with modifier</p>

I read this article, which is a great workaround, but maybe we can improve it.

What do you all think?

Thanks!

First 5 of 5 Posts Switch mode

LostKobrakai

LostKobrakai

attr :class, :string
attr :rest, :global
slot :inner_block, required: true

def paragraph(assigns) do
  ~H"""
  <p class={["paragraph is-animateable is-i-dont-know", @class]} {@rest}>
    <%= render_slot(@inner_block) %>
  </p>
  """
end
mssantosdev

mssantosdev OP

I mentioned your solution in the post, @LostKobrakai.

LostKobrakai

LostKobrakai

Ah, didn’t look at that one. I wouldn’t consider this a workaround. That’s imo the business logic of your component. Sure initial_value sounds easy, but you can compose passed data with defaults in multiple ways, not just one. How would you decide if the syntax you showed combines values or replaces the default or does some more complex transformation? Even combining values is not a trivial assumption to make, as it might work well for classes or styles, but unlikely for much else.

aiwaiwa

aiwaiwa

{@rest} could be tweaked inside the render before returning ~H""" like is done in core_components:


  def input(%{type: "checkbox", value: value} = assigns) do
    assigns =
      assign_new(assigns, :checked, fn -> Phoenix.HTML.Form.normalize_value("checkbox", value) end)

to tweak the list of classes based on availability of another class.

mssantosdev

mssantosdev OP

I agree. There are multiple ways we can accomplish that. My point is: should we have something out-of-the-box for this case?

Yes. Classes/styles/data-attributes are probably the only use-cases for this as they can have multiple values.

— All posts loaded —

Where Next? Top

Trending in Proposals: Ideas Top

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New

We're in Beta

About us Mission Statement