In place editing with Javascript, jQuery and Rails 3

Posted on   January 16th, 2011

Introduction

In the project I’m working on I had to create a script that would allow users to update their information directly from their (show) views.
From a general perspective, I think it is useful to give users the option to edit-in-place contents by just clicking them, leaving forms for when there is no other alternative (such as creating new objects). To me, an in-place editor feels more natural and usable and helps notably the user experience. The obvious fact is that most real world applications, such as Flickr or Facebox have implemented this feature in most of their interfaces.

To do that, I wanted to think of a general solution, one that could be applied to my other projects. Even a framework agnostic solution. I decided I would write it completely in Javascript and communicate with the server in a conventional RESTful way. Doing a bit of research before starting out, I ran across the project of Jan Varwig called Rest In Place which did precisely the same thing. So I examined carefully his code, fixed some stuff and extended it to support all usage cases I wanted to cover, this is how BestInPlace is born.

Basically, BestInPlace makes possible to tag (via HTML classes and HTML5 data* attributes) any field that is going to be user-editable so that the script automatically converts it to an form input when the user clicks on it, with no further muss or fuss for the developer.  Of course, this field can take the form of a one-line text input, a textarea for longer texts, a select dropdown that will populate with your custom collection of options or boolean sort of data that works the same way a checkbox would, and allows value customization as well. Additionally, the script will trim and sanitize all user input, display server errors in case the format is not proper, it will also allow you to provide an external handler to activate the input.

Before getting into details.

SEE THE DEMO | SEE THE CODE

Read the rest of this entry »

Tags:

28 Comments »

jQuery and Rails 3: A mini tutorial

Posted on   May 7th, 2010

As most of Rails developers, recently I’ve been through a process of unlearning all concepts of older versions of Rails and learning again the new ones of 3. But hey! I must admit that so far it’s been more pleasure than pain as things only get simpler and more natural than they used to be!

Here I’d like to talk about how simple it has become to integrate unobtrusive jQuery to a Rails app. Let’s use as an example a system of comments. I’ll create a simple app to create YouTube like comments:

Start typing in your teminal:

rails myCommentsApp
rails g resource Comment name:string body:text
rake db:migrate

Then we have to include the jQuery library and the jQuery driver file and place it inside /public/javascripts/ (or grab the helpers generator of Code Officer and do it automatically if you wish).  Now remove the  javascript_include_tag :defaults in the layout and add the following includes:
/app/views/layouts/application.html.erb

javascript_include_tag  "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"
javascript_include_tag  "http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js"
javascript_include_tag "jquery-rails.js"

Now let’s add some logic to our program.  We’ll start out with an index action that will get all the comments:
/app/controllers/comments_controller.rb

def index
    @comments = Comment.all
    respond_to do |format|
      format.html # index.html.erb
      format.rss
    end
  end

And the views for the index will look like this:
/app/views/comments/index.html.erb

<span id="comments_count"><%= pluralize(@comments.count, "Comment") %></span>
<div id="comments">
  <%= render :partial => @comments, :locals => { :list => true } %>
</div>

<hr />

<div id="comment-notice"></div>

<h2>Say something!</h2>
<% form_for Comment.new, :remote => true do |f| %>
	<%= f.label :name, "Your name" %><br />
	<%= f.text_field :name %><br />
	<%= f.label :body, "Comment" %><br />
	<%= f.text_area :body, :rows => 8 %><br />
	<%= f.submit "Add comment" %>
<% end %>

Notice that the new attribute remote is all we need to worry about when creating a form that will submit with Ajax. Rails 3 works with HTML5 attributes, so it only adds the attribute data-remote=”true” to the form and that’s it, the jQuery driver will handle the rest.
We’ll create a partial for the comments:
/app/views/comments/_comment.html.erb

<%= div_for comment do %>
  <span class="dateandoptions">
    Posted <%=time_ago_in_words(comment.created_at)%> ago<br />
    <%= link_to 'Delete', comment_path(comment), :method => :delete, :class => "delete", :remote => true  %>
  </span>
	<p><b><%= comment.name %></b> wrote:</p>
	<br />
  <%= content_tag(:p, comment.body, :class => "comment-body") %>
<% end %>

So now let’s add the fireworks! That is, the asynchronous creation and deletion of comments along with some trendy effects. We come back to the CommentsController and add the actions:
/app/controllers/comments_controller.rb

 def create
    @comment = Comment.create!(params[:comment])
    flash[:notice] = "Thanks for commenting!"
    respond_to do |format|
      format.html { redirect_to comments_path }
      format.js
    end
  end

  def destroy
     @comment = Comment.find(params[:id])
     @comment.destroy
     respond_to do |format|
       format.html { redirect_to comments_path }
       format.js
     end
   end

