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.
Related
I know there are a lot of similar questions on SO but I haven't found one that fits what I'm trying to do...(because I'm not 100% sure it is even possible to do what I am trying to do!!)
At the moment I have a page that creates several form_tags based on how many 'questions' there are in the 'test', like this:
<div class="jumbotron">
<% #test.questions.each do |question| %>
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">Question <%= question.question %></h3>
</div>
<div class="panel-body">
<p>
<%= image_tag question.image.url(:medium) %>
</p>
<%= form_tag edit_test_testsession_path(#test, question.testsessions), id: 'submit_answer' do %>
<%= radio_button_tag :answer, "A" %> A
<%= radio_button_tag :answer, "B" %> B
<%= radio_button_tag :answer, "C" %> C
<%= radio_button_tag :answer, "D" %> D
<%= submit_tag 'Submit', class: "btn btn-success", id: 'single_submit' %>
<% end %>
</div>
</div>
<% end %>
<br/>
<%= link_to "See Test Results!", results_path(#test), class: "btn btn-lg btn-info btn-block", id: "submit_all" %>
</div>
At the moment I have disabled the 'submit' buttons because I don't want the forms to actually be submitted until all of them are completed so I have this Javascript:
$(document).ready(function() {
$('.btn.btn-success').click(function(e) {
e.preventDefault();
this.value="Resubmit";
});
$('#submit_all').click(function(){
$('#submit_answer').each(function(){
$(this).submit();
});
});
});
It does attempt to submit the answers but still tries to take the user to an edit page, but of several [:id], which obviously doesn't work...
In my TestsessionsController I have this:
class TestsessionsController < ApplicationController
def new
#test = Test.find(params[:test_id])
#testsession = Testsession.new
redirect_to action: :create
end
def create
#test = Test.find(params[:test_id])
#test.questions.each do |question|
#test.testsessions.create user: current_user, question: question
end
redirect_to action: :index
end
def index
#test = Test.find(params[:test_id])
#questions = #test.questions
# #testsession = Testsession.find(params[:id])
end
def show
#testsession = Testsession.find(params[:id])
#test = Test.find(params[:test_id])
#questions = #test.questions
end
def update
#testsession = Testsession.find(params[:id])
# #question = Question.find(params[:question_id])
#testsession.update(params.require(:testsession).permit(:answer))
redirect_to action: :results
end
def edit
#testsession = Testsession.find(params[:id])
#question = Question.find(params[:question_id])
#testsession.update(params.require(:testsession).permit(:answer))
redirect_to action: :results
end
def results
#test = Test.find(params[:id])
#testsession = Testsession.find(params[:id])
end
end
Is there any way to force the last link to take it directly to the results page, but still submit the information from the form_tags?
At the moment it is just giving me this error:
No route matches [POST] "/tests/1/testsessions/%23%3CTestsession::ActiveRecord_Associations_CollectionProxy:0x007fe197a72e40%3E/edit"
and only when I hit the back button it takes me to the results page...
EDIT
config/routes file:
Rails.application.routes.draw do
devise_for :users
root 'welcome#index'
get 'tests/:id/testsessions/new' => 'testsessions#create'
get 'tests/:id/testsessions/results' => 'testsessions#results', as: :results
resources :tests do
resources :questions
resources :testsessions
end
end
Upd I think what you was meaning is:
<%= form_tag edit_test_testsession_path(#test, #testsession), id: 'submit_answer' do %>
The problem seems to be here:
<%= form_tag edit_test_testsession_path(#test, question.testsessions), id: 'submit_answer' do %>
You are passing a collection of all associated testsessions instead of single object, and I doubt it's what you intended to do.
Please show a part of your config/routes.rb related to the controller
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');
I have problems getting a form to work when I use JavaScript with it.
Without the remote option set to true all works fine but as soon as I
set it, change the controller and create the needed JS files it produces an
POST .. 500 Internal Server Error
when clicking on the submit button.
Furthermore the reply-preview shows me a Rails Error Page saying
NoMethodError in Vocabs#create and undefined method > for nil:NilClass
in a related partial (which works fine before using JS)
The form (vocabs/_new_form.html.erb) looks like this:
<%= form_for #vocab, remote: true do |f| %>
<%= render 'fields', f: f %>
<%= f.submit "Add", class: "btn btn-large btn-primary" %>
<% end %>
The vocab_controller's create action like this:
def create
#user = current_user
#vocab = current_user.vocabs.build(vocab_params)
#vocabs = #user.vocabs.paginate(page: params[:page])
respond_to do |format|
format.html {
if #vocab.save
flash.now[:success] = "Vocab created!"
redirect_to root_url
else
flash.now[:error] = "Vocab not saved!"
render 'new'
end
}
format.js
end
end
The create.js.erb like this:
$('#new_vocab').remove();
Of course I want to do more than just to remove the form but as I didn't even manage to do that I wanted to keep it simple.
_fields.html.erb:
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.label :nl_word, #user.native_language %>
<%= f.text_field :nl_word %>
<%= f.label :fl_word, #user.foreign_language %>
<%= f.text_field :fl_word %>
<%= f.label :tag, "Your Tag" %>
<%= f.text_field :tag%>
<%= f.label :importance, "Importance" %>
<%= f.select(:importance, options_for_select(
StaticData::IMPORTANCE_SEED, :selected => f.object.importance)) %>
</div>
I hope I could explain myself good enough to get some help here. I tried for hours to find the solution to the problem myself and was looking for similar questions here without finding any and without getting anywhere.
With the help of a friend I found out that the main problem was that I didn't provide any validation for the vocab object i wanted to create in the form (_new_form.html.erb), when processing it with JS.
So I changed the vocab_controller.rb to
def create
#user = current_user
#vocab = current_user.vocabs.build(vocab_params)
#vocabs = #user.vocabs.paginate(page: params[:page])
respond_to do |format|
if #vocab.save
flash.now[:success] = "Vocab created!"
format.html do
redirect_to root_url
end
format.js
else
flash.now[:error] = "Vocab not saved!"
format.html do
render 'new'
end
format.js
end
end
end
That does the trick, and my form works with JS now.
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.
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?