stimulus_reflex: Params incorrect for form submitted for nested resource
Bug Report
Bug Description
When I submit the form using without StimulusReflex all of the parameter data is present including:
id: 6
post_id: "photos-reveal-devastation-from-oil-spill-in-mauritius"
<ActionController::Parameters {"_method"=>"patch", "authenticity_token"=>"MDtxGtFe65lUnSZ8K32PdwNNBv7aAlOAmkAhjkKio7IrMTOdfMmn1s6M6vTsMarWRiBVmoUips7El17UxgToYw==", "element"=>{"content"=>"<h2><strong>Enim nulla aliquet porttitor lacus luctus accumsan. </strong></h2><div><br>Cursus euismod quis viverra nibh cras pulvinar mattis nunc. Urna id volutpat lacus laoreet non. Ut tortor pretium viverra suspendisse. something</div>"}, "commit"=>"Save", "controller"=>"authors/elements", "action"=>"update", "post_id"=>"photos-reveal-devastation-from-oil-spill-in-mauritius", "id"=>"6"} permitted: false>
However, when I submit a form using StimulusReflex the params that are submitted along with the form are including different data and are missing the post_id:
id: "photos-reveal-devastation-from-oil-spill-in-mauritius"
no post_id present
<ActionController::Parameters {"controller"=>"authors/posts", "action"=>"edit", "id"=>"photos-reveal-devastation-from-oil-spill-in-mauritius", "_method"=>"patch", "authenticity_token"=>"oBnP815mQ/E0IltrBnc4BB5CQurRxiA4o9qZTVE3dWj0tLy+krC2nN+Sy8+sAvN9gueYsx87YBc2YxQTrScJdg==", "element"=>{"content"=>"<h2><strong>Enim nulla aliquet porttitor lacus luctus accumsan. </strong></h2><div><br>Cursus euismod quis viverra nibh cras pulvinar mattis nunc. Urna id volutpat lacus laoreet non. Ut tortor pretium viverra suspendisse. something</div>"}} permitted: false>
I have the following in my routes.rb file:
scope module: 'authors' do
resources :posts do
resources :elements
end
end
I have a Post made up of many elements:
class Post < ApplicationRecord
has_many :elements
end
class Element < ApplicationRecord
belongs_to :post
end
On my edit post page I have one form for each of the elements that comprise the post: views/posts/edit.html.erb
<%= @post.element.each do |element| %>
<%= render partial: 'paragraph_form', locals: { post: @post, element: element} %>
<% end %>
Paragraph Form
<%= form_with(model: [post,element], data: { reflex: "submit->ElementsReflex#update", signed_id: element.to_sgid.to_s}) do |form| %>
<%= form.rich_text_area :content %>
<div class="mt-3 d-flex justify-content-between">
<div>
<%= form.submit "Save", class: 'btn btn-primary' %>
<a href="#" class="cancel btn btn-secondary">Cancel</a>
</div>
<%= link_to 'Delete', post_element_path(element.post, element), method: :delete, class: 'btn btn-danger', data: { confirm: 'Are you sure?'} %>
</div>
<% end %>
This resolves correctly and I have the following in my form (simplified):
<form data-reflex="submit->ElementsReflex#update" action="/posts/new-titles/elements/16" data-remote="true" method="post" data-action="submit->elements#__perform">
Here is the full output from the submission:
StimulusReflex::Channel#receive( {"target"=>"ElementsReflex#update", "args"=>[], "url"=>"http://localhost:3000/posts/new-titles/edit", "attrs"=>{ "data-reflex"=>"submit->ElementsReflex#update", "data-signed-id"=>"BAh7CEkiCGdpZAY6BkVUSSIcZ2lkOi8vcy1ibG9nL0VsZW1lbnQvMTYGOwBUSSIMcHVycG9zZQY7AFRJIgxkZWZhdWx0BjsAVEkiD2V4cGlyZXNfYXQGOwBUSSIdMjAyMC0wOS0xM1QxNzoyNDo0Ny40NjdaBjsAVA==--707039c8db2558e68dd5af108e93ebdfd2d2c9b1", "action"=>"/posts/new-titles/elements/16", "accept-charset"=>"UTF-8", "data-remote"=>"true", "method"=>"post", "data-action"=>"submit->elements#__perform", "checked"=>false, "selected"=>false, "tag_name"=>"FORM"}, "dataset"=>{"data-reflex"=>"submit->ElementsReflex#update", "data-signed-id"=>"BAh7CEkiCGdpZAY6BkVUSSIcZ2lkOi8vcy1ibG9nL0VsZW1lbnQvMTYGOwBUSSIMcHVycG9zZQY7AFRJIgxkZWZhdWx0BjsAVEkiD2V4cGlyZXNfYXQGOwBUSSIdMjAyMC0wOS0xM1QxNzoyNDo0Ny40NjdaBjsAVA==--707039c8db2558e68dd5af108e93ebdfd2d2c9b1", "data-remote"=>"true", "data-action"=>"submit->elements#__perform"}, "selectors"=>[], "reflexId"=>"e687bf2e-e108-4200-adbe-aab85ab99ba5", "permanent_attribute_name"=>"data-reflex-permanent", "params"=>{"_method"=>"patch", "authenticity_token"=>"dSWS/Ezp/wjaVGs4fyPT+rxJBstorYswpWCqk6pdp1HXf2ot+eK5V0/VCmaaKhcwNu7gac8n3dAqno6QYVLptQ==", "element"=>{"content"=>"<div>another one bites the</div>"}}})
To Reproduce
Create a page that contains several forms. The forms are for the has_many model of the association. Submit the form to a Stimulus Reflex reflex and examine the params object.
Expected behavior
I would expect that the params submitted via a Reflex would be the same as if I submitted the form to a Rails controller.
Versions
StimulusReflex
- Gem: stimulus_reflex 3.2.3
- Node package: “stimulus_reflex”: “^3.3.0-pre2”,
External tools
- Ruby:ruby 2.6.5p114
- Rails: 6.0.3.2
- Node: v14.5.0
Browser
- Browser Firefox
- Version 79
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 17 (10 by maintainers)
Commits related to this issue
- Manage form params vs. URL params. And replace state with params. "One needs to understand what the source of the params in a reflex is. And there are two sources: the closest form the url of the cu... — committed to openfoodfoundation/openfoodnetwork by jibees a year ago
- Manage form params vs. URL params. And replace state with params. "One needs to understand what the source of the params in a reflex is. And there are two sources: the closest form the url of the cu... — committed to openfoodfoundation/openfoodnetwork by jibees a year ago
- Manage form params vs. URL params. And replace state with params. "One needs to understand what the source of the params in a reflex is. And there are two sources: the closest form the url of the cu... — committed to openfoodfoundation/openfoodnetwork by jibees a year ago
- Manage form params vs. URL params. And replace state with params. "One needs to understand what the source of the params in a reflex is. And there are two sources: the closest form the url of the cu... — committed to jibees/openfoodnetwork by jibees a year ago
- Manage form params vs. URL params. And replace state with params. "One needs to understand what the source of the params in a reflex is. And there are two sources: the closest form the url of the cu... — committed to jibees/openfoodnetwork by jibees a year ago
- Manage form params vs. URL params. And replace state with params. "One needs to understand what the source of the params in a reflex is. And there are two sources: the closest form the url of the cu... — committed to jibees/openfoodnetwork by jibees a year ago
- Manage form params vs. URL params. And replace state with params. "One needs to understand what the source of the params in a reflex is. And there are two sources: the closest form the url of the cu... — committed to jibees/openfoodnetwork by jibees a year ago
- Manage form params vs. URL params. And replace state with params. "One needs to understand what the source of the params in a reflex is. And there are two sources: the closest form the url of the cu... — committed to jibees/openfoodnetwork by jibees a year ago
- Manage form params vs. URL params. And replace state with params. "One needs to understand what the source of the params in a reflex is. And there are two sources: the closest form the url of the cu... — committed to jibees/openfoodnetwork by jibees a year ago
- Manage form params vs. URL params. And replace state with params. "One needs to understand what the source of the params in a reflex is. And there are two sources: the closest form the url of the cu... — committed to jibees/openfoodnetwork by jibees a year ago
@helphop I think this is working/broken as intended.
TLDR:
The
paramsobject in a reflex is extremely similar to the params object in a controller action from a form submit. But the tricky difference is that in a reflex the path and query params come from the originating url, and in a controller action we get the path and query params from the url the form was submitted to.So in you reflex you find the right paragraph not by using the params but by passing around the id via the dataset.
You already prepared that in your code (
signed_id)<%= form_with(model: [notebook, page, paragraph], data: {controller: "paragraph", action: "submit->paragraph#update", signed_id: paragraph.to_sgid.to_s}) do |form| %>Access it in the reflex with:
element.dataset[:signed_id]Why are you seeing what you are seeing in the params
One needs to understand what the source of the params in a reflex is. And there are two sources:
my_params is the complete serialization of the closest form
In your example my_params is the serialization of the form. The form only contains one field, only (the content field), along with the method and the authenticity_token.
You say you are missing all the ids. Now usually you get the ids from the path that the form submit to, this information you pass into the form by using `form_with(model: [notebook, page, paragraph]. But that path has no bearing on the params in the reflex. It is not used at all by the reflex
Why does the reflex have some of the ids then?
<ActionController::Parameters {"controller"=>"pages", "action"=>"show", "notebook_id"=>"3", "id"=>"1", "_method"=>"patch", "authenticity_token"=>"1JeJgqusIAEVnldgaLdW/dsdXu0keu+I8tED/thuSjNmweEYAI7N1KowDlB1wFG+QI+yJM/toZDHNQL5OK/eGg==", "paragraph"=>{"content"=>"<div>A new message again</div>"}} permitted: false>As you note in the reflex you have some more parameter, namely
notebook_idandid. This is the confusing part. These parameters come from the page that rendered the form, it does not come from the action-path of the form.This is just how stimulus reflex works, when you trigger a reflex, the url of the current page gets sent along. In the reflex SR infers the path and query params from the url.
Could we do better?
Basically the situation is that the params in a reflex are not the same ones you would receive, if you would just submit the form. I think it might be worth to discuss if some renaming might be in order here. Such as deprecating params and instead making the form data and the path params available with separate methods. I think this would be premature. But I would not be surprised if more people run into this confusion.
Thanks very much for those details and especially for the link to documentation for file uploads. I made the error of searching the documents for ActiveStorage instead of file uploads which is why I did not find it. My mistake. I will try out the form-serialize suggestion and see what the data structure returns.