And we are done with the logic part! A few javascript lines will end the work:
/app/views/comments/create.js.erb

/* Insert a notice between the last comment and the comment form */
$("#comment-notice").html('<div class="flash notice"><%= escape_javascript(flash.delete(:notice)) %></div>');

/* Replace the count of comments */
$("#comments_count").html("<%= pluralize(Comment.count, 'Comment') %>");

/* Add the new comment to the bottom of the comments list */
$("#comments").append("<%= escape_javascript(render(@comment)) %>");

/* Highlight the new comment */
$("#comment_<%= @comment.id %>").effect("highlight", {}, 3000);

/* Reset the comment form */
$("#new_comment")[0].reset();

/app/views/comments/destroy.js.erb

/* Eliminate the comment by fading it out */
$('#comment_<%= @comment.id %>').fadeOut();
/* Replace the count of comments */
$("#comments_count").html("<%= pluralize(Comment.count, 'Comentari') %>");

Finish! That’s all the effort you need nowadays to a create a Web 2.0 fashionable feature such as this one. Of course, you need some styling with CSS, and you would need tons of more things in the real world (such as antispam, authentication, something to comment about…) but that’s the part I chose to talk about today!

I pushed myCommentsApp to Github in case you want to have a closer look (or download the zip version).

You might be interested in checking out my script for letting user in-place edit your application contents.

Tags:

60 Comments »

Reading WordPress from Rails

Posted on   February 8th, 2010

I found odd how few places were talking about reading a WordPress database from a Rails app.
If you want to flash your blog updates somewhere in your Rails app it comes handy to have your WP posts mapped as if they were any other Rails model. How to do so is not complicated so long as you consider a few details.
First off, add a new database in your config/database.yml.

blog:
  adapter: mysql
  encoding: utf8
  host: YOUR HOST
  database: DATABASE OF YOUR WP
  username: USERNAME
  password: PASS

Add your WP model: /app/models/wp_post.rb:

class WpBlogPost < ActiveRecord::Base
  establish_connection :blog
  set_primary_key 'ID'
  set_table_name "wp_XXXXX_posts"
  #Only in case you want to add comments as well:
  has_many :comments, :class_name => "WpComment", :foreign_key => "comment_post_ID"
end

In case you want to add comments as well, /app/models/wp_comment.rb:

class WpBlogComment < ActiveRecord::Base
  establish_connection :blog
  set_table_name "wp_XXXXX_comments"
  set_primary_key "comment_ID"
  belongs_to :post , :class_name => "WpPost", :foreign_key => "comment_post_ID"
end

Attention! You need to tell Rails what field is the primary key since these tables do not follow the Rails convention, and also you need to specify the name of the table within the WP database, something like wp_234f8_posts, or wp_234f8_comments.

When that’s done, you can start using it from your controllers and views. Remember to get all posts marked as “publish”, as WP registers a lot of redundant data in the DB as well. Something like this would work:

@posts = WpBlogPost.find(:all, :conditions => ['post_status = "publish"'], :limit => 10)

One other important thing should be considered. Be sure you configured your host db server to accept access from the outside if you want to try your controllers from your local development server. Also, you need the last MySQL installed as a gem (sudo gem install mysql).

Tags:

6 Comments »

History of Events with rails

Posted on   December 19th, 2009

Another common task in a Web app is to create a register of events. Sometimes you want to email your users about something that happened, others you just want to show somewhere a table with the timeline of recent events. For example, let’s take the example of a project managing application (like the well-known Basecamp of 37Signals).

Timeline Demo

This comes from the last app in which I worked. I reckon there are two ways to embrace this problem. One involves adding or using the rails default field updated_at of every table in the database. Of course some other handy fields might be added as well, such as updated_by and updating_description. Showing a register of events in this case means taking every model in the database, ordering it by the updating date and limiting the results to a given constant. All of those should be added by the controller to a hash or an array and would later on be globally ordered by the value of the update field, only to be delivered to the views and finally displayed. Nonetheless, this method could get stuff complex over time, specially when using a large set of models, and it gets events dependent to subjects.

Another approach is using a self-contained concept of event. Everything done in the system is recorded and added to a register, to which we can access whenever we please and display it. As usual, someone has had this problem before and created a useful plugin for us, in this case it’s called timeline_fu. I added though a very small modification to serve my purposes.

Read the rest of this entry »

Tags:

No Comments »

In place editing with rails

Posted on   December 9th, 2009

Update: This post is deprecated. Have a look at the last in-place-editing plugin I wrote in jQuery and its gem to work with Rails 3.

