Closed gfrancischelli closed 7 months ago
{text|switch...}_field
with base components API duplicating.Moon.LiveView
. I'd like to see live_view components in a separate package/repo to avoid extra dependencies, so would like to have package-based namespace, e.g. MoonView
. discussable
- vanila: the only problem i do see here is additional layer of components
{text|switch...}_field
with base components API duplicating.
I'm not sure if I understood 🤔, which component is the duplicate of switch_field
?
- namespace: not sure about
Moon.LiveView
. I'd like to see live_view components in a separate package/repo to avoid extra dependencies, so would like to have package-based namespace, e.g.MoonView
. discussable
A fresh start sounds like a great idea
switch_field component will have all the attributes from switch_input (now knows as Form.switch). the rendering method itself will be a copy of checkbox_field. the same for input, textarea, select, ... this is how we break DRY here
in general, additional layer of vanila *_field components is hiding some templates complexity inside moon library. but it also can be done on a project-level.
Oh yeah, that's right. switch_input
will receive a subset of switch_field
assigns. I don't think that's a problem.
switch_field
and be happyswitch_field
and build your own shiny version of itThe value of an abstraction resides in:
Thus making switch_field
super valuable to regular users
In general, I'm content and pleased with the current approach.
Correct me if I'm wrong. Would this proposal change the way we consume all forms of inputs or only the ones wrapped with a form?
Namely, I find it's essential to be mindful of potential disruptions for users heavily reliant on input elements that aren't encapsulated within a form(us in 8io currently). Where we employ a controlled approach where the callsite retains full control over the component's state (we pass the inputs' values via props mostly and the component only serves a representational purpose), rather than relying on the <Form>
's abstraction. We've used the controlled approach due to sometimes requiring special handling from either overrides or where it's less markup to achieve the same results.
Perhaps, we can strike a balance and avoid unintended consequences for users who heavily leverage input
workflows.
The proposed pattern can easily accommodate formless inputs:
Consumer's just need to pass whatever mandatory values were calculated from the Form.Field
struct:
<.switch_field name="activate" value={@value} label="Activate" phx-change={ "my_event" or JS.command() } />
def switch_field(assigns) do
assigns = assigns_from_field(assigns)
~H"""
<div phx-feedback-for={@name}>
...
</div>
"""
end
def assigns_from_field(%{field: %Phoenix.HTML.FormField{} = field} = assigns) do
# Calculate assigns based on form field
end
# Ignore form otherwise
def assigns_from_field(assigns), do: assigns
The proposed pattern can easily accommodate formless inputs:
Perfect! I don't have any objections. I can see it have a positive effect for users who have to work with a lot of forms :muscle: .
Just use regular input
<.input type="switch />
This is a proposal for the Form Inputs API, which is currently being migrated from Surface to vanilla LiveView (#810)
TOC
Need
Moon form input components must fulfil the following requirements:
Phoenix.HTML.Form
APIThe non-goals are:
Ecto.Changeset
(those components must not be aware of it!)API Proposal
TLDR
field
andswitch
intoswitch_field
for better abstractionSimple Use Case
The
switch_field/1
should:label
,input
anderror
components so callers don't have totext_field
.Basic Customization
Simple approach: expose child assigns in
switch_field/1
The
switch_field/1
can also expose asize
assign which is forwarded to the label. What else the size could belong to?Advanced Customization
Simple approach: provide tools and building blocks to assemble entirely new components
Instead of chasing the holy grail of extensibility, MoonDS could provide the basic building blocks necessary for consumers to quickly assemble their own alternative version of every component.
Example:
When
switch_field
is composed of well-abstracted building blocks like.label
,.error
,.switch_input
andassigns_from_field
, consumers can effortlessly assemble their own alternative versionmy_custom_switch_field
.Allowing
switch_field
,checked_star_switch_field
,sparks_switch_field
to be written with the same building blocks removes a ton of complexity and mental gymnastics necessary for chasing endless customization.Adoption Strategy
Successfull adoption depends on running this proposal through:
Giving consumers a chance and structure to provide feedback and express concerns is an act of respect. Once the benefits of the approach are clearer to consumers the drawbacks of adopting a new version will be more palatable.
Possible adoption path
Moon.LiveView.*
namespaceContext
Moon is currently being ported from Surface to vanilla LiveView. The migration strategy is to keep a 1:1 parity with the old API (correct if I'm wrong), leveraging the use of a translator script which converts Surface code to a similar vanilla LiveView implementation.
This strategy can't possibly succeed. What the library consumers need isn't just to eliminate Surface dependency, but also to eliminate Surface design patterns and mental model as a whole.
Current Moon LiveView API and its shortcomings
The formerly proposed LiveView api is problematic due to the dogmatic usage of compound components design:
Basic:
The most common use case requires consumers to write too much markup and duplication.
Advanced customization:
If the basic scenario requires too much markup, advanced one is plagued by too much repetition and markup. It is fragile against changes plus it doesn't do a complete job at making customization possible, since the
error
section is impossible to customize.There's no silver buller
Compound Components are currently seen as a silver-bullet and the technique is applied even when unecessary. Any dogmatic approach is superficial and harmful. MoonDS requires actual design principles to succeed, instead of rules to follow blindly.
Clarifying the problem
The problem solved by this technique is to:
Only part
1.
is relevant to form inputslabel
anderror
belong to a wide array of different inputs.Alternatives
1) Use
:let
to forward the child elements assignsswitch_input
element, which is used internally byswitch_field
@inner_block
slot, skip normal renderinglabel
andswitch_input
assigns using:let
, so the API user has full controlA very nice advantage of this technique is hiding implementation details of which assigns
label
andswitch_input
require to be wired well withswitch_field
. Moon is free to change as many of them as desired without incurring breaking changes on consumers, or requiring knowledge about which assigns they are.It's not my preferred option for the Form Inputs, but it will definitely be useful to other types of components.
2) Use
:let
to forward the child elements assigns AND child function componentsThis is but an experimental option. I'm not sure if it would incur perf issues. The technique allows forwarding and entire component, and not only it's assigns.
About this Document
This document is written like it's meant for a "Request for Comments" (RFC) process. It's a practice from mature engineering organizations to:
Moon could greatly benefit from an RFC process and help the company spread this practice. Read more about React RFC Process or the A Structured RFC Process.