Gokul
Gokul

Reputation: 3231

Rails: Capturing error messages in JavaScript

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.

Upvotes: 3

Views: 7037

Answers (1)

Sean Huber
Sean Huber

Reputation: 3985

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 %>

Upvotes: 7

Related Questions