Search code examples
ruby-on-railsrubysimple-form

Different Ways to use simple_form in Ruby on Rails | What is the difference?


What is the difference between these methods of addressing an edit form for "packs"?

How do these different approaches affect the stability of the application? It's versatility?

Where would these run into major problems? Why is one better than the other?

  1. Via "method?" <%= simple_form_for Pack.find(params[:id]), method: :patch do |f| %>
  2. Via Object <%= simple_form_for @pack, url: pack_path(@pack), method: :patch do |f| %>
  3. Via Symbol <%= simple_form_for :pack, url: pack_path(@pack), method: :patch do |f| %>

Solution

  • 1. and 2. - Binding a form helper to a model instance

    Are basically identical besides the fact that in 2. you're referencing a model instance from an instance variable instead of just passing the return value of a method call.

    The end result is that you get a form that is bound to a model instance. This means that the value attributes of the inputs will contain the values from the model.

    In fact all of these will give the same result:

    <% 
      @pack = Pack.find(params[:id]) 
      pack = Pack.find(params[:id]) 
    
    %>
    
    <%= simple_form_for Pack.find(params[:id]), method: :patch do |f| %>
    <%= simple_form_for @pack |f| %>
    <%= simple_form_for pack |f| %>
    

    But the first is less desirable since you should avoid doing queries directly from the view. The view should recieve data from the controler and use it to create HTML and be as simple and declarative as possible. Another major problem is that .find will raise ActiveRecord::RecordNotFoundError which should have been caught much earlier in the controller if the id is invalid.

    3. Calling the form helpers with a symbol:

    <%= simple_form_for :pack, url: pack_path(@pack), method: :patch do |f| %>
    

    This creates a form that is "scoped" without necissarily being bound to an specific model instance. Take this example:

    <%= simple_form_for :thing do  |f| %>
      <%= f.input :name %>
    <% end %>
    

    This generates the following HTML:

    <form novalidate="novalidate" class="simple_form thing" action="/things/new" accept-charset="UTF-8" method="post">
      <input name="utf8" type="hidden" value="&#x2713;" /><input type="hidden" name="authenticity_token" value="F4r1gLuboZc1CKIdn9qac0sefxSVIvkjxk9KsD+sRl1wnVtEIKzHvWY0mPuLPvHI1Kcv3TIWD883YXHKXA+yJQ==" />
      <div class="input string required thing_name">
        <label class="string required" for="thing_name"><abbr title="required">*</abbr> Name</label>
        <input class="string required" type="text" name="thing[name]" id="thing_name" />
      </div>
    </form>
    

    Note name="thing[name]" which means it will give a params hash which is just like if we had a model instance:

    {
       thing: {
         name: "foo"
       }
    }
    

    This is not as commonly used (at least not correctly) since most of the time you're manipulating model instances but it does have a use in special cases like search forms.

    Note that Rails will try to resolve the local variable @thing but will not raise an error if it is nil. Using this form is therefore generally not advised as it can mask errors.

    The best way to understand this behavior is to read the docs for ActionView::FormHelpers which SimpleForm is just sugar on top of.