Reputation: 9573
Let's assume that I have a Rails app in which users can write blog posts. After the user starts typing, an AJAX function is called which commits a POST request and saves the blog post. What I'd like to do is be able to check if a blog post is already in the database, and then continuously save it through AJAX as the user types.
I'm using HTML5 contenteditable attribute for the title and content blog post input areas:
<div id="entry-create-partial">
<div id="title-create-partial" name="title" contenteditable="true" data-placeholder='Title it' style="color:black"></div>
<div id="content-create-partial" name="content" contenteditable="true" data-placeholder='Write anything' style="color:gray"></div>
</div>
Here's the JavaScript to do what I want:
$(function(){
if ($("#title-create-partial").length > 0) {
setTimeout(autoSavePost, 6000);
}
});
function autoSavePost(){
$.ajax({
type: "POST",
url: "/submissions",
dataType: "json",
data: {title: $("#title-create-partial").text().serialize(), content: $("#content-create-partial").text().serialize()},
success: function(){
$("#success-indicate").fadeIn().delay(4000).fadeOut();
}
});
setTimeout(autoSavePost, 6000);
}
However, in my JS console I'm getting an error stating that the data I've given to .ajax doesn't have a serialize method. I'm not sure how to pass it correctly to AJAX.
Also, once I do send it through AJAX, I'm not sure how to update the :content
on the database side. Here's the create
controller that the AJAX url routes to:
def create
@submission = Submission.where(title: params[:title]).first_or_create(
title: params[:title],
content: params[:content],
user_id: params[:user_id]
)
@submission.user = current_user
if @submission.save
redirect_to :action => :show, :id => @submission.id
else
redirect_to :action => :index
end
end
How do I send the text from contenteditable divs, and then in the controller how do I update the content? I've heard about update_attributes, but I'm not sure how to use that.
Upvotes: 2
Views: 4653
Reputation: 48589
This code here:
$("#title-create-partial").text()
returns a string. Then you call a method named serialize() on a string. There is no javascript method named serialize(). jQuery has a method named serialize(), but it must be called on a jQuery object. A string is not a jQuery object. And as far as I can tell, serialize() doesn't even work on non-form elements. Here is how you would use serialize:
var str = $( "form" ).serialize();
Or,
var str = $( "#my_text_input, #my_select").serialize()
which will produce a string like this:
"text_input_name=hello&select_name=goodbye"
However, according to the jQuery docs, jQuery automatically serializes the key/value pairs you specify for data inside .ajax()--so you don't want to serialize them again.
How do I send the text from contenteditable divs, and then in the controller how do I update the content?
You need to pick an event to trigger your ajax call. Probably the best bet would be to set a timeout to go off every 5 minutes, which calls your ajax function.
As far as updating the content in the database, you can query your database and see if the user has a post with the same title. If the query comes back with 0 results, then create a new post with the content. Otherwise, set the post's content to the new content. Something like this:
id = params[:session][:id]
ajax_title = params[:title]
ajax_content = params[:content]
user = User.find id
posts = user.posts.where(title: ajax_title)
if posts.empty?
post = user.posts.create(title: ajax_title, content: ajax_content)
else
post[0].content = ajax_content
post[0].save
end
That assumes you've done:
class User < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :user
end
Upvotes: 3
Reputation: 24815
This is rather a comment than an answer. I write it here because it is a bit long.
I have difficulty to understand your logic. It seems the server will create tons of posts on every hit.
Let's say you are going to write a post titled "Updating Rails models through AJAX".
When you type "U" in title input, the Ajax is triggered, the server received a title "U". There is no such post titled "U" in db, so controller create one.
Then you type "p". Again there is no such post, so new post "Up" created.
Now you have two posts, "U" and "Up". And more will be created once typing.
See the problem? Correct me if wrong.
Add: About Solution
I have not done such case before so can't provide practice but some ideas only.
Subjectively I don't think creating post by direct typing is a good idea, the reason as above. I would suggest:
Basic: Create post on submit, either Ajax of normal request.
Advanced: Add option to save as draft. And also add feature to see or load from draft(edit form). The post is same as normal post with extra column "draft" as true
Even advanced: After typing lots of code, saying 100 characters in body, autosave it as a draft. This is similar to your code with the main difference at server-side draft setting.
You may also noticed, in Stackoverflow, if you write an answer but don't submit it, next time when you go back, the draft of answer is still in in the form. This is easier than your case, because an answer is always associated with a question and you are only allowed to post answers one by one, so the draft can be easily identified.
But blog post is different, it's the main model, no association, and you can create multiple drafts at same time. So you need to pay attention to draft loading.
Just some cents.
Upvotes: 1
Reputation: 5105
please only using text()
when geting value title and content. And when using serialize()
is more effective on getting data from form input. So change your code into below.
$(function(){
if ($("#title-create-partial").length > 0) {
setTimeout(autoSavePost, 6000);
}
});
function autoSavePost(){
$.ajax({
type: "POST",
url: "/submissions",
dataType: "json",
data: {title: $("#title-create-partial").text();, content: $("#content-create-partial").text();},
success: function(){
$("#success-indicate").fadeIn().delay(4000).fadeOut();
}
});
setTimeout(autoSavePost, 6000);
}
Upvotes: 0