Rails submitting a form through ajax - javascript

There is some problem with using ajax in rails. I want to create new post in my wellcome/inex page. But when I push the botton it show
No route matches [POST] "/welcome/index"
wellcome/index.html
<b>Projects</b>
<ul id="projects">
<% #projects.each do |project| %>
<h2><%= project.title %></h2>
<% end %>
</ul>
<br>
<%= form_for :project do |f| %>
<%= f.text_field :title, :placeholder => ' Enter new project here..' %>
<but><%= f.submit 'Add Project'%></but>
<% end %>
welcome_controller.rb
class WelcomeController < ApplicationController
def index
#projects = Project.all
#project = Project.new
end
def create
#project = Project.new(project_params)
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'project was successfully created.' }
format.js
format.json { render json: #project, status: :created, location: #project }
else
format.html { render action: "new" }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
private
def project_params
params.require(:project).permit(:title)
end
end
create.js.erb
$('#projects').html("<%= escape_javascript (render 'projects') %>");
routes project
Prefix Verb URI Pattern Controller#Action
project_index GET /project(.:format) project#index
POST /project(.:format) project#create
new_project GET /project/new(.:format) project#new
edit_project GET /project/:id/edit(.:format) project#edit
project GET /project/:id(.:format) project#show
PATCH /project/:id(.:format) project#update
PUT /project/:id(.:format) project#update
DELETE /project/:id(.:format) project#destroy
Spent a lot of time updating this with AJAX but failed everytime. Any there to help? Thanks in advance

You have to add remote: true into form.
<%= form_for #project, url: project_index_path, remote: true do |f| %>
<%= f.text_field :title, :placeholder => ' Enter new project here..' %>
<but><%= f.submit 'Add Project'%></but>
<% end %>

If you want integrate Ajax and Ruby you need to add the line below inside your link action
remote:true
also you can check this about Ajax and Ruby
How Ajax works with ruby on rails

Related

How do I render a partial containing a form_for for an edit function?

I have a div in a parent view that renders a show partial. In this show partial, I have a button that should change the parent's partial to render the 'form' partial to edit the object, but I keep getting jammed up because it seems that the 'form' partial's form_for is missing the #project object.
How can I pass this object to the 'form' partial? Am I missing something else?
I am rather new to using AJAX. Happy to provide more information if you need.
The error I am getting in the server terminal is
ActionView::Template::Error (First argument in form cannot contain nil or be empty):
routes.rb
get 'switchProjectView', to: 'projects#switch_main_view'
resources :projects
projects_controller.rb
class ProjectsController < ApplicationController
def new
#project = Project.new
end
def create
#project = Project.new(project_params)
if #project.save
flash[:success] = "New Project Added"
redirect_to #project
else
flash[:danger] = "Project Not Added. Please Try Again"
end
end
def show
#project = Project.find(params[:id])
end
def index
#projects = Project.all
end
def update
#project = Project.find(params[:id])
if #project.update_attributes(project_params)
redirect_to #project
else
render 'edit'
end
end
def edit
end
def switch_main_view
respond_to do |format|
format.html
format.js
end
end
def project_params
params.require(:project).permit(:main_contact_name, :id, :main_contact_phone, :main_contact_email, :project_name)
end
end
show.html.erb
<div class="body">
<div class="center jumbotron heading-jumbo">
<h1><%= #project.project_name %></h1>
</div>
<div class="body-jumbo jumbotron" id='project-details'>
<%= render "projects/show" %>
</div>
</div>
switch_main_view.js.erb
$('#project-details').html("<%= j render :partial => 'projects/form' %>");
_form.html.erb
<%= form_for(#project) do |f| %>
<%= f.label :project_name %>
<%= f.text_field :project_name, class: 'form-control'%>
<%= f.label :main_contact_name %>
<%= f.text_field :main_contact_name, class: 'form-control'%>
<%= f.label :main_contact_phone %>
<%= f.text_field :main_contact_phone, class: 'form-control'%>
<%= f.label :main_contact_email %>
<%= f.text_field :main_contact_email, class: 'form-control'%>
<%= f.submit 'Save Project', class: 'btn btn-primary'%>
<% end %>
_show.html.erb
<div class='text-center center'>
<h3><strong>Project Information</strong></h2>
</div>
<h2>Start Date: Launch Date:</h1>
<span class='pull-right jumbo-btn-span'><%= link_to "Edit Project Information", switchProjectView_path(#project), remote: true, class:'btn btn-primary jumbo-btn' %></span>
<h2>Primary Conact's Name: <%= #project.main_contact_name %></h2>
<h2>Primary Conact's Phone: <%= #project.main_contact_phone %></h2>
<h2>Primary Conact's Email: <%= #project.main_contact_email %></h2>
Well, with each request, you need to re-initialize the variable in your controller method. In your case, you need to do the following:
def switch_main_view
#project = Project.new
respond_to do |format|
format.html
format.js
end
end
Doing so will enable you to get rid of the error: First argument in form cannot contain nil or be empty, but that won't enable you to do what you are actually trying to do.
When you use link_to, by default it goes for an HTML request as you can see in Rails logs, something like following will appear:
Processing by ProjectsController#switch_main_view as HTML
In order to get the JS response, you need to tell link_to that you are up for a JS response, not for an HTML response, and you can do by passing format: :js in link_to like:
<%= link_to "Edit Project Information", switchProjectView_path(#project, format: :js), remote: true, class:'btn btn-primary jumbo-btn' %></span>
remote: true will be there to make sure that you aren't reloading the contents of the page, rather you are trying to fetch something from the server in the background.
Your request "switchProjectView_path(#project)" only send object id to server, so you need to load the object to present it in view for response.
1. First the route should look like this so that, the url could contain object id:
get 'switchProjectView/:id', to: 'projects#switch_main_view', as:"switchProjectView"
2. You have to load the object in controller:
def switch_main_view
#project = Project.find(params[:id])
respond_to do |format|
format.html
format.js
end
end
Try passing the variable when calling the partial:
$('#project-details').html("<%= j render :partial => 'projects/form', locals: {project: #project} %>");

Act as Votable for nested Resources

I'm a newbie on rails and have created a listings and reviews app, (yelp type) and I want users to vote on the reviews, I have installed act as votable and configured as below but I seem to be missing something... any ideas why am getting that error?
Below is my routes.rb
Rails.application.routes.draw do
devise_for :users
resources :listings do
resources :reviews, except: [:show, :index, :upvote, :downvote] do
resources :user do
put "upvote", to: "reviews#upvote"
put "downvote", to: "reviews#downvote"
end
end
end
get 'pages/about'
get 'pages/how'
get 'pages/faqs'
get 'pages/contact'
get 'pages/privacy'
get 'pages/tos'
get 'pages/guidelines'
root 'listings#index'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
Here is my show.html.erb, I will turn it into a partial when it works
<div class="row">
<div class="col-md-3">
<%= image_tag #listing.image_url (:medium) %>
<h3><%= #listing.name %></h3>
<div class="star-rating" data-score= <%= #avg_rating %> ></div>
<p class="small"><%= "#{#reviews.length} reviews" %></p>
<address>
<strong>Address:</strong>
<%= #listing.address %><br>
<strong>Phone:</strong>
<%= #listing.phone %><br>
<strong>Email:</strong>
<%= mail_to #listing.email %> <br>
<strong>Website:</strong>
<%= link_to #listing.website, #listing %>
</address>
<hr>
<p>
<strong>About:</strong>
<p><%= h(#listing.description).gsub(/\n/, '<br/>').html_safe %></p>
</p>
</div>
<div class="col-md-9">
<%= link_to "Haiya, Post a Review ", new_listing_review_path(#listing), class: "btn btn-info" %> <br><br>
<table class="table">
<thead>
<tr>
<th class="col-md-3"></th>
<th class="col-md-9"></th>
</tr>
</thead>
<tbody>
<% if #reviews.blank? %>
<tr>
<p>No reviews yet. Be the first to write one!</p>
<% else %>
<% #reviews.each do |review| %>
<td>
<h4> <%= "#{review.user.first_name.capitalize} #{review.user.last_name.capitalize[0]}." %></h4>
<p class = "small"><%= time_ago_in_words(review.created_at) %> ago </p>
</td>
<td>
<div class="star-rating" data-score= <%= review.rating %> ></div>
<p><%= h(review.comment).gsub(/\n/, '<br/>').html_safe %></p>
<%= link_to 'UP', listing_review_user_upvote_path(#listing, review), method: :put %>
<!-- Check if user is signed in, to enable edit and update -->
<% if user_signed_in? %>
<% if (review.user == current_user) || (current_user.admin?) %>
<%= link_to "Edit", edit_listing_review_path(#listing, review), class: "text-left" %>
<%= link_to "Delete", listing_review_path(#listing, review), method: :delete, class: "text-left" %>
<% end %>
<% end %>
</td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
</div>
</div>
<% if user_signed_in? && current_user.admin? %>
<%= link_to 'Edit', edit_listing_path(#listing), class: "btn btn-link" %> |
<% end %>
<%= link_to 'Back', listings_path, class: "btn btn-link" %>
<!--The script for star ratings. -->
<script>
$('.star-rating').raty({
path: 'https://s3.eu-west-2.amazonaws.com/bebuwaphotos/uploads/stars',
readOnly: true,
score: function() {
return $(this).attr('data-score');
}
});
</script>
Here is my reviews controller
class ReviewsController < ApplicationController
before_action :authenticate_user!
before_action :set_listing
before_action :set_review, only: [:show, :edit, :update, :destroy, :upvote, :downvote]
before_action :check_user, only: [:edit, :update, :destroy]
# GET /reviews/new
def new
#review = Review.new
end
# GET /reviews/1/edit
def edit
end
#Added the def upvote and downvote
def upvote
#review = Review.find(params[:id])
#review.upvote_from current_user
redirect_to :back
end
def downvote
#review = Review.find(params[:id])
#review.downvote_from current_user
redirect_to :back
end
# POST /reviews
# POST /reviews.json
def create
#review = Review.new(review_params)
#review.user_id = current_user.id
#review.listing_id = #listing.id
respond_to do |format|
if #review.save
format.html { redirect_to #listing, notice: 'Your review was successfully posted.' }
format.json { render :show, status: :created, location: #review }
else
format.html { render :new }
format.json { render json: #review.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /reviews/1
# PATCH/PUT /reviews/1.json
def update
respond_to do |format|
if #review.update(review_params)
format.html { redirect_to root_url, notice: 'Your Review was successfully updated.' }
format.json { render :show, status: :ok, location: #review }
else
format.html { render :edit }
format.json { render json: #review.errors, status: :unprocessable_entity }
end
end
end
# DELETE /reviews/1
# DELETE /reviews/1.json
def destroy
#review.destroy
respond_to do |format|
format.html { redirect_to listing_path(#listing), notice: 'Your Review was successfully destroyed :(.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_review
#review = Review.find(params[:id])
end
#Check user
def check_user
unless (#review.user == current_user) || (current_user.admin?)
redirect_to root_url, alert: "Sorry, this review belongs to someone else, you can only edit reviews you have posted."
end
end
#set listing
def set_listing
#listing = Listing.find(params[:listing_id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def review_params
params.require(:review).permit(:rating, :comment)
end
end
And my model :
class Review < ApplicationRecord
belongs_to :user
belongs_to :listing
validates :rating, :comment, presence: true
validates :rating, numericality: {
only_integer: true,
greater_than_or_equal_to: 1,
less_than_or_equal_to: 5,
message: "You have to select at least 1 star if not more stars.."
}
acts_as_votable
end
Finally here is my error
Please explain like you would to a newbie.. or a 5 year old .
Thank you Eshan, followed your answer and got the following error.
You are doing well as a newbie.
The first thing you do when you get an error related to routes, you run rails routes in commandline, so you can see your routes in front of you :
Problem:
You are having a problem with listing_review_user_upvote path, which leads to /listings/:listing_id/reviews/:review_id/user/:user_id/upvote(.:format) URI pattern as you see above.
This route needs three params: listing_id, review_id, and user_id, but you call it this way in your view:
listing_review_user_upvote_path(#listing, review).
You don't send user_id at all, meanwhile you don't even need it since you use current_user in your controller's action.
Solution:
I suggest you to update your routes to be:
resources :listings do
resources :reviews, except: [:show, :index] do
put "upvote", to: "reviews#upvote"
put "downvote", to: "reviews#downvote"
resources :user
end
end
And to use it in your view this way, with ids instead of objects :
listing_review_user_upvote_path(#listing.id, review.id)
These two steps will resolve your problem hopefully.
Finally, except option is used with resources to avoid generating some of its CRUD routes like: update, index, edit, you don't need to use it with your custom routes like: upvote and downvote.
Update:
You should use the correct param in your controller, review_id rather than id, change review = Review.find(params[:id]) to review = Review.find(params[:review_id])
I suggest you to use find_by(id: params[:review_id rather than find(params[:review_id because find method raises an exception if it couldn't find the object in db.

Render Form partial from Ajax in rails

I have a form as image below:
Then I want to render this partial from ajax call: from .js.erb. But I don't know how to pass object to f from partial. See the image below:
how can I pass object to f:
Adding :remote => true it makes the button asynchronously(Ajax call).
index.html.erb
<%= link_to "Add Journal", new_journal_path, remote: true, class: 'new_journal_button'%>
new.js.erb
$('.new_journal_button').after("<%= j render('form') %>");
$('.new_journal_button').hide();
If you want to submit a form asynchronously(Ajax call)
_form.html.erb
<%= form_for(#journal, remote: true) do |f| %>
<div class="field">
<%= f.label "journal" %><br>
<%= f.text_field :title, placeholder: 'Journal', autofocus: true, 'data-behavior' => 'submit_on_enter' %>
</div>
<% end %>
Journals_controller.rb
def index
#journal = Journal.new
#journals = Journal.all
end
def create
#journal = Journal.new(journal_params)
respond_to do |format|
if #journal.save
format.html { redirect_to #journal, notice: 'Journal was successfully created.' }
format.js
else
format.html { render :new }
end
end
end
I was pondering on how to replace part of the form (if that is what you are doing) for a while. The following is not perfect but "works for me".
We instantiate the form builder f in the controller (instead of a view) and render only your partial into a string. Then escape and render js into Ajax reply as usually.
def show
respond_to do |format|
format.js do
helpers.form_for(#journal) do |f|
out = render_to_string partial: 'journal_entry_transaction_fields', locals: {f: f}
render js: %($('.journal-entry').html("#{helpers.j out}");)
end
end
end
end
Perhaps the corresponding coffeescript part that is Turbolinks friendly and uses jQuery for sending Ajax is akin to
$(document).on 'change', '#something', (e) ->
$.get
url: "/path/to/your/controller/#{e.target.value}.js"
you could send #journal object then use fields_for #journal into partial
.js.erb
$('.journal-entry').html("<%= j render partial: 'journal_entry_transaction_fields', object: #journal %>");
Partial .html.erb
<%= fields_for #journal do |f| %>
... code .....
<% end %>

Ruby on rails AJAX submit form error

So I'm trying to update my comments section with AJAX without the full page refresh for a college project. However I can't seem to get this working.
In the console it gives me s
Failed to load resource: the server responded with a status of 500 (Internal Server Error)
My show.html.erb file:
<h1>Title: <%= #post.title %></h1>
<h2>Body: <%= #post.body %></h2>
<hr />
<h1>Your comments</h1>
<%= link_to "View comments", "#", id: "comments-link" %>
<ol id="comments">
<%= render 'comments' %>
<hr />
<h1>Create comment</h1>
<%= form_for(#comment, :html => {class: "form", role: "forms"}, :url => post_comments_path(#post), remote: true) do |comm| %>
<div class="container">
<div class="input-group input-group-md">
<%= comm.label :body %>
<%= comm.text_area :body, class: "form-control", placeholder: "Enter a comment" %>
</div>
<%= comm.submit "custom", id: "button" %>
</div>
<% end %>
</ol>
My comments.coffee:
$(document).on "page:change", ->
$('#comments-link').click ->
$('#comments').fadeToggle()
$('#comments_body').focus()
My create.js.erb:
$('#comments').append("<%= j render #comment %>");
and my Comments controller:
class CommentsController < ApplicationController
def index
end
def new
end
def new
#comment = Comment.new
end
def create
#comment = Comment.new(comment_params)
#comment.post_id = params[:post_id]
if #comment.save
flash[:success] = "Successfully created comment"
respond_to do |format|
format.html { redirect_to post_path(#comment.post_id) }
format.js
end
else
flash[:danger] = "Failed to create comment"
redirect_to root_path
end
end
private
def comment_params
params.require(:comment).permit(:body)
end
end
I may have missed some files so just let me know, it is basic as it's just a post and comment system - no styling needed for the project, so yeah. I have been trying this for the last 4 hours and other places just don't work. I've looked on here, Youtube - everywhere however no one else's code works for me so I have come here! Thank's for you're help.
EDIT:
I noticed it said to create a view in the error response, however I made that view and rendered the comment's body onto the create.html.erb however I just need to display the form now.
I notice you are posting to the url post_comments_path(#post). For nested routes, it might be cleaner to do the following:
1) Post to the nested route directly:
<%= form_for([#post, #comment], html: {class: "form", role: "forms"}, remote: true) do |comm| %>
2) Make sure your routes are nested properly, in routes.rb:
resources :posts do
resources :comments
end
3) Refactor your CommentsController, to build through the #post instance:
class CommentsController < ApplicationController
before_action :get_post
def index
#comments = #post.comments
end
def new
#comment = #post.comments.build
end
def create
#comment = #post.comments.build(comment_params)
if #comment.save
flash[:success] = "Successfully created comment"
respond_to do |format|
format.html { redirect_to post_path(#post) }
format.js
end
else
respond_to do |format|
format.html { render :new }
format.js { render :error }
end
end
end
private
def comment_params
params.require(:comment).permit(:body)
end
def get_post
#post = Post.find(params[:post_id])
end
end
4) Render the validation errors in app/views/comments/error.js.erb. I'll let you decide how best to do that, but here's a quick dump to the console:
<% #comment.errors.full_messages.each do |message| %>
console.log("<%= message %>");
<% end %>
This file is not to be confused with your app/views/comments/create.js.erb which is handling successful save of the comment. That should look something like this:
$('#comments').append("<%= j render #comment %>");
$(".form")[0].reset();
5) Tweak your view a little bit. I notice you need to output the comments differently:
<ol id="comments">
<%= render #post.comments %>
</ol>
which should correspond to a partial in app/views/comments/_comment.html.erb so make sure this is there.

Ajax/Rails Nested Comments

I have an application where book_comments belongs_to books.
My routes file :
resources :books do
resources :book_comments
end
I have a book_comment form on show.html.erb for books.
def show
#book = Book.find(params[:id])
#new_book_comment = #book.book_comments.build
end
I'm using ajax to post the book_comments to the book show page via a partial: _book_comment_form.html.erb :
<%= form_for([#book, #new_book_comment], remote: :true) do |f| %>
<div class="field" style="margin-top:60px;">
<%= f.text_area :comment, rows: 3 %>
<%= f.submit "Add Comment" %>
</div>
<% end %>
When #new_book_comment is called it refers to a book_comments_controller create action:
def create
#book = Book.find(params[:book_id])
#book_comment = current_user.book_comments.build(params[:book_comment]) do |f|
f.book_id = #book.id
end
if #book_comment.save
respond_to do |format|
format.html { redirect_to #book }
format.js { render :layout => false }
end
end
end
The book_comment is saved but my problem comes in when trying to render via ajax in the _book_comments partial:
<div id="comments">
<% #book_comments.each do |book_comment| %>
<%= render 'comment', :book_comment => comment %>
</div>
<% end %>
</div>
via: create.js.erb in the book_comments folder
$("div#comments").append("<%= escape_javascript(render('books/comment'))%>")
To sum, I'm having trouble making the create.js.erb in book_comments render to the book show page. Why wouldn't create.js.erb in the book_comments folder know to look at the objects in the and infer the new book comment should go inside the div?
My error message:
undefined method `comment' for nil:NilClass
don't understnd why. Seeing that I'm passing the instance variable to the comment partial.
Your partial's doing it wrong. You're passing a not referenced value (comment) to a not used variable (book_comment).
Instead of:
<% #book_comments.each do |book_comment| %>
<%= render 'comment', :book_comment => comment %> ....
you should do:
<% #book_comments.each do |book_comment| %>
<%= render 'comment', :comment => book_comment %>

Categories

Resources