Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Ruby

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="activity_<%= activity.id %>_<%= activity.member.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

1 Answer

I wasn't defining @activity in my create action in my controller. So defining it like so made everything work.

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

        respond_to do |format|
          if @status.save
            @activity = current_member.create_activity(@status, 'created') # needed to define it here
            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