How to Use Reform to Prepopulate for Featured Objects

How to use Reform to prepopulate for featured objects?

Try this:

challenges_controller

  def new
@form = ChallengeForm.new(Challenge.new)

respond_modal_with @form, location: root_path
end

def create
challenge = Challenge.new(challenge_params)
@form = ChallengeForm.new(challenge)

if params[:step] == '2'
@form.validate(user_id: current_user.id)
@form.save
redirect_to challenge
end
end

challenges/new.html.erb

<%= simple_form_for @form, html: { data: { modal: true } }, url: 'your_challenge_create_path', method: :post do |f| %>

<%= f.text_field :action, placeholder: 'Enter a Custom Challenge' %><br>
Or choose a featured challenge:
<%= f.collection_radio_buttons :action, [['Run a Mile','Run a Mile'], ['Drink 16oz of Water','Drink 16oz of Water'], ['Take a Picture','Take a Picture'], ['1 Drink Max','1 Drink Max'], ['See Eiffel Tower','See Eiffel Tower'], ['Write a Book','Write a Book'], ['Skydive','Skydive'], ['Start a Business','Start a Business'], ['No Snooze','No Snooze'], ['Visit All 50 States','Visit All 50 States'], ['Talk to a Stranger','Talk to a Stranger'], ['Try a New Recipe','Try a New Recipe'], ['Media-fast','Media-fast']], :first, :last %>
<%= f.submit %>
<% end %>

challenges/create.html.erb

<%= simple_form_for @form, html: { data: { modal: true } }, url: 'your_challenge_create_path', method: :post do |f| %>
<%= hidden_field_tag :step, 2 %>
Challenge: <%= f.text_field :action %>
Do For: <%= f.number_field :days_challenged %>
Do On: <%= f.collection_check_boxes :committed %>
<% end %>

I might be a bit off, but you get the point?

How to prepopulate _form for featured objects?

You can use simple_form with reform. Reform will give you form object, where you can override methods that will populate your form.

Here is a watered-down example (you will have to adjust it to your case):

class ChallengeForm < Reform::Form
property :action
property :committed
property :days_challenged

model :challenge

def commited
super || action_to_commited_hash[model.action]
end

def days_challenged
super || action_to_days_challenged_hash[model.action]
end

def action_to_days_challenged_hash
{
'Run a Mile' => 30,
'Take a Picture' => 12
}
end

def action_to_commited_hash
{
'Run a Mile' => ['Mon', 'Wed', 'Fri'],
'Take a Picture' => ['Tu', 'Thu']
}
end
end

super in the methods above will delegate to the model. Note that you are overriding getter methods, and it doesn't affect setters (you can override setters too if you wanted to change form data before writing it).

In your template, instead of

form_for @challenge

you will have:

simple_form_for @form

It's a super common form library for Rails and I can't imagine not using it myself!

Populating single property in Reform

populator and prepopulator assume that the relevant properties are present in the incoming data. In your case, interval is not.

Do you want to validate the interval value? If not, calculate interval in an Operation step and don't involve reform.

If you want validation, your options are:

  • a populator on either timestamp:

    # some_form.rb
    class SomeForm < Reform::Form
    property :starts_on
    property :ends_on, populator: ->(model:, fragment:, **) do
    self.interval = fragment - starts_on
    self.ends_on = fragment
    end
    property :interval, readable: false

    validation do
    required(:starts_on).filled(:time?)
    required(:ends_on).filled(:time?)
    required(:interval).filled(gt?: 2.hours) # or whatever value you like
    end
    end
  • a custom predicate+rule combo:

    # some_form.rb
    class SomeForm < Reform::Form
    property :starts_on
    property :ends_on

    # NOTE: uses dry-validation v0.13 syntax
    validation do
    configure do
    config.messages_file = "/path/to/your/error_messages.yml"

    def gt?(interval, starts_on, ends_on)
    ends_on - starts_on > interval
    end
    end
    end

    required(:starts_on).filled(:time?)
    required(:ends_on).filled(:time?)
    rule(ends_on: [:ends_on, :starts_on]) do |ends_on, starts_on|
    ends_on.gt?(starts_on, 2.hours) # or whatever value you like
    end
    end
    end

    # error_messages.yml
    en:
    errors:
    rules:
    ends_on:
    gt?: "must be greater than starts_on by %{interval} seconds"

Prepopulate Reform with GET vars

To follow the docs on Trailblazer GitHub:

class AlbumsController
def new
@form = AlbumForm.new(Album.new)
end

should be:

class AlbumsController
def new
@form = AlbumForm.new(Album.new(language: params[:lang]))
end

Reform gem: use one model for multiple forms

Accidentally I've found an answer. The actual problem was my module name was Address what is an exactly name of one of my models. And it made some conflict obviously. So I've changed Address to AddressModule.

And my working setup will be:

class Checkout < Reform::Form
extend ::ActiveModel::Callbacks
#...
property :billing_address, populate_if_empty: Address, form: BillingAddress
property :shipping_address, populate_if_empty: Address, form: ShippingAddress
#...
end

class BillingAddress < Reform::Form
extend ::ActiveModel::Callbacks
include AddressModule
end

class ShippingAddress < Reform::Form
extend ::ActiveModel::Callbacks
include AddressModule
end

module AddressModule
include Reform::Form::Module

property :firstname
property :lastname
property :address1
property :address2
property :phone
property :city
property :zipcode
property :country_id
property :billing_address_for_id
property :shipping_address_for_id

validates :firstname,
:lastname,
:address1,
:phone,
:city,
:zipcode,
:country_id,
presence: true

# provided by phony_rails gem
# validates phone number to be correct and plausible
# without country accordance
validates :phone, phony_plausible: { ignore_record_country_code: true }

# provided by validates_zipcode gem
# validates zipcode to be correct due to country alpha2 code
validates :zipcode, zipcode: { country_code: :country_code }
end

Validation of a set value with Reform

According to reform documentation field can be validated with inclusion:

validates :status, inclusion: { in: %w(open closed) }

undefined method `persisted?' with Reform and Virtus model

It seems like the comment from @fanta helped, but I the long-term answer is that you should avoid using Virtus, especially since you are building a new project. Virtus is no longer supported by it's own team, they moved on to dry-rb ( dry-types and dry-validations)

If you need to mock a model - you can use dry-struct, OpenStruct, etc

Also Reform now has full support for dry-validation and dry-types, and it is going to be the way of the future ( thou AM is going to be supported till version 4)
Best of luck :-)



Related Topics



Leave a reply



Submit