bernat farrero thinking

I enjoy building stuff that fits the needs of large groups of individuals. I specialized in doing so with limited resources, deep layers of uncertainty and great doses of ambition. I contribute to Internet startups that plan and rationalize their course of action.

 

I founded @itnig and @camaloon. While holding the helm of Camaloon I help other entrepreneurs at itnig to build products and services with strong technological and internet basis and deliberate vocation to differentiate.

In place editing with Javascript, jQuery and Rails 3

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

Installation

Simply copy and load the files from the folder the following JS files in your application (in the same order):

Add the following line to your onLoad block:

Installation of the Rails 3 Gem

Only add the folloging line to your application’s Gemfile and run bundle install:

You still need to load the onLoad block, jQuery.js and jquery.purr.js in your application, but you can use the generator to copy (and update when necessary) the best_in_place.js script:

Usage

First of all, you should write your controllers in a RESTful way and make sure they respond properly to a json request. Here you can see an example of how a standard update action should look like in a Rails app:

At the same time, if you want to perform server-side validations so that errors are displayed when users introduce invalid data, then the models should look something like this:

Now all defined messages will be shown to the user if he introduces invalid data and the field will remain unchanged. Notice that messages will be displayed in a jquery.purr pop up. Take time to style it the way you prefer.

Options / Parameters

