I am using ajax for creating post that belongs to a particular topic where I render the form in post index page. Each post can have many tags and I am also using devise authentication and CanCanCan authorization.
I need to capture the error message of the post submit and show it in the browser through this JavaScript template Create.js.erb with custom error messages instead of doing in post form.
Below is the code:
Post controller
class PostsController < ApplicationController
load_and_authorize_resource
before_action :set_post, only: [:show, :edit, :update, :destroy, :update_status]
skip_before_action :verify_authenticity_token
# GET /posts
# GET /posts.json
def index
if params[:topic_id].present?
#topic = Topic.find(params[:topic_id])
#posts = #topic.posts.paginate(page: params[:page], per_page: 10)
#post = #topic.posts.new
else
#posts = Post.eager_load(:topic, :user).paginate(page: params[:page], per_page: 10)
end
#tags =Tag.all
end
# GET /posts/1
# GET /posts/1.json
def show
#tags = #posts.tags
end
def update_status
current_user.posts<<(#posts)
end
# GET /posts/new
def new
#topic = Topic.find(params[:topic_id])
#posts = #topic.posts.new
#tags =Tag.all
end
# GET /posts/1/edit
def edit
#tags = #posts.tags
end
# POST /posts
# POST /posts.json
def create
#topic = Topic.find(params[:topic_id])
#posts = #topic.posts.create(post_params)
respond_to do |format|
#posts.user_id = current_user.id
if #posts.save
format.html { redirect_to topic_posts_path(#topic), notice: 'Post was successfully created.' }
format.js
format.json { render :show, status: :created, location: #posts }
else
format.html { render :new }
format.json { render json: #posts.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
#tags = #posts.tags
respond_to do |format|
if params[:rate].to_i>0
#posts.ratings.create(:star => params[:rate])
format.html { redirect_to post_path(#posts), notice: 'Rating was successfully updated.' }
elsif #posts.update(post_params)
format.html { redirect_to post_path(#posts), notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #posts }
else
format.html { render :edit }
format.json { render json: #posts.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#posts.destroy
respond_to do |format|
format.html { redirect_to topic_posts_url(#posts.topic_id), notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#posts = Post.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:image, :name, :message, :topic_id, {tag_ids:[]}, :rate, :user_id)
end
protected
def json_request?
request.format.json?
end
end
post form
<%= form_for [#topic, #post], remote: true do |f| %>
<% if #post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<div class="field">
<%= f.label :Image %><br>
<%= f.file_field :image %>
</div>
<div class="field">
<%= f.label :Name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :Message %><br>
<%= f.text_area :message %>
</div>
<% if #tags %>
<% #tags.each do |tag| %>
<div>
<%= check_box_tag "post[tag_ids][]", tag.id, #post.tags.include?(tag) %>
<%= tag.name %>
</div>
<% end %>
<% end %>
<br><br>
<%= link_to 'Create Tag', tags_path %>
<br><br>
<%= f.submit %>
<% end %>
create.js.erb
$("#post_table").append("<%= j render #posts %>");
alert("Post created")
index.html.erb
<p id="notice"><%= notice %></p>
<div id='ajax_loader' style="position: absolute; left: 50%; top: 50%; display: none;">
<%= image_tag "ajax-loader.gif" %>
</div>
<script>
$(document).ajaxStop(function(){
$("#ajax_loader").hide();
});
$(document).ajaxStart(function(){
$("#ajax_loader").show();
});
</script>
<%= will_paginate %>
<h1>Listing Posts</h1>
<table id = "post_table">
<thead>
<tr>
<th><th>Name</th></th>
<th><th>Author</th></th>
<th><th>Message</th></th>
<th><th>Status</th></th>
<th colspan="4"></th>
</tr>
</thead>
<tbody>
<%= render #posts %>
</tbody>
</table>
<br>
<% if #topic %>
<%= link_to 'New Post', "#", id: "new_post" %>|
<section id = "new_post_section">
<%= render 'form' %>
</section>
<%= link_to 'Back to Topics', topic_path(#topic) %>
<% else %>
<% link_to 'New Post', new_post_path %>
<% end %>
<%= will_paginate %>
Post model
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :topic
has_many :comments
has_and_belongs_to_many :tags
has_many :ratings
validates_presence_of :name, :presence => true
validates_length_of :name, maximum: 5
has_attached_file :image
#validates_attachment_presence :image, :presence => true
validates_attachment_content_type :image, :content_type => ['image/jpeg', 'image/png']
validates_attachment_size :image, :in => 0..100.kilobytes
end
Please help me.
Modify your the create action in the posts_controller.rb to something like this (notice that format.js was added to the else clause of the respond_to block):
# POST /posts
# POST /posts.json
def create
#topic = Topic.find(params[:topic_id])
#posts = #topic.posts.create(post_params)
respond_to do |format|
#posts.user_id = current_user.id
if #posts.save
format.html { redirect_to topic_posts_path(#topic), notice: 'Post was successfully created.' }
format.js
format.json { render :show, status: :created, location: #posts }
else
format.html { render :new }
format.js # call create.js.erb on save errors
format.json { render json: #posts.errors, status: :unprocessable_entity }
end
end
end
Then, in create.js.erb you can check for errors and handle them however you'd like. Example:
<% if #post.errors.any? %>
alert("ERROR(S): <%= j #post.errors.full_messages.join('; ') %>")
<% else %>
$("#post_table").append("<%= j render #posts %>");
alert("Post created");
<% end %>
Related
In my blog app, there are posts and each post can optionally have tags. When the user is on the new or edit post form, they currently have the means to select or deselect tags to associate with the post. There is a many-to-many relationship between the post and tag models and I am using bootstrap, bootstrap_form and bootstrap-select. This all works seemingly very well. The problem is, when the user is assigning tags to their post, these tags currently have to already exist (otherwise they would have to abort their post and go add tags...bad user experience). I am trying to devise a way to give the user the option to create new tags and/or select already-existing tags and apply them to the post, all on the same post form in the collection_select field, all at the same time.
Apparently I'm asking the wrong questions on google...wouldn't this be a common need, already solved?
I am asking for guidance in providing an 'add new tag' functionality to an otherwise functional collection_select field. How best to go about this?
Posts Controller:
class PostsController < ApplicationController
before_action :set_post, only: %i[edit update interim destroy]
# GET /posts
# GET /posts.json
def index
if user_signed_in? && current_user.admin_role
if params[:tag]
#posts = Post.tagged_with(params[:tag]).all.order('updated_at DESC').page params[:page]
else
#posts = Post.all.order('updated_at DESC').page params[:page]
end
else
if params[:tag]
#posts = Post.tagged_with(params[:tag]).where(published: true).order('updated_at DESC').page params[:page]
else
#posts = Post.where(published: true).order('updated_at DESC').page params[:page]
end
end
end
# GET /posts/new
def new
#post = current_user.posts.build
#categories = Category.pluck(:name, :id)
end
# GET /posts/1/edit
def edit
#categories = Category.pluck(:name, :id)
#cat = #post.category_id
end
# POST /posts
# POST /posts.json
def create
#post = current_user.posts.create(post_params)
respond_to do |format|
if #post.save
if params[:interim]
format.html { redirect_to edit_post_path(#post), notice: 'Post was successfully created.' }
format.json { redirect_to edit_post_path(#post), status: :created, location: #post, notice: 'Post was successfully created.' }
elsif params[:complete]
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: #post }
end
else
format.html { render :new }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
respond_to do |format|
if #post.update(post_params)
if params[:interim]
format.html { redirect_to edit_post_path(#post), notice: 'Post was successfully updated.' }
format.json { redirect_to edit_post_path(#post), status: :ok, location: #post, notice: 'Post was successfully updated.' }
elsif params[:complete]
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #post }
end
else
format.html { render :edit }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_post
#post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :content, :user_id, :published, :category_id, :tag_list, :tag, { tag_ids: [] }, :tag_ids,
comment_attributes: [:id, :title, :user_id, :content, :post_id, :parent_id, :ancestry, :commentable, :commentable_id, :commentable_type])
end
end
No tags controller (not needed thus far)
Post Model:
class Post < ApplicationRecord
has_many :taggings
has_many :tags, through: :taggings
has_rich_text :content
include PgSearch::Model
multisearchable :against => [:title, :content]
def self.published(post)
post.published
end
def self.tagged_with(name)
Tag.find_by!(name: name).posts
end
def self.tag_counts
Tag.select('tags.*, count(taggings.tag_id) as count').joins(:taggings).group('taggings.tag_id')
end
def tag_list
tags.map(&:name).join(', ')
end
def tag_list=(names)
self.tags = names.split(',').map do |n|
Tag.where(name: n.strip).first_or_create!
end
end
end
Tag Model:
class Tag < ApplicationRecord
has_many :taggings
has_many :posts, through: :taggings
end
Tagging Model:
class Tagging < ApplicationRecord
belongs_to :tag
belongs_to :post
end
Post Form Partial (the actual new and edit views do nothing but render this form):
<%= bootstrap_form_for #post, local: true, html: { class: 'form-horizontal' } do |f| %>
<%= f.text_field :title %>
<%= f.rich_text_area :content, control_class: 'trix-content-edit' %>
<%= f.collection_select :category_id, Category.all, :id, :name %>
<%= f.form_group :published, label: { text: "Publication Status" } do %>
<%= f.radio_button :published, true, label: "Published" %>
<%= f.radio_button :published, false, label: "Draft" %>
<% end %>
<%= f.collection_select :tag_ids, Tag.order(:name), :id, :name, {label: 'Tags', include_blank: true}, {class: 'selectpicker show-tick', multiple: 'multiple', title: 'Make your selection...', 'data-live-search': 'true', 'data-actions-box': 'true'} %>
<br><br>
<%= f.submit "Save and Resume Editing", name: "interim", class: "btn btn-primary" %>
<%= f.submit "Save and Quit", name: "complete", class: "btn btn-primary" %>
<br><br>
<% end %>
No forms for tag at this time.
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
I am using the gem "acts_as_votable" in my Rails application so that Users can vote on Posts.
I'm adding Ajax functionality so that the entire page doesn't have to refresh when a user upvotes a post. This is what I have:
routes.rb
Rails.application.routes.draw do
devise_for :users, controllers: {registrations: 'registrations'}
resources :posts do
member do
put "like", to: "posts#upvote"
put "dislike", to: "posts#downvote"
end
end
# Define Root URL
root 'pages#index'
# Define Routes for Pages
get '/home' => 'pages#home'
get '/explore' => 'pages#explore'
get '/privacy' => 'pages#privacy'
get '/:id' => 'pages#profile'
end
pages_controller.rb
def explore
#posts = Post.where('created_at >= :one_days_ago', one_days_ago: Time.now - 16.hours)
end
posts_controller.rb
class PostsController < ApplicationController
def index
#posts = Post.allow
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
#post.user_id = current_user.id
respond_to do |f|
if (#post.save)
f.html { redirect_to :back, :flash => { :notice => "Posted" } }
else
f.html { redirect_to :back, :flash => { :alert => "Error" } }
end
end
end
def destroy
#post = Post.find(params[:id])
if current_user == #post.user
#post.destroy
end
redirect_to :back, alert: 'Deleted.'
end
def upvote
#posts = Post.find(params[:id])
#posts.upvote_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
def downvote
#posts = Post.find(params[:id])
#posts.downvote_by current_user
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
private
def post_params # allows certain data to be passed via form.
params.require(:post).permit(:user_id, :content)
end
end
explore.html.erb
<% if #posts.each do |p| %>
<div class="panel-body">
<p class="post-content"><%= auto_link(p.content, :html => { :target => '_blank' }) %></p>
<%= render :partial => 'vote', :class => "vote", :locals => { p: #posts } %>
</div>
<% end %>
_vote.html.erb
<div id="vote_<%= p.id %>" class="vote">
<%= link_to 'Vote Up', like_post_path(p), :class => "upvote", :method => :put, :remote => true %>
<span><%= p.score %></span> <!--show the current vote-->
<%= link_to 'Vote Down', dislike_post_path(p), :class => "downvote", :method => :put, :remote => true %>
</div>
upvote.js.erb
$(document).ready(function(){
$('#vote_<%= p.id %>').html("<%= escape_javascript render :partial => '/pages/vote' %>");
});
When I go to run the project I get this error:
NoMethodError in Pages#explore
Showing /home/ubuntu/workspace/app/views/pages/_vote.html.erb where line #1 raised:
undefined method `id' for #<Post::ActiveRecord_Relation:0x007f6715d018e8>
Did you mean? ids
<div id="vote_<%= p.id %>" class="vote">
The issue arises from trying to render _votes.html.erb.
Where am I going wrong here? I've been stuck on this for a while and would appreciate any help. Thanks!
You are passing entire collection to partial
<% if #posts.each do |p| %>
<div class="panel-body">
<p class="post-content"><%= auto_link(p.content, :html => { :target => '_blank' }) %></p>
<%= render :partial => 'vote', :class => "vote", :locals => { p: #posts } %>
</div>
<% end %>
In line <%= render :partial => 'vote', :class => "vote", :locals => { p: #posts } %> replace #posts with p
I'm following the jquery file upload video from RailsCast but I can't get the images to show up without refreshing the page.
My photo is being uploaded through this form:
<%= simple_form_for #upload, html: { multipart: true, id: 'add_new_project_photos' } do |f| %>
<%= f.simple_fields_for :project_images, ProjectImage.new, child_index: ProjectImage.new.object_id do |ff| %>
<%= ff.file_field :photo, multiple: true, name: "project[project_images_attributes][][photo]" %>
<% end %>
<% end %>
I also created a create.js.erb file.
<% if #project.project_images.new_record? %>
alert("Failed to upload painting: <%= j #painting.errors.full_messages.join', ').html_safe %>");
<% else %>
$("#added_photos").append("<%= j render(#project) %>");
<% end %>
My controller
class ProjectsController < ApplicationController
#... other methods
def create
#project = Project.new(trip_params)
#upload = Project.create(trip_params)
if request.xhr?
redirect_to root_url
else
redirect_to :back
end
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render :show, status: :created, location: #project }
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
end
My coffee script file
jQuery ->
$('#add_new_trip_photos').fileupload
dataType: "script"
You need to respond to js as well. In your create method you are only responding to html and json
format.js
Should be placed inside your respond_to block
I have 2 model Post and Comment, where Post has-many Comments and Comment belongs_to Post.
Every thing working fine with creation of posts and comments for that posts.
Now I have a requirement where when I click on "create new comment" in posts/show page, I want to show the comments/_form in modal.
comments_controller.rb:
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
# GET /comments
# GET /comments.json
def index
#comments = Comment.all
respond_with(#comments)
end
# GET /comments/1
# GET /comments/1.json
def show
respond_with(#comments)
end
# GET /comments/new
def new
#post = Post.find(params[:post_id])
#comment = #post.comments.build
end
# GET /comments/1/edit
def edit
#post = Post.find(params[:post_id])
end
# POST /comments
# POST /comments.json
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(comment_params)
respond_to do |format|
if #comment.save
format.html { redirect_to(#comment, :notice => 'Article was successfully created.') }
format.xml { render :xml => #comment, :status => :created, :location => #comment }
format.js
else
format.html { render :action => "new" }
format.xml { render :xml => #comment.errors, :status => :unprocessable_entity }
format.js
end
end
end
# PATCH/PUT /comments/1
# PATCH/PUT /comments/1.json
def update
#post = Post.find(params[:post_id])
#comment.update(comment_params)
redirect_to post_path(#post)
end
# DELETE /comments/1
# DELETE /comments/1.json
def destroy
#comment.destroy
respond_to do |format|
format.html { redirect_to comments_url, notice: 'Comment was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_comment
#comment = Comment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def comment_params
params.require(:comment).permit(:commenter, :description)
end
end
posts/show.html.erb:
<%= link_to 'New Coment', new_post_comment_path(#post), :id => 'create_comment' %>
comments/new.html.erb:
<div id="content">
<h1>New Comment</h1>
<%= render 'form' %>
</div>
comments/_form.html.erb:
<%= form_for([#post, #comment], :remote => true) do |f| %>
<div class="field">
<%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
comments/create.js.erb:
<%- if #comment.errors.any? %>
console.log('Error');
$('#dialog-form').html('<%= escape_javascript(render('form')) %>');
<%- else %>
console.log('Created');
$('#dialog-form').dialog('close');
$('#dialog-form').remove();
$('table').append('<%= escape_javascript(render(#comment)) %>');
<%- end %>
I don't know where I went wrong. When I click on "create comment" it is redirecting to new page instead of pop-up model and even couldn't create comment.
Please help.
Create a partial for showing comment form.
Render this partial to post show page as hidden.
show hidden partial.
Example:
<%= link_to 'Show Modal', "#", data: {toggle: "modal", target: "#modal"} %>
hidden partial
<div class="modal hide fade" id="modal" title="My modal">
/* your comment form */
/* render comment form */
</div>
Just make sure target is your hidden partial id.