I have seen shown code in this Go Rails-video at 05:15:
Video-tutorial
<%= form_with model: Link.new do |form| %>
# ...
<% end %>
Normally I would have written a new-action. Something like:
def new
@link = Link.new
end
Obviously it’s possible to skip the action and create the needed object on the fly, when invoking the form_with.
What’s the benefit of having a new-action?
Is it for having a separate route, which creates the form, when invoked?
The controller method is strictly speaking not actually needed but the main advantage to having a controller action is that it places the logic in one clear coherent place.
The view is inherently messy as it’s a combination of markup and code and it should really just be concerned with transforming data into HTML in the most straight forward way possible. Instanciating or querying for that data should be the responsibility of the controller.
This separation of concerns becomes more important as the level of complexity grows.
Besides the conceptual issues there are also direct practical reasons why <%= form_with model: Link.new do |form| %>
is a not a very good practice:
- It needlessly limits the resuse potential of form code. If you pass the model instance to the view the same code can easily be used for both creating and updating.
- You’re not binding the user input to the model instance so anything the user types in will be lost in an unsuccessful form submission.
- It’s not actually that much simpler or provides any real benefits over doing it the “right” way.
While you could handle this in the view with:
<%= form_with model: @link || Link.new do |form| %>
# ...
<% end %>
Again this is putting the logic in the view where it doesn’t belong.
Is it for having a separate route, which creates the form, when invoked?
No. Even if you don’t have a matching controller action rails will implicitly look for a view and render it.
Is it for having a separate route, which creates the form, when invoked?
Essentially — yes. If the client has otherwise knowledge what kind of request to make, it just makes a request directly to create
action and passes correct parameters. The example would be you testing create
via curl
in the command line, or a REST client making the requests (another programmer read the docs, and knows what to send in the body of the request – no new
call needed).
The browser is a “special” kind of client, and it needs to call new
so it can show a form to your user. They make choices based on the form widgets (type in stuff in text inputs, select options from selects/radio/checkboxes) and then click submit, which ends up as a POST that lands in the create
action.
The Link.new
part is not communicating to your app that you’re creating new object. It’s used by the form_with
helper and tries to fetch data to show in your form. (It also uses the model’s name to figure out what should the field names be, in your example the fields would be link[attribute_name]
). So passing an object (even empty one, like Link.new) is necessary for the framework to figure out all the details it needs to properly render the form. And properly rendered form means properly executes POST request on submission.
Usually, the fact that you’re creating new object is communicated by the fact there’s no id
field. Check your edit forms and see there’s a hidden id
field in there, or it’s in the form’s action URL.
To better understanding Link.new
in your form, do some experiments. For exampe you can do form_with model: Link.new(foo: "default value")
and of the rendered form would try to show field :foo
it would have a “default value” set. Or you can do SomeDifferentModel.new
and compare the resulting HTML form.