Rails - pass a parameter to controller from form select dropdown - javascript

I have 2 models: Team and Quest. When creating a new team, I have a drop-down of all the quests. When a quest is selected, I want to display information about the quest.
My understanding is that everything in the form is on the client side and AJAX is required to pass the selected quest to the server side. My code is based on this Stack Overflow answer.
Here is how I constructed my form:
app/views/teams_form.html.erb
<%= form_for(#team) do |f| %>
<fieldset>
<ol>
<li>
<%= f.label :name %>
<%= f.text_field :name %>
</li>
<li>
<%= f.label :quest_id %>
<%= f.select :quest_id,
options_from_collection_for_select(#quests, :id, :name),
{}, {remote: true, url: '/teams/new', method: 'get'} %>
</li>
<% if #x != nil && #x.id != nil %>
<li><%= #x.id %></li>
<% end %>
</ol>
<p>
<%= f.submit %>
</p>
</fieldset>
<% end %>
app/controllers/team_controller.rb
def new
#team = Team.new
#quests = Quest.all
respond_to do |format|
if params[:quest_id] != nil
#x = Quest.find(params[:quest_id])
end
format.html #new.html.erb
format.json
format.js
end
end
My goal was to pass the :quest_id parameter from the form to the #x variable and use that in the form.
This has produced nothing. I'm not getting the parameter in the controller and I'm not sure what I'm missing.

As per the description shared it seems like the you are unable to get the value of the selected item from the dropdown.
Below mentioned code is used for selecting value from dropdown also you can inspect the same using developer tools of the browser.
quest = $("#quest_id").val();
Note: Assuming the selector id is quest_id, change it according to your form.
Now, you can call the ajax using the below mentioned code.
$.ajax({
type: "GET",
url: "/teams/new",
data:{ quest_id: quest },
dataType: "json",
success:function(data){
# logic for using ajax result
}
Hope it helps!!

Finally got this working, wanted to post if anyone sees this and is having the same problem:
I went with a separate AJAX request since that was being suggested
app/views/teams_form.html.erb
<script>
$(document).ready(function() {
$('#team_quest_id').change(function() {
$.ajax({
url: "/teams/new",
data: {quest_id: $("#team_quest_id option:selected").val()},
dataType: "script",
method: "get",
success: function(r){}
});
});
});
</script>
I moved the location of the parameter assignment
app/controllers/team_controller.rb
def new
#team = Team.new
#quests = Quest.all
if params[:quest_id] != nil
#x = Quest.find(params[:quest_id])
end
respond_to do |format|
format.html #new.html.erb
format.json
format.js
end
end
And most importantly - I created a js file to render my form
app/views/new.js.erb
$('#new_team').html("<%= j (render 'form') %>");
This video was extremely helpful

The code in your question is almost correct, you forgot to nest the attributes in data.
<% # app/views/teams_form.html.erb %>
<%= f.select :quest_id,
options_from_collection_for_select(#quests, :id, :name),
{}, {remote: true, url: '/teams/new', method: 'get'} %>
<% # should be: %>
<%= f.select :quest_id,
options_from_collection_for_select(#quests, :id, :name),
{}, {data: {remote: true, url: '/teams/new', method: 'get'}} %>
<% # or even better, use the path helper instead of the hard coded path %>
<%= f.select :quest_id,
options_from_collection_for_select(#quests, :id, :name),
{}, {data: {remote: true, url: new_team_path, method: :get}} %>
Having set the attributes correctly, we still need to fix the form further. On page request the browser will request the form, but #x will never be set. Since ERB will not be send to the client we'll need to add a handle to find our quest container element back.
<% # app/views/teams_form.html.erb %>
<% if #x != nil && #x.id != nil %>
<li><%= #x.id %></li>
<% end %>
<% # should be something like %>
<li id="quest-info-container"></li>
Now in the controller split of the HTML request from the JS request.
# app/controllers/teams_controller.rb
def new
respond_to do |format|
format.html do
#team = Team.new
#quests = Quest.all
end
format.js do
#quest = Quest.find(params.dig(:team, :quest_id))
end
end
end
You could simplify the above by sending the select data-path to another url that handles the quest preview.
Now we need to render the preview in our container we need 2 files for this, first of how the resulting structure should look. Keep in mind that this will be rendered inside the container.
<% # app/views/teams/_quest_preview.html.erb %>
<% # Here comes what you want to display about the quest. You can give this %>
<% # file another name if you like. You have #quest to your disposal here. %>
<%= #quest.id %> <strong><%= #quest.name %></strong>
Now we only need a JavaScript file that loads the above structure into our created handle.
<% # app/views/teams/new.js.erb %>
handle = document.getElementById('quest-info-container');
handle.innerHTML = '<%= j render('quest_preview') %>';
The j is an alias for escape_javascript. If the partial is not in the same directory use <%= j render('other_dir/some_partial') %> instead.

Related

Custom country/state dropdowns with rails

I been diggins some days on gems for country and state/province selection. Some are great (like country-state-select) but not for my needs.
The almost mandatory country_select gem is good but lacks states. Nevertheless is based on this very cool gem called countries. Gem countries really puts together a lot of good info, lets say Mozambique and its subdivisions, and so on, for over 300 countries, and includes i18n.
Having countries gem in the app, all is needed is some javascript to interact with it. Calls are made with this:
country = ISO3166::Country.new('US')
Here is the form:
<%= simple_form_for(#order) do |f| %>
<div><%= f.input :country %></div>
<div><%= f.input :state %></div>
<%= f.button :submit, t('.submit') %>
<% end %>
<script type="text/javascript">
states_drop_down = $("#order_state");
$("#order_country").change(function() {
// How to make this work?
<% unless params[:country].nil? %>
states_drop_down.clearAttributes();
<% ISO3166::Country.new(params[:country]).states.each do |state| %>
states_drop_down.setAttribute(<%= state[:name] %>, <%= state[:alpha2] %>); // How to log to check if new attributes are present?
<% end %>
states_drop_down.reload(); // How to reload the state simple_form input?
<% end %>
});
The goal is the known one, to populate state selector with correct country every time the country dropdown changes. Any help? Thanks.
I found a solution, even though is not using gem countries anymore. Data is seeded to database and pulled from there. Found the data here.
Then all is needed is few steps:
// 1. new action at controller, in my case was orders. This receives the calls and renders the views.
def update_states
country = Country.find(params[:nation_id])
#states = country.states
respond_to do |format|
format.js
end
end
// 2. new get at routes to find the new action
get "orders/update_states", as: "update_states"
// 3. simple_form with collections. Note uses nation to avoid the simple_form country selector error.
<%= simple_form_for(#order) do |f| %>
<div><%= f.input :nation, collection: #countries %></div>
<div><%= f.input :state, collection: #states %></div>
<%= f.button :submit, t('.submit') %>
<% end %>
// 4. new doc at app/views/states/_state.html.erb to render inside the dropdown selector.
<option value="<%= state.id %>"><%= state.name %></option>
// 5. new lines at app/assets/javascripts/orders.js.coffee to listen to nation or country selector.
$ ->
$(document).on 'change', '#order_nation', (evt) ->
$.ajax 'update_states',
type: 'GET'
dataType: 'script'
data: {
nation_id: $("#order_nation option:selected").val()
}
error: (jqXHR, textStatus, errorThrown) ->
console.log("AJAX Error: #{textStatus}")
success: (data, textStatus, jqXHR) ->
console.log("State selection is working!")
// 6. and app/views/orders/update_cities.js.cofee, the piece that close the circle. This actually renders the info and views.
$("#order_state").empty()
.append("<%= escape_javascript(render(:partial => #states)) %>")
Must thanks Kernel Garden, I found the javascript I was looking here.

jQuery .load()-function doesn't reload element (Rails)

In my Rails Project, I am trying to alter an instance variable via JS in a controller response (create.js.erb) and then reload an HTML-Element that makes use of the instance variable. But it doesn't work. Is it possible? This is just an example.
I also want to display the newly created object. But as I don't seem to understand the mechanism of exchanging information between rails and JS, I wanted to keep it simple here.
vocabs-controller.rb
def new
#user = current_user
#vocab = #user.vocabs.build
#vocabs = #user.vocabs.all
#count = #vocabs.count
.
.
end
def create
#user = current_user
#vocab = #user.vocabs.build(vocab_params)
#count = #user.vocabs.count
.
.
respond_to do |format|
format.html do
redirect_to new_user_vocab_path(#user)
flash[:success] = "Vocab created!"
end
format.js {render :layout=>false}
end
else
respond_to do |format|
format.html {render 'vocabs/new'}
format.js
end
end
new.html.erb (from here I make the call to the controllers create action)
<div class="panel-body">
<%= form_for(#vocab,
url: user_vocabs_path(#user),
method: :post, remote: true) do |f| %>
.
.
.
<%= f.submit "Add", class: "btn btn-large btn-primary" %>
<% end %>
<h4>
<%= #count %> Vocabs in your collection
</h4>
<%= link_to "(Show)", "#", id: "show_link" %>
<%= link_to "(Hide)", "#", id: "hide_link" %>
<ul class='vocabs <%= #vocabs_class %>'>
<% #vocabs.each do |vocab| %>
<%= render vocab %>
<% end %>
</ul>
<hr>
<%= link_to "Back", home_user_path(#user) %>
</div>
.
.
create.js.erb
<% #count+=1 %>
$('h4').load();
The server recognizes that I made a JS request and also renders create.js.erb with 200 OK. But the .load() function doesn't seem to reload the h4-Element with the new data.
When I was trying to .load() the div-element that contains the formular fields, these also weren't updated. The text input was still visible.
Further question: Where can I debug the code in JS Controller responses? I can't find them neither in Chrome's dev tools nor in Rails's server output.
If I follow you correctly, try
Add an id attribute to h4:
<h4 id="count-header">...</h4>
And in the .erb.js callback:
$('#count-header').text('<%= #count += 1 %> Vocabs in your collection');

Live Search with AJAX Ruby on Rails

I'm following the railscasts rails ajax tutorial and geting into some trouble. Everything went well, except the live keyup. The live search does not work, I have to click the search button to get the result.
Here is my application.js
$("#emos_search input").keyup(function() {
$.get($("#emos_search").attr("action"), $("#emos_search").serialize(), null, "script");
return false;
});
index.html.erb
<%= form_tag emoticons_path, :method => 'get', :id => "emos_search" do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<div id="emos"><%= render 'emos' %></div>
<% end %>
emoticons_controller.rb
def index
#emoticons = Emoticon.search params[:search]
end
emoticon.rb
def self.search(search)
if search
where('name LIKE ? or emo LIKE ?', "%#{search}%", "%#{search}%")
else
scoped
end
end
I don't know what is the problem. I think I already followed the steps in tutorial. And there is nothing showed in js console.

Rails 3 javascript: How to render a partial with parameters

I'm still getting the hang of Rails. Here I'm using Rails 3 and the goal basically is to have an AJAX call triggered when I click the subscribe button the post_form partial is rendered beneath for the topic I have just subscribed to. The button then becomes an unsubscibe button and the post_form partial is removed. The toggling of the button alone works (i.e: by removing the second line in the two immediately following snippets), but the rendering of the *post_form* partial does not.
The problem is I can't seem to get the right syntax and/or passing of parameters in the two following partials. The topic object is just not passed and I get an invalid model_name for NilClass error when clicking on the subscribe or unsubscribe button. If I refresh the page manually, the partial is rendered or hidden the correct way, so it's really just the AJAX part that isn't working right.
views/subscription/create.js.erb
$("#subscription_form").html("<%= escape_javascript(render('users/unsubscribe')) %>");
$("#post_form").html("<%= escape_javascript(render('shared/post_form', :topic => #topic)) %>");
views/subscription/destroy.js.erb
$("#subscription_form").html("<%= escape_javascript(render('users/subscribe')) %>");
$("#post_form").html("<%= escape_javascript(render('shared/post_form', :topic => #topic)) %>");
views/users/_subscription_form.html.erb
<% unless current_user?(#user) %>
<div id="subscription_form">
<% if current_user.subscribed?(#topic) %>
<%= render 'users/unsubscribe', :topic => #topic %>
<% else %>
<%= render 'users/subscribe', :topic => #topic %>
<% end %>
</div>
<% end %>
controllers/subscriptions_controller.rb
class SubscriptionsController < ApplicationController
before_filter :signed_in_user
respond_to :html, :js
def create
#topic = Topic.find(params[:subscription][:topic_id])
current_user.subscribe!(#topic)
respond_with #topic
end
def destroy
#topic = Subscription.find(params[:id]).topic
current_user.unsubscribe!(#topic)
respond_with #topic
end
end
views/shared/_post_form.html.erb
<%= form_for(#post) do |f| %>
<div class="field">
<%= f.hidden_field :topic_id, :value => #topic.id %>
<%= f.text_area :content, placeholder: "Tell us about it ..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
If it is of any help, the relationships are:
post -> belongs_to -> topic and topic -> has_many -> posts
Looks like you're using the variable "#post" in the "views/_post_form.html.erb" file.
<%= form_for(#post) do |f| %>
Since you aren't setting that variable anywhere in your actions you would get a null reference error.
You would need to do something like this:
def create
#post = Post.find(the_post_id)
#topic = Topic.find(params[:subscription][:topic_id])
current_user.subscribe!(#topic)
respond_with #topic
end
Also you are passing in the "topic" variable as a local but accessing it as an instance variable. You should change the your _post_form.html.erb file to look like this:
<%= form_for(#post) do |f| %>
<div class="field">
<%= f.hidden_field :topic_id, :value => topic.id %>
<%= f.text_area :content, placeholder: "Tell us about it ..." %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
I don't have my ruby environment readily available so I can't verify that this will solve your problem but I think it should move you in the right direction.

Rails: best way to test an action called remotely?

I was trying out Rails again, this time the 3 version, but I got stuck while writing tests for an action that I only call remotely.
A concrete example:
Controller
class PeopleController < ApplicationController
def index
#person = Person.new
end
def create
#person = Person.new(params[:person])
#person.save
end
end
View (index.html.erb)
<div id="subscription">
<%= form_for(#person, :url => { :action => "create" }, :remote => true) do |f| %>
<%= f.text_field :email %>
<%= f.submit "Subscribe" %>
<% end %>
</div>
View (create.js.erb)
<% if #person.errors.full_messages.empty? %>
$("#subscription").prepend('<p class="notice confirmation">Thanks for your subscription =)</p>');
<% else %>
$("#subscription").prepend('<p class="notice error"><%= #person.errors.full_messages.last %></p>');
<% end %>
How can I test that remote form submission? I would just like to find out if the notice messages are being presented correctly. But if I try to do just
test "create adds a new person" do
assert_difference 'Person.count' do
post :create, :people => {:email => 'test#test.com'}
end
assert_response :success
end
It will say that the "create" action is missing a template.
How do you guys usually test remote calls?
Could you just use the 'xhr' function instead of the 'post' function? An example can be found at http://weblogs.java.net/blog/2008/01/04/testing-rails-applications, if you search for 'xhr'. But even then, I'm curious, even with a remote call, don't you need to return SOMETHING? Even just an OK header?

Categories