Best in Place will be wrapped into an html object (div/span preferred) with the following attributes:

  • class = “best_in_place”
  • data-url = Object Path
  • data-object = Object name
  • data-attribute = Object field
  • data-type = “input”, “textarea”, “select”, “checkbox” (defaults to input)
  • data-collection = “[[key, 'value'], [key, 'value'],…]” (a structured JSON containing all possible values in the collection in case of the select or a couple of values in case of a checkbox ["falsevalue", "truevalue"].
  • data-activator = “#DOM-OBJECT”.Dom object that will convert the text into an input. In case it is not specified it will work by clicking on the text.

Beware: All attributes are assigned underscore strings.

If you are using the Rails 3 Gem, you only need to tag user in-place editable fields like this:

best_in_place object, field, OPTIONS

Params:
  • object (Mandatory): The Object parameter represents the object itself you are about to modify
  • field (Mandatory): The field (passed as symbol) is the attribute of the Object you are going to display/edit.
Options:
  • :type It can be only [:input, :textarea, :select, :checkbox] or if undefined it defaults to :input.
  • :collection: In case you are using the :select type then you must specify the collection of values it takes. In case you are using the :checkbox type you can specify the two values it can take, or otherwise they will default to Yes and No.
  • :path: URL to which the updating action will be sent. If not defined it defaults to the :object path.
  • :nil: The nil param defines the content displayed in case no value is defined for that field. It can be something like “click me to edit”. If not defined it will show “-“.
  • :activator: Is the DOM object that can activate the field. If not defined the user will making editable by clicking on it.
  • :sanitize: True by default. If set to false it will allow users to introduce html tags to the input/textarea.

Let’s  see now how to create user-editable fields by some examples. We’ll use a generic User show action, as I did in the Test Application.

Inputs

In regular HTML:

Using the Rails 3 gem:

Textarea

In regular HTML:

Using the Rails 3 gem:

Allowing html tags:

Select dropdowns

In regular HTML:

Using the Rails 3 gem:

Checkbox values

In regular HTML:

Using the Rails 3 gem:

Wrapping up

Best In Place will make easier your application interfaces by letting users modify in-line every content. At the same time, it will control user input and display errors. Let me know if there’s something you would do different or don’t hesitate to give me a pull request if you contribute anyhow.

 

30 Comments

  1. Jürgen Brüder

    4 years ago

    Hi!

    I really like your work here. I was using Jan’s solution but yours is a very great improvement.

    A feature I would love to see is adding relations to the :collection. Let’s say a @user can have a language. Then it would be great to just do something like:

    :select, :collection => Language.all %>

    It could show the name of the language but save the id.

    Any thoughts on that?

    Nevertheless, a great gem!

    Best
    Jay

  2. bernat

    4 years ago

    Thanks, but this feature is there already. The only thing is you should pass the collection doing [[id, value], [id, value], [id, value],…]

  3. Paul Strong

    4 years ago

    Have you tried running this on Rails 2? Should I just use REST in Place or do you think it would be easy enough for me to port your library to Rails 2?

    Thanks

  4. bernat

    4 years ago

    It should work in Rails 2 as well. The gem is just a helper though, you could create it yourself if you needed. The important thing is the script, and it isn’t framework-specific, all it needs is for you to have RESTful controllers, the way I explain in this tutorial.

  5. Brad

    4 years ago

    Thank you so much for posting this! This is exactly what I was looking for. Nice work.

  6. Pol

    4 years ago

    Buscant informació de Rails em trobo amb aquesta notícia i al cap d’una estona, coi si aquest de la foto de la dreta em sembla que l’he vist a algun lloc… ah vale és de la FIB. Felicitats pel blog, he trobat coses molt útils!

  7. Steve

    4 years ago

    Hi Bernat,

    This sounds like an nice implementation of inline editing. However, still being fairly new to rails, I’m getting the following error when I try and run my server after installing your gem. I can uninstall it and everything goes back to working fine. Its essentially a photo gallery with lots of javascript and other packages. I’m running Rails 3.0.5 on Ruby 1.9.2, with jquery 1.5.1. I’ll keep digging to see if I can figure it out, but thought I’d ping you to see if you had any ideas. Thanks for your contribution and efforts on this.

    Here’s the error:
    /opt/dev/rails/3/pappy/config/initializers/setup_jquery.rb:1:in `': You have a nil object when you didn’t expect it! (NoMethodError)

    My setup_jquery.rb has:
    Pappy::Application.config.action_view.javascript_expansions[:defaults] = ['jquery.min', 'rails']

    which works fine normally. I suspect there is a require missing somewhere, but I’m not sure where. Any ideas?

    Thanks,
    Steve

  8. Steve

    4 years ago

    A quick update that I got things working by removing my jquery initializer. I thought I needed it to replace prototype as the default javascript lib. I found and installed the ‘jquery-rails’ gem and that seems to have installed jquery the correct way. Thanks again for creating and sharing your code!

    – Steve

  9. Ynes

    4 years ago

    Can I have the id value from the “select” to replace a div?

  10. durr

    4 years ago

    if one wanted to override one of the callbacks, what is the “right” way? Presumably, i should be able to get a handle to the BestInPlaceEditorObject, and set the loadErrorCallback function appropriately, but what scope does that editor object exist?

  11. Luis

    4 years ago

    Man, this was a great tutorial. You rock!

    Is there any way to implement this in a “real” checkbox, rather than using “Yes, No” collection?

  12. Ryan

    4 years ago

    Being new at this stuff, I apologize, but can you clarify how to pass in relations to the :collection part. For example, I have a table of clients with name as an attribute. How would I pass in the list of client names that have been entered into the system so far? Great gem by the way!

  13. Alberto

    4 years ago

    I am trying to add the jquery datepicker to the input field and combine that with your awesome best in place. Any suggestions on how to do this would be appreciated.

  14. aqabawe

    3 years ago

    thanks man, awesome plugin.

    for those who want to use this on some other page like the INDEX page just add

    or whatever your model name to the partial that populates it.

  15. sharath

    3 years ago

    hello Bernat,
    thanks a lot for posting these kind of tutorials ,using these idea in the project has helped me lot and any newbie can also learn easily from u r tutorials
    you rock dude!!!!!!!!!!

  16. ejangi

    3 years ago

    This is awesome man. But, is there a way I can define an “oncomplete” callback? I need to do some post processing to the updated html…

  17. bernat

    3 years ago

    I appologize for not keeping the project updated, i’ve been craizingly busy lately. I recommend you to look at the Github README instead of the instructions above, I’ll keep there the latest version of the specifications. And also, I’ll devote some time eventually to look at the pull requests and issues people opened, thanks everyone for posting!

  18. Tommie Jones

    3 years ago

    I can get it working by embedding the html. But I cannot get the best_in_place method working. I keep getting a MoMethod error. Any suggestions? Also I want to trigger another javascript event after the value is entered and accepted. Any suggestions.

  19. hey dear

    3 years ago

    dude you done a great job. its so simple and having magical effects…

  20. imohan

    3 years ago

    I see the message when there is an error in the data due to validations but no message if the data is saved correctly
    I see these codes where I need to add the message any example or can i extend and put something in application.js exampple will be great if possible.

    loadSuccessCallback : function(data) {
    this.element.html(data[this.objectName]);
    // Binding back after being clicked
    $(this.activator).bind(‘click’, {editor: this}, this.clickHandler);
    },

    thank you
    -imohan

  21. jeromeyers

    3 years ago

    The edit in place demo doesn’t really work in Chrome for the multiline “User Description” text area. Before the first time you edit-in-place the label moves up and down as you move your mouse over the text, and then, after you leave the field and let it revert to simple text, and you move your mouse over the field, the content itself jumps up and down maddeningly, seeming to squirm away from all attempts to click it, like fish through castaways’ fingers.

  22. jeromeyers

    3 years ago

    To add to that, it is only on one of the examples that this is happening, it is an example with a … tag in it. This creates a break and it is when the mouse moves over this break that the squirming starts.

  23. Bryan Wang

    3 years ago

    Amazingly simple and elegant! Thank you for sharing it with the public

  24. Bryan Wang

    3 years ago

    Is there a plan to add advanced input types such as tinymce or markdown or textile?

  25. Dmitry

    3 years ago

    Please, say how to scroll red pen over editable field?

  26. Pol Miro

    3 years ago

    Felicitats pel railcast nano! quin crack estas fet :)

  27. habib

    3 years ago

    really thanx but look at this ……. irtci.com

  28. rpruiz

    3 years ago

    Felicidades Bernat. Muy útil. Tienes alguna idea de cómo poder implementar el uso de tabs para moverse entre campos a editar? He tratado usando :html_attrs => {:tabindex => 1} sin éxito. Ojalá puedas comentar algo al respecto.

    Gracias.

  29. jim

    3 years ago

    Hey I really like your gem. I just have one question: how can I get it to work with jQuery autocomplete? Thanks!

  30. Rashila Noushad

    2 years ago

    Hi,

    I like your gem. But when trying to test with rspec and capybara the bip_area test helper is simulating the filling procedure, but it is not updating the field. The field is not shown updated after entering the new value in the test. But the whole thing working very beautifully in the application.

    Can you please help in testing using rspec.

    Thanks.

LEAVE A COMMENT