I'm new on RoR, and I try to build a classic web app with post & user.
There is a model & controller(Onlines) that allow the user to put his posts on a common wall with new informations own to this action.
I'm currently trying to modify a nested form associated with this action(Onlines), by modifying the model Onlines.
But I can't access to this action of my controller, and I don't understand why ?
My code ::
Onlines controller :
class OnlinesController < ApplicationController
before_action :set_online
def edit
end
private
def set_online
#post = Post.find(params[:post_id])
#online = Online.find_by(params[:id])
end
end
Post controller :
class PostsController < ApplicationController
before_action :set_online
def show
#online.post_id = #post.id
end
private
def set_online
#onlines = Online.find_by(id: params[:id])
end
end
Views/posts/show : `
<div class="btn-group" role="group" aria-label="...">
<%= link_to '- taked - ', edit_online_path(#online), data: { confirm: 'Confirmer la mise en ligne de #{#title}?' }, class: "btn btn-primary " %>
</div>
Views/onlines/edit :
<%= simple_form_for([#post, #onlines]) do |f| %>
<div class="row">
<div class="col-md-12">
<div id="Order">
<%= f.simple_fields_for :orders do |order| %>
<%= render 'orders_fields', f: order %>
<%end%>
<div class="Order_links">
<%= link_to_add_association 'Ajouter une part', f, :orders, class: "btn btn-default" %>
</div>
</div>
</div>
</div>
<div class="form-group text-center">
<%= f.submit "Pusher", class: 'btn btn-success' %>
</div>
<% end %>
Routes:
Rails.application.routes.draw do
get 'profiles/show'
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
devise_for :users, :controllers => { registrations: 'registrations' }
resources :posts do
resources :comments
resources :onlines
end
get ':pseudo', to: 'profiles#show', as: :profile
get ':pseudo/edit', to: 'profiles#edit', as: :edit_profile
patch ':pseudo/edit', to: 'profiles#update', as: :update_profile
get ':post_id/online/new', to: 'online#new', as: :new_online
post ':post_id/online/:id/edit', to: 'onlines#edit', as: :edit_online
root 'posts#index'
So if you can guide me to succeed this action it would be wonderful, thanks !
Firstly, always refer to your models in singular form (Online in your case) as that is what Rails expects and controller's in plural as you have stated. Be careful of your "before_action :set_online" statement as it is using the 'find' method for defining #post and will cause an exception if a param isn't passed in! Also, your route is only showing a get request to get you to your edit_online page. You'll then need a 'post' route linked to your 'update' action that will submit the data to your application after the user submits his/her form! Provide the rest of your routes and I'll take a deeper look but also clarify your question a bit. If you're just trying to go from one action in one controller to another action in a different controller you're looking to use the "redirect_to" statement.
Related
I want to add confirmation modal when bank manager has to delete bank_employee without clients bank_employee.users = nil. If bank_employee has clients I want to render different modal - destroy_confirmation_modal. How to do it in a proper way? Where should I put if condition?
code snipped of
edit.html.erb
<div class="row">
<div class="col-sm-12 col-md-12 col-xs-12 text-center bank-employee__button-wrapper bank-employees-users-registration__registrations-submit--wrapper">
<%= t('.delete') %>
<%= f.submit t('.submit'), id: "formSubmit", class: "bank-employee__button bank-employee__button-submit"%>
</div>
</div>
<% end %> // this `end` comes from `form_for`
<%= button_to "", bank_employee_path(#bank_employee), method: :delete, form: {id: "bankEmployeeDestroyForm" }, class: "bank-employee__button-destroy" %>
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12 text-center">
<%= link_to bank_employees_path do %>
<span class="bank-employee__back-button">
<%= image_tag "icon_back.svg", alt: "Back icon", class: ""%>
<%= t('.back') %>
</span>
<% end %>
</div>
</div>
</div>
<%= render "destroy_confirmation_modal" %>
I don't think I should update my controller method but maybe I'm wrong?
controller.rb
def destroy
authorize current_bank_employee
#bank_employee = find_bank_employee(params[:id])
if #bank_employee.users.any? && associated_bank_employees.present?
reassign_users_and_notify_bank_employee
elsif #bank_employee.users.any?
render :edit
else
#bank_employee.destroy
render :destroy_notice, locals: { old_bank_employee: #bank_employee, assigned: false }
end
end
EDIT
my routes.rb
resources :bank_employees, except: [:show], concerns: [:with_datatable] do
member do
get :confirm
end
end
rails routes showed me this path as confirm_bank_employee so I've changed if condition as follow
<% if #bank_employee.users.empty? %>
<%= button_to "", confirm_bank_employee_path(#bank_employee), form: {id: "bankEmployeeDestroyForm" }, class: "bank-employee__button-destroy", remote: true %>
<% else %>
<%= button_to "", bank_employee_path(#bank_employee), method: :delete, form: {id: "bankEmployeeDestroyForm" }, class: "bank-employee__button-destroy" %>
<% end %>
You need a different approach.
<% if #bank_employee.clients.empty? %>
<%= button_to "", bank_employee_confirm_path(#bank_employee), form: {id: "bankEmployeeDestroyForm" }, class: "bank-employee__button-destroy", remote: true %>
<% else %>
<%= button_to "", bank_employee_path(#bank_employee), method: :delete, form: {id: "bankEmployeeDestroyForm" }, class: "bank-employee__button-destroy" %>
<% end %>
now you need two things:
inside routes.rb create a collection for your blank_employee_confirm_path:
whatever setup you have, i assume you have some kind of blank_employees resources path:
resources :blank_employees do
member do
get :confirm
end
end
now, inside your controller you need to add the method confirm:
def confirm
## do whatever you want do to here
render :js
end
this will then head over to confirm.js
create a confirm.js.erb file inside your blank_employees view folder. To confirm this is working, you can add a console.log('it works') in it.
Once you have confirmed that it is working you can add the javascript code to the confirm.js.erb file:
$('#modal-body').html('<%= escape_javascript(render :partial => 'shared/your_confirmation_modal'%>');
with this setup, you need in your edit.html.erb file a <div id="modal-body"></div> that will take the modal. Also notice that in my example the confirmation modal is stored in "views/shared/_your_confirmation_modal.html". Change the path to your setup or create the exact path in order to make this work!
Notice that the "confirm" path is for the blank_employees that have no clients. The button will be only rendered when there is no client. All the other logic you had before for the blank_employees with clients stay the same. You don't need to change there anything. If you had any logic inside there for blank_employees without any clients, move the code to the confirm method.
One more thing: Make sure to add to your destroy method a render :js as well, and inside destroy.js.erb add the same kind of logic like inside confirm.js.erb, beside that you want to render the modal for blank_employees with clients. Your destroy.js.erb file should look something like this:
$('#modal-destroy').html('<%= escape_javascript(render :partial => 'shared/your_destroy_modal'%>');
Very important: Just like with the first modal, add a <div id="modal-destroy"></div> to your edit.html.erb file, otherwise it wont render the modal.
If something is unclear, let me know!
Greetings!
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.
i'd like to make a like/dislike ability on my RoR application. How can i make it via Ajax-requests ?
dislike and like - are integer how can i make an Ajax-request, than i can send the data of what i want to increment either "like" or "dislike" counter in my methods
I have a table with posts :
#app/views/dashboard/view.html.erb
<table>
<%if #post.count!=0%>
<%#post.each do |p|%>
<%if !p.text.nil?%>
<tr>
<td><b class="margin"><h4><%=p.text%></b></h4></td>
<td>by <%=p.user.username%> </td>
<td><span class="glyphicon glyphicon-thumbs-up likeAction"><%= link_to p.like, dashboard_like_path, :remote => true, :id => 'likecount' %> </td>
<td><span class="glyphicon glyphicon-thumbs-down"><%= link_to p.dislike, dashboard_dislike_path, :remote => true, :id => 'dislikecount' %> </td>
<%end%>
<% end %>
<%else%>
There's no posts yet, but you can add <%=link_to "one", dashboard_posts_create_a_post_path%>
<%end%>
</table>
My js file
#app/views/dashboard/view.js
$('#likecount').text(#post.like);
$('#dislikecount').text(#post.dislike);
my methods in controller :
#app/controller/dahsboard_controller.rb
def like
#post.increment!(:like)
respond_to do |format|
format.html
format.js
end
end
def dislike
#post.increment!(:dislike)
respond_to do |format|
format.html
format.js
end
end
My dashboard.js in assets/javascripts
jQuery(function($) {
$("likeAction").click(function(){
$.ajax({
url: dashboard_like_path,
type: 'POST',
success: function(){
$('#linkcount').text(data);
}
error: function(error){
alert(error);
}
});
});
});
You already have Rails built-in AJAX functionality, so no need for calling $.ajax. Simply set remote: true on your link_to 'Like', ..., remote: true and respond with the same code you have in app/views/dashboard/view.js: format.js { render action: 'view' }
EDIT: As long as like and dislike are set as member routes on posts:
dislike_post POST /posts/:id/dislike(.:format) posts#dislike
like_post POST /posts/:id/like(.:format) posts#like
You will have a params[:id] (if you send one) to do something like #post = Post.find(params[:id]), if you share this code with show, like and dislike. You can create a set_post before filter, so you don't repeat yourself.
You'll probably want to look at a gem called acts_as_votable
This sets much of your model functionality up - allowing you to use the likes of #post.downvote_from #user2 etc. I'll let you look into that, as it's what you need in the backend I think.
In regards the front-end (especially Ajax), you'll have to set up a controller action, and then hit it with a JS request:
#config/routes.rb
resources :posts do
match :vote, via: [:post,:delete]
end
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
respond_to :js, :html, only: :vote
def vote
if request.delete?
#downvote
elsif request.post?
#upvote
end
end
end
This will allow you to use the following:
#app/views/posts/vote.js.erb
$(".element").html("<%=j render partial: "post/vote_count", object: #post %>");
#app/views/posts/index.html.erb
<% #posts.each do |post| %>
<%= render partial: "post/vote_count", object: :post %>
<% end %>
#app/views/posts/_vote_count.html.erb
<% method = #post.liked_by(current_user)
<%= link_to post.likes, post_vote_path(post), method: :post, remote: true %>
--
The Ajax functionality is pre-built into Rails; you have to be wary of which controller action it's going to send you to, as well as the response given.
My above code uses the respond_to block to invoke the .js.erb response -- allowing you to perform some actions when you send your request.
I'm having some issues deleting my comments with Ajax. I think I'm close but not sure and would like some advice. Still getting used to jquery and such. I can remove the comment via ajax but not actually delete the record itself so maybe its a simple syntax issue.
destroy.js.erb
$('#remove_comment').remove();
I think I need to tag this with the comment ID but I'm having issues being as the comments are nested under the Pit model.
_comment.html.erb
<div class = "well", id = "remove_comment">
<p>
<%= comment.body %>
<p>posted by: <%= comment.user.name %></p>
<div class = "response">
<p class = "like-response">Was this response persuading to you?</p>
<%= link_to "Yes", pit_comment_like_path(#pit, comment), method: :put %>
<%= link_to "No", pit_comment_dislike_path(#pit, comment), method: :put %>
</div>
<div class = "response-convince">
<p class = "dislike-comment">
<%= comment.get_dislikes.size %> users found this response unpersuasive
</p>
<p class = "like-comment">
<%= comment.get_likes.size %> users found this response persuasive</p>
</p>
</div>
<p>
<%if comment.user == current_user %>
<%= link_to 'Destroy Comment', [#pit, comment],
method: :delete,
data: { confirm: 'Are you sure?' }, remote: true, class: "btn btn-default" %>
</p>
<% end %>
</div>
Comments Controller
def destroy
#pit = Pit.find(params[:pit_id])
#comment = #pit.comments.find(params[:id])
#comment.destroy
respond_to do |format|
format.html {redirect_to pit_path(#pit)}
format.js {}
end
Logs seem to be working properly
Started DELETE "/pits/398/comments/63" for 127.0.0.1 at 2014-09-11 12:31:08 -0500
Processing by CommentsController#destroy as JS
Parameters: {"pit_id"=>"398", "id"=>"63"}
Pit Load (0.1ms) SELECT "pits".* FROM "pits" WHERE "pits"."id" = ? LIMIT 1 [["id", 398]]
Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."pit_id" = ? AND "comments"."id" = ? LIMIT 1 [["pit_id", 398], ["id", 63]]
(0.1ms) begin transaction
ActsAsVotable::Vote Load (0.1ms) SELECT "votes".* FROM "votes" WHERE "votes"."votable_id" = ? AND "votes"."votable_type" = ? [["votable_id", 63], ["votable_type", "Comment"]]
SQL (0.3ms) DELETE FROM "comments" WHERE "comments"."id" = ? [["id", 63]]
(3.0ms) commit transaction
Rendered comments/destroy.js.erb (0.5ms)
Completed 200 OK in 13ms (Views: 3.9ms | ActiveRecord: 3.7ms)
This is the associated markup I have in pits/show.html.erb
<h3>Responses</h3>
<div id = "comment_body">
<%= render #pit.comments %>
</div>
<%= render partial: "comments/form" %>
pit.rb
class Pit < ActiveRecord::Base
validates :topic, :author, :summary, presence: true
acts_as_votable
has_many :comments
belongs_to :user
mount_uploader :image, ImageUploader
end
comment.rb
class Comment < ActiveRecord::Base
acts_as_votable
belongs_to :pit
belongs_to :user
end
Everything inserts correctly with my create.js.erb. I just need to remove it and I think I need to pass in the comment ID or something to that effect. Any advice here would be appreciated. Thanks.
Actually, the comment is being DELETED as logs shows the query:
SQL (0.3ms) DELETE FROM "comments" WHERE "comments"."id" = ? [["id", 63]]
I guess your jQuery doesn't remove the appropriate comment after the callback. You can try changing view code of _comment.html.erb:
<div class = "well", id = "remove_comment_<%= comment.id %>">
<p>
<%= comment.body %>
And then your destroy.js.erb:
$("#remove_comment_<%= #comment.id %>").remove(); // Since #comment will be available in the variable here!
create a link_to with a data-id attribute, when clicked use jquery to make a GET request to your controller.
First, create a route for the delete action in your config/routes.rb:
get 'delete_comment' => 'comments#delete_comment'
Next, add a method to your controller (assumably CommentsController):
def delete_comment
#comment = Comment.find(params[:id])
#comment.destroy
end
Now, set up a link in your view:
= link_to "Remove Comment", "#", :class => "remove_comment", :'data-id' => #comment.id
Now set up the jquery GET request to fire when you click on the link:
$(".remove_comment").click(function(event){
event.preventDefault();
$.get("/delete_comment", {id: $(this).attr("data-id") } );
});
In this example, you would need to rename your delete.js.erb file to delete_comment.js.erb
I've got a form that should render error messages but I'm not sure how to render the error messages since my form is being displayed with Javascript after clicking a button. I'm using Devise for the registration which is working just not displaying the error messages. I'm using Rails 4.
This is the code that hides the register button after it's clicked and shows the form:
<script type="text/javascript">
$(function() {
$('#prelaunchbutton').click(function() {
$('#prelaunchregistrationbutton').fadeToggle('slow');
$('.prelaunchhide').fadeToggle('slow');
return false;
});
});
</script>
Here is the Devise registration form (Shortened for brevity):
<div class="prelaunch_preregisterbutton" id="prelaunchregistrationbutton">
<%= link_to image_tag("pre-register.png", :border=>0), '#', id:'prelaunchbutton' %>
</div>
<div class="span10 offset1 prelaunchhide">
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :class => "form-inline") do |f| %>
...
<% end %>
</div>
Here is the Devise registration controller:
class RegistrationsController < Devise::RegistrationsController
before_filter :authenticate_user!
respond_to :html, :json
def new
end
def create
#user = User.new(user_params)
#user.build_store(store_params)
if #user.save
UserMailer.preregister_confirmation(#user).deliver
sign_in #user
redirect_to prelaunch_gear_path, :flash => {:success => "Welcome"}
else
render root_path
end
end
...
protected
def after_update_path_for(resource)
user_path(resource)
end
private
def user_params
params.require(:user).permit(:firstname, :lastname, :email, :password)
end
def store_params
params.require(:store).permit(:storename)
end
end
You might look into using toastr for displaying javascript notifications.
Here's an SO question I answered before that may help set it up: Rails 4 - Toaster notifications rather than flash notifications -- you would just need to make your flash notification be flash.now[:toastr] and then be sure to call the initializer for that in the JS returned by the AJAX request.