iamdhunt
iamdhunt

Reputation: 453

Rails ajax prepend activity

In my app I have a status model that also creates an activity item when a status is created. My activity model is polymorphic is belongs to a number of different models. Everything works fine, but I'm having trouble with my ajax status creation. On users' profile pages there's a status form and an activity list. I want users to be able to create a status through ajax and have the corresponding activity item appended to the list. Any ideas how I can do this? Thanks in advance.

statuses_controller.rb

class StatusesController < ApplicationController

    before_filter :authenticate_member!, only: [:index, :new, :create, :edit, :update, :destroy] 
    before_filter :find_member
    before_filter :find_status, only: [:edit, :update, :destroy]

    rescue_from ActiveRecord::RecordNotFound do
        render file: 'public/404', status: 404, formats: [:html]
    end

    def show
        @status = Status.find(params[:id])
        @commentable = @status
        @comments = @commentable.comments.order('created_at desc').page(params[:page]).per_page(15)
        @comment = @commentable.comments.new
        respond_to do |format|
          format.html # show.html.erb
          format.json { redirect_to profile_path(current_member) }
        end
    end

    def new
        @status = Status.new
        @status.build_document

        respond_to do |format|
          format.html # new.html.erb
          format.json { render json: @status }
          format.js
        end
    end

    def create
        @status = current_member.statuses.new(params[:status])

        respond_to do |format|
          if @status.save
            current_member.create_activity(@status, 'created')
            format.html { redirect_to :back }
            format.json
            format.js
          else
            format.html { redirect_to profile_path(current_member), alert: 'Post wasn\'t created. Please try again and ensure image attchments are under 10Mbs.'  }
            format.json { render json: @status.errors, status: :unprocessable_entity }
            format.js
          end
        end
    end

    def destroy
        @activity = Activity.find_by_targetable_id(params[:id])
        @commentable = @status
        @comments = @commentable.comments
        if @activity
            @activity.destroy
        end
        if @comments
            @comments.destroy
        end 
        @status.destroy

        respond_to do |format|
            format.html { redirect_to profile_path(current_member) }
            format.json { head :no_content }
        end
    end

    private
        def find_member
            @member = Member.find_by_user_name(params[:user_name])
        end 

        def find_status
            @status = current_member.statuses.find(params[:id])
        end  

end

activities_controller.rb

class ActivitiesController < ApplicationController

    before_filter :authenticate_member!
    before_filter :find_activity, only: [:destroy]

    def index
        params[:page] ||= 1
        @activities = Activity.for_member(current_member, params)

        respond_to do |format|
          format.html # index.html.erb
          format.js
        end
    end

    def destroy
        @status = @activity.targetable
        if @activity.targetable_type == 'Status'    
            @status.destroy
        end
        @activity.destroy

        respond_to do |format|
          format.html { redirect_to :back }
          format.json { head :no_content }
          format.js
        end
    end

    def upvote
        @activity = Activity.find(params[:id])
        if current_member.voted_up_on? @activity
            @activity.unliked_by current_member
        else 
             @activity.liked_by current_member
        end
        respond_to do |format|
          format.html { redirect_to :back }
          format.js
        end
    end

    private

    def find_activity
        @activity = current_member.activities.find(params[:id])
    end 

end

activity.rb

class Activity < ActiveRecord::Base
    belongs_to :member
    belongs_to :targetable, polymorphic: true
    acts_as_votable

    self.per_page = 36

    def self.for_member(member, options={})
        options[:page] ||= 1
        following_ids = member.following_members.map(&:id).push(member.id)
        where("member_id in (?)", following_ids).
        order("created_at desc").
        page(options[:page])
    end 

end

profiles/show.html.erb

<% if @activities.count > 0 %>
    <div id="media_query_stream">
        <div id="activity_stream_wrap">
            <%= render :partial => "activities/activities", locals: { activity: @activity} %>
        </div>
    </div>          
<% else %>
    <div class="none_message">
        No Posts Yet
    </div>
<% end %>    

activities/_activities.html.erb

<% @activities.each do |activity| %>
    <%= render partial: "activities/#{activity.targetable_type.underscore}/#{activity.action}", locals: { activity: activity } %>
<% end %> 

activities/status/_created.html.erb

<div id="" class="list_act_wrap status_fil">

    <div class="act_status_top">
        <span class="">
            <%= avatar_profile_link activity.member, :class => "act_av", title: activity.member.full_name, alt: activity.member.full_name %>
        </span>

        <span class="act_name">
            <%= link_to activity.member.user_name, profile_path(activity.member) %>
        </span>

        <span class="meta"> 
            <%= time_ago_in_words(activity.targetable.created_at) %>
        </span>

        <span class="act_title stat">
            wrote a new <%= link_to 'Status', status_path(activity.targetable_id) %>
        </span>
    </div>

    <div class="act_content">
        <%= Rinku.auto_link(activity.targetable.content_html).html_safe %>
    </div>

</div>

I tried to append the activity through ajax like so:

statuses/create.js.erb

$("#activity_stream_wrap").prepend("<%= escape_javascript(render :partial => 'activities/status/created', :locals => {:activity => @activity}) %>");
$('#stat_count').html("280");
$('#status_form')[0].reset();

But it throws an error like:

undefined method `member' for nil:NilClass

**EDIT**

Here's my create_activity method that actually creates the corresponding activity when a status is created. How do I get to this activity that's created so I can append it?

def create_activity(item, action)
    activity = activities.new
    activity.targetable = item
    activity.action = action 
    activity.save 
    activity
end

Upvotes: 2

Views: 747

Answers (1)

Mandeep
Mandeep

Reputation: 9173

If you look at your statuses/create.js.erb you are rendering your partial like this:

$("#activity_stream_wrap").prepend("<%= escape_javascript(render :partial => 'activities/status/created', :locals => {:activity => @activity}) %>");

Notice you are passing @activity in your partial but if you look at your create method in statuses controller you don't have an instance variable named @activity

Fix:

You need to create your activity and assign it to your instance variable like this:

def create
    @status = current_member.statuses.new(params[:status])

    respond_to do |format|
      if @status.save
        @activity = current_member.create_activity(@status, 'created') # you weren't assigning it to your instance variable
        format.html { redirect_to :back }
        format.json
        format.js
      else
        format.html { redirect_to profile_path(current_member), alert: 'Post wasn\'t created. Please try again and ensure image attchments are under 10Mbs.'  }
        format.json { render json: @status.errors, status: :unprocessable_entity }
        format.js
      end
    end
end

Upvotes: 2

Related Questions