Last active
March 8, 2022 17:43
-
-
Save bradgessler/f855f03e1ffac3224ef92700ed66870c to your computer and use it in GitHub Desktop.
Code I'm using in production to replace Rails form helpers. The benefit over Rails or Simple Form? I can keep my views in my views and my logic in Ruby classes. If you compare with Simple Form, their view code is in an initializer, which requires a reboot for every change. Rails form helpers don't have a great way to let users define views in te…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
class FormComponent < ViewComponent::Base | |
attr_reader :model, :url, :data | |
renders_many :inputs, -> (field, as: :text, hint: nil, label: nil) do | |
case as | |
when :hidden | |
InputComponent.new(model: model, field: field, type: :hidden) | |
else | |
TextFieldComponent.new(model: model, field: field, type: as, hint: hint, label: label) | |
end | |
end | |
renders_many :buttons, -> (type, value) do | |
ButtonComponent.new(value: value, type: type) | |
end | |
def initialize(model:, url:, data:) | |
@model = model | |
@url = url | |
@data = data | |
end | |
# Basic functionality for mapping a model attribute. | |
class InputComponent < ViewComponent::Base | |
attr_reader :type, :field, :model | |
def initialize(model:, field:, type:) | |
@model = model | |
@field = field | |
@type = type | |
end | |
def name | |
helpers.field_name model.model_name.param_key, @field | |
end | |
def id | |
"#{model.model_name.param_key}_#{@field}" | |
end | |
def value | |
model.send field | |
end | |
end | |
class HiddenFieldComponent < InputComponent | |
def type | |
:hidden | |
end | |
end | |
# Handles more functionality such as the label for the field, | |
# errors, and other information that an input would need that's | |
# not hidden and is a textbox. | |
class TextFieldComponent < InputComponent | |
attr_reader :hint | |
def initialize(hint: nil, label: nil, **kwargs) | |
@label = label | |
@hint = hint | |
super(**kwargs) | |
end | |
def label | |
@label ||= field.to_s.humanize | |
end | |
def errors | |
model.errors[field] | |
end | |
def error_message | |
"#{label} #{model.errors.generate_message(field)}" | |
end | |
def invalid? | |
errors.any? | |
end | |
def error_classes | |
"border-red-500" | |
end | |
def valid_classes | |
"" | |
end | |
def classes | |
invalid? ? error_classes : valid_classes | |
end | |
end | |
class ButtonComponent < ViewComponent::Base | |
attr_reader :value, :type | |
def initialize(value:, type:) | |
@value = value | |
@type = type | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
input type=type value=value name=name id=id / |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
= article_component title: "Sign in with email", subtitle: "We'll email you a code so you can sign in" | |
= form_component model: @email_authentication, url: create_url, data: { turbo: false } do |f| | |
.my-8 | |
= f.input :email, as: :email, hint: "We only use this email address to sign you into your account" | |
.my-8 | |
= f.button :submit, "Email sign in code →" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
label.block.text-gray-700.dark:text-gray-200.font-bold.mb-2 for=id | |
= label | |
input.shadow.appearance-none.border.rounded.w-full.py-2.px-3.text-gray-700.mb-1.leading-tight.focus:outline-none.focus:shadow-outline.dark:bg-neutral-800.dark:text-neutral-200 class=classes type=type value=value name=name id=id / | |
-if invalid? | |
p.text-red-600.text-sm.my-1=error_message | |
-if hint | |
p.hint= hint |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment