I have a form that I've extracted to a partial.
<%= form_with url: filtered_tags_path, class: 'filter-box' do %>
<%= text_field_tag(:filter_by, '', id: 'filter-text-field', autocomplete: 'off') %>
<% end %>
<script>watch_form()</script>
There's a script in application.js
that watches for changes in the text field and fires a request after each keystroke:
function watch_form() {
document.getElementById('filter-text-field').addEventListener('keyup', function(){
Rails.fire(this.form, 'submit')
})
}
The correspond action looks like this:
def create
@labels = Label.by_search_term(params[:filter_by])
respond_to do |format|
format.js
end
end
With this template:
document.querySelector('.filter-results')
.innerHTML = "<%= escape_javascript(render partial: 'filtered_results') %>"
Which renders:
<% if @labels.present? %>
<% @labels.each do |label| %>
<%= label_link(name: label.name, is_remote: true) %>
<% end %>
<% else %>
<div class="badge no-tags-badge">No tags found</div>
<% end %>
In short, a series of buttons (wrapped in forms) are rendered. For example:
<form class="button_to" method="post" action="/issues?labels%5B%5D=ready" data-remote="true">
<input class="label-badge" type="submit" value="+ ready">
<input type="hidden" name="authenticity_token" value="yvWh51r/xHqLPb5V/qdfeiar/j9fqd/8FoZM0jnRFUFEHkdU5WlgVjyaIQi9viiiSfckjJypOhMnKAh29wjY3w==">
</form>
When I click a button, it sends its request and I get a successful response.
I want to have two separate forms. My first step was to swap the id
of the form's textfield for a variable.
<%= form_with url: filtered_tags_path, class: 'filter-box' do %>
<%= text_field_tag(:filter_by, '', id: filter_field_id, autocomplete: 'off') %>
<% end %>
<script>watch_form()</script>
Passing it to the partial like so:
<div class="filter-form">
<%= render partial: 'filter_form', locals: { filter_field_id: 'filter-text-field' } %>
</div>
The form behaves exactly as before. The buttons are rendered for each keystroke. Apart from the authenticity token, the example button looks exactly the same.
<form class="button_to" method="post" action="/issues?labels%5B%5D=ready" data-remote="true">
<input class="label-badge" type="submit" value="+ ready">
<input type="hidden" name="authenticity_token" value="0xjeK53JZSF0nSA2kUT2MREgaCeQ8RlmK2wqcd2sCIZd8ziYIl/BDcM6v2vSXYHpfnyylFPx/Ikawm7VE3XFGA==">
</form>
There's nothing to suggest that there's anything different but this time, on clicking a button, I get a 500
response.
In the console, I see:
rails-ujs.self-d109d8c5c0194c8ad60b8838b2661c5596b5c955987f7cd4045eb2fb90ca5343.js?body=1:212
POST http://localhost:3000/issues?labels%5B%5D=documentation&labels%5B%5D=ready 500 (Internal Server Error)
Rails.ajax @ rails-ujs.self-d109d…a5343.js?body=1:212
Rails.handleRemote @ rails-ujs.self-d109d…a5343.js?body=1:568
(anonymous) @ rails-ujs.self-d109d…a5343.js?body=1:169
Now, I'm completely lost. I've tried binding my javascript to a turbolinks:load
event listener but that hasn't made a difference.
The only thing I can think of is that it's something to do with the authenticity token.
What is the difference between passing a string literal as the id and using a variable? Why does my change break things? What do I need to do to make it work?
UPDATE
I've discovered that clicking any similar buttons on the page, in addition to the ones rendered when the input has changed, results in a 500
response.
UPDATE 2
The error in rails console:
ActionView::Template::Error (undefined local variable or method `filter_field_id' for #<#<Class:0x00007fa891909b60>:0x00007fa88bf7ef78>
Did you mean? file_field):
1: <%= form_with url: filtered_tags_path, class: 'filter-box' do %>
2: <%= text_field_tag(:filter_by, '', id: filter_field_id, autocomplete: 'off') %>
3: <% end %>
4: <script>watch_form()</script>
app/views/issues/_filter_form.html.erb:2:in `block in _app_views_issues__filter_form_html_erb___4066184210884757332_70181011505480'
app/views/issues/_filter_form.html.erb:1:in `_app_views_issues__filter_form_html_erb___4066184210884757332_70181011505480'
app/views/issues/create.js.erb:14:in `_app_views_issues_create_js_erb__4388147431667736133_70180939735900'
From that I can see that I'm not passing in the local variable when I render the partial in create.js.erb
. So I've amended that to:
document.querySelector('.filter-form')
.innerHTML = "<%= escape_javascript(render 'filter_form', locals: { :filter_field_id=> 'filter-text-field' }) %>"
But I still get the same error. undefined local variable or method 'filter_field_id'
Passing local variables is different when you don't use partial
. You don't need locals
in your create.js.erb file
.
# Instead of <%= render partial: "account", locals: { account: @buyer } %>
<%= render "account", account: @buyer %>
So your code should be either:
document.querySelector('.filter-form')
.innerHTML = "<%= escape_javascript(render 'filter_form', filter_field_id: 'filter-text-field' ) %>"
or
document.querySelector('.filter-form')
.innerHTML = "<%= escape_javascript(render partial: 'filter_form', locals: { filter_field_id: 'filter-text-field' }) %>"