My last post addressed the need to get a decent WYSIWYG tool and/or a markup language so as to input formatted data to your web application. Today though this is just not enough. The concept in everyone’s mouth is Web 2.0 and, if something, this means giving users the full control to enrich their webs. That is, if there’s a header here or a description there she’d like to change, it should be done with little to no effort! Why not just clicking on it and typing the new text? This is the concept behind in-place-editing.

I’d say, hopefully not mistakenly, this feature was made popular by Flickr. There you can do and undo almost everything very much like a desktop application, and that is our aim as well.

Example of flickr in place editing

Example of flickr in place editing

There could be a lot of chin-wagging on in-place-editing, but I’ll focus on how to set up the native Rails plugin for this, which works with Prototype and Scriptaculous. If someone is interested in using jQuery and a good library for inline edit, I recommend Jeditable, it’s beautiful and simple to use. One day I’ll get my hands dirty talking about my opinion on Javascript frameworks and their pros and cons, but not today. Some people prefer to use the default Prototype and Scriptaculous libraries for Rails, since they are built-in along with a set of available helpers.

If you didn’t know, Rails was split in plugins from its version 2.0 on, the growing core functionalities made the whole thing too bulky to drive, so they wittingly decided to cut it back. In_place_editing is one of the plugins that separated from the core in the version 2.0. Now it can be found on github (here). Some vulnerabilities where discovered afterwards, but were fixed later on. Now I think it’s a nice piece of coding. Unfortunately, the README file feels a bit short to me.

First thing to do is to think what fields are going to be inline editable. I recommend all short descriptions and headers to be easily changeable by one click. Thus, usually in the Show views or sometimes also in Index views (for example in a task list) we’ll add this extra functionality. In most cases it will completely replace the Edit view, in some others, for instance those you want to use markup or wysiwyg (i.e. a mailer form) it won’t be advisable.

To install the plugin it must be downloaded or cloned from github and copied to the right location:  /vendor/plugins/in_place_editing/

Then, next thing to do is to load the default javacript files (that includes Prototype and Scriptaculous) in the header of your layout:

<%= javascript_include_tag :defaults %>

Then, since you already know what fields of your model are going to be inline-editable, you have to mark them in the controller as so. This is as simple as doing this:

class PostsController < ApplicationController
 in_place_edit_for :post, :info

where info is the field of Post that is going to be editable. At this point, only the views remain to be shaken:

<p id="activator">Edit</p>
<%= in_place_editor_field :post, :info, {}, :rows => 4, :external_control => "activator" %>

It is important to note that we are using the Scriptaculous Ajax.InPlaceEditor (read docs here).  Rows is the height of the text-area, if it’s 1 or not specified it will be an input. The external control, if specified, is a something in the page that will trigger the editing, this sometimes makes a better visual metaphor. For example, a small pencil or an edit label beside the text will do the trick. It must go before the in place editor field, or else won’t work.  The text automatically highlights when the mouse passes over it. As you will see in the Ajax.InPlaceEditor wiki you can change the color of highlighting and everything else. Another important thing to notice is that it requires an initial value or otherwise it won’t be shown. I recommend leaving a “click to edit” default text.

Remember to read the additional options of the plugin:

:rows::              Number of rows (more than 1 will use a TEXTAREA)
:cols::              Number of characters the text input should span (works for both INPUT and TEXTAREA)
:size::              Synonym for :cols when using a single line text input.
:cancel_text::       The text on the cancel link. (default: "cancel")
:save_text::         The text on the save link. (default: "ok")
:loading_text::      The text to display while the data is being loaded from the server (default: "Loading...")
:saving_text::       The text to display when submitting to the server (default: "Saving...")
:external_control::  The id of an external control used to enter edit mode.
:load_text_url::     URL where initial value of editor (content) is retrieved.
: options::           Pass through options to the AJAX call (see prototype's Ajax.Updater)
:with::              JavaScript snippet that should return what is to be sent
in the AJAX call, +form+ is an implicit parameter
:script::            Instructs the in-place editor to evaluate the remote JavaScript response (default: false)
:click_to_edit_text::The text shown during mouseover the editable text (default: "Click to edit")

And this is all really necessary to know about the helper. You can also skip the helper and create the InPlaceEditor straight away:

<p id="activator">activate</p>
<span id="post_info_1_in_place_editor"><p>click to edit</p></span><script type="text/javascript">
//<![CDATA[
new Ajax.InPlaceEditor('post_info_1_in_place_editor', '/posts/set_post_info/1', {callback:function(form) 
{ return Form.serialize(form) + '&authenticity_token=' + encodeURIComponent('A4YrXQUzO7siznNx30y1Fnzflfn/nq54VTd9PYTOzSw=') }, 
externalControl:'activator', rows:4})
//]]>
</script>

Simple, isn’t it?

Tags:

2 Comments »