Reputation: 4605
I'm trying to create an AJAX message stream in my Rails 3 app using jQuery. The app shows posts and replies to the post. Right now it requires the user to refresh the browser to update:
//in app/views/posts/_replies.html.erb
<div class="replies">
<% for reply in @replies %>
<div class="reply">
<p><%= reply.body %></p>
</div>
<% end %>
</div>
I'm trying to use the jQuery port of PeriodicalUpdater (https://github.com/RobertFischer/JQuery-PeriodicalUpdater/) to ajaxify the replies as a constantly-updating stream.
Based on Ryan Bates' Railscast (http://railscasts.com/episodes/136-jquery), here is what I've got:
// in public/javascripts/application.js
$(document).ready(function(){
$.PeriodicalUpdater('/replies', {
method: 'post',
data: ???,
minTimeout: 1000,
maxTimeout: 8000,
multiplier: 2,
type: 'json',
maxCalls: 0,
autoStop: 0
}, function(data) {
????
});
});
I'm new to js and jQuery, so it's not really clear to me how to pass the current post to the server nor how to have it automatically render the replies
partial. Here is the rest of my code:
// in app/views/posts/show.js.erb
$("#replies").append("<%= escape_javascript(render 'replies') %>");
// app/controllers/posts_controller.rb
class PostsController < ApplicationController
def replies
@replies = Post.find(params[:id]).replies
respond_to do |format|
format.js
end
end
end
Is there some way to pass the ID of the current post
to the controller? Maybe even pass the ID of the last reply
so the server will only send back new replies?
Also, do I have to manually append everything inside of the callback function of PeriodicalUpdater? Or is there a way to render the show
partial for the updates?
Update: I did like @Spencer suggested below, changing it to first have a button that you push to do an AJAX update.
I'm having trouble passing that parameter. My actual code looks up things based on a unique URL key, so I just have:
<input type="button" value="Refresh" onclick="getReplies('<%= @post.url_key %>');" />
Then in my application.js file:
function getReplies(url_key) {
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
url: "/replies",
data: { url_key: url_key },
success: function(msg) {
$.each(msg.Replies, function(index, reply) {
$("#replies").prepend('<p>' + reply.body + '</p>')
});
}
});
}
In my controller, I have:
def replies
@post = Post.find_by_url_key(params[:url_key])
respond_to do |format|
format.json { render :json => @post.replies }
end
end
I get:
Processing by PostsController#replies as JSON
Parameters: {"_json"=>"url_key=some-post"}
←[1m←[35mPost Load (1.0ms)←[0m SELECT `posts`.* FROM `posts` WHERE (`posts`.`url_key` IS NULL) LIMIT 1
So it looks like the parameter isn't being retrieved from params
.
Upvotes: 2
Views: 1326
Reputation: 35117
I think I see a couple issues here. I'd recommend breaking up the problem a bit since as you say you're new to jQuery. Start out using a manual button to retrieve the latest posts through AJAX. Once you've handled that then move on to the periodic updater.
The second thing I would recommend is look into various templating methods that people use with jQuery. That is to say that you should have Ruby simply return a JSON data structure which you can then have jQuery take and populate a post template with the various pieces of data.
Here's an example of a templating method I've used on one of my sites. I start out with the following static HTML.
<table id="tblExpenses"><tbody>
<tr>
<th>Date</th>
<th>Title</th>
<th>Amount</th>
<th>Attachments</th>
<th colspan="2"> </th>
</tr>
<tr class="expenseTemplate" style="display: none;">
<td class="litDate"></td>
<td class="litTitle"></td>
<td class="litAmount"></td>
<td class="litAttachments">test</td>
<td><a class="lnkEdit" href="#">Edit</a></td>
<td><a class="lnkDelete" href="#">Delete</a></td>
</tr>
</tbody></table>
I then perform an AJAX request for expenses stored in the database and dynamically create new rows to insert into the Expenses table.
function showTaxYear(taxYearId) {
$.ajax({
type: "GET",
contentType: "application/json; charset=utf-8",
dataType: "json",
url: "/Services/GetExpensesByTaxYearId",
data: { taxYearId: taxYearId },
success: function(msg) {
$.each(msg.Expenses, function(index, expense) {
var row = $("#tblExpenses tr.expenseTemplate")
.clone()
.removeClass("expenseTemplate")
.show()
.addClass("expense")
.addClass("expenseid_" + expense.ID);
row.find("td.litDate").text(expense.Date);
row.find("td.litTitle").text(expense.Title);
row.find("td.litAmount").text(expense.Total);
row.find("a.lnkEdit").attr("data-id", expense.ID)
.unbind().click(function() { editExpense($(this)); });
row.find("a.lnkDelete").attr("data-id", expense.ID)
.unbind().click(function() { deleteExpense($(this)); });
if (expense.Attachment != "")
row.find("td.litAttachments").empty().append($("<a>View</a>").attr("href", expense.Attachment));
else
row.find("td.litAttachments").empty();
row.insertBefore("#tblExpenses tr.expenseTemplate");
});
}
});
}
GetExpensesByTaxYearId returns a JSON result that looks like this
{"Expenses":[{"ID":9,"Title":"Some expense","Description":"","Date":"02/02/2010","Amount":"$10.00","Tax":0.00,"Attachment":"","Total":"$10.00"}]}
Once you've done that you can actually check to see if you've already created an element for whatever data you get back from Ruby by doing a jQuery search. In my example for instance I can check to see if an element exists with the class expenseid_<Identifier>
. if it does, I've already rendered that expense.
I know this is probably a lot to chew on but streaming AJAX content has a lot to it. Let me know if I can clarify further.
Upvotes: 1