rootstrap/yaaf

View on GitHub
docs/recipes/nested_forms.md

Summary

Maintainability
Test Coverage
# Using YAAF with nested forms

When you need to create/update a collection of models you can use nested forms.

## Usage

Add the nested form objects to the `@models` of the base one. To render the form using Rails helpers you might need to define an `attr_accessor` for the collection.

For example, a bulk invites form will look like this:

```ruby
# app/forms/bulk_invites_form.rb

class BulkInvitesForm < ApplicationForm
  # invites_attributes is needed in order to use the
  # fields_for helper with a collection
  attr_accessor :invites_params, :invites_attributes
  validate :amount_of_invites

  def initialize(args = {})
    super(args)

    @models = [filled_invites].flatten
  end

  def invites
    @invites ||= Array.new(5) do |i|
      InviteForm.new(
        invites_params&.dig(:invites_attributes, i.to_s)
      )
    end
  end

  private

  def filled_invites
    @filled_invites ||= invites.select { |invite| invite.email.present? }
  end

  def amount_of_invites
    return if filled_invites.size.between?(1, 5)

    errors[:base] << 'You need to send between one and five invites'
  end
end
```

```ruby
# app/forms/invite_form.rb

class InviteForm < ApplicationForm
  attr_accessor :email
  validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }

  def initialize(args = {})
    super(args)

    @models = [invite]
  end

  private

  def invite
    @invite ||= Invite.new(invited_user_email: email)
  end
end
```

```ruby
# app/controllers/invites_controller.rb

class InvitesController < ApplicationController
  def new
    @form = BulkInvitesForm.new
  end

  def create
    @form = BulkInvitesForm.new(invites_params: invites_params)

    if @form.save
      flash[:success] = 'Invites have been sent successfully'
      redirect_to root_path
    else
      render :new
    end
  end

  private

  def invites_params
    params.require(:bulk_invites_form).permit(invites_attributes: [:email])
  end
end
```

```erb
# app/views/invites/new.rb

<%= simple_form_for(@form, url: invites_path) do |f| %>
  <%= f.error_notification %>

  <% f.object.errors.messages[:base].each do |message| %>
    <li><%= message %></li>
  <% end %>

  <div class="form-inputs">
    <%= f.simple_fields_for :invites do |ff| %>
      <%= ff.input :email %>
    <% end %>
  </div>

  <div class="form-actions">
    <%= f.submit 'Send invites' %>
  </div>
<% end %>
```

## Got questions?

Feel free to [create an issue](https://github.com/rootstrap/yaaf/issues) and we'll discuss about it.

YAAF is maintained by [Rootstrap](http://www.rootstrap.com) with the help of our [contributors](https://github.com/rootstrap/yaaf/contributors).

[![YAAF](../images/footer.png)](http://www.rootstrap.com)