Lightview for modal dialogs on Rails
Long-time followers of this blog will know that I’ve tried various solutions for modal dialogs in rails apps (namely, Prototype-Window, and Redbox).
Prototype-Window worked fine, but uses Prototype 1.5RC3, and I’m using rails 2 with Prototype 1.6. The guys behind Prototype-Window are working on a new open source library, Prototype-UI which uses Prototype 1.6 and Script.aculo.us 1.8, but it’s still just a release candidate at the moment. This is a really exciting development and it will be interesting to see how this project progresses.
Initially, I was a big fan of Redbox, but I uncovered a few problems as time went on (including some cross-browser issues), which meant that it lost favour. To be fair to Craig Ambrose, the developer of Redbox, he did mention to me that he wasn’t currently working on it, and that it was just built to fit the exact requirements he had at the time.
So, to get down to the topic of this blog post… my current modal dialog solution du jour is Lightview by Nick Stakenburg. It’s not free, but relatively cheap, even for commercial use (€49 for one domain – if you compare this cost to the amount of developer-time it would take to write something of equal quality, it’s really a no-brainer). You can download a free-trial so that you can try it out before buying. I can’t comment on the quality of the code because it’s obfuscated but the documentation on the website is great, and the product itself works well across all major browsers.
I thought I would share how i got Lightview working in a rails application, for showing ajax content.
1. Download Lightview from here. (I’m using version 2.2.9.2)
2. Make a lightview folder in your application’s public/javascripts folder, and copy the contents (i.e. the css, images and js folders) of the lightview 2.x folder into your new folder.
3. Create a new file, lightview_helper.rb, in your helpers folder, and add the following code into it.
module LightviewHelper
# A lightview is shown with js like below (example taken from the lightview site:
# (http://www.nickstakenburg.com/projects/lightview/)
# There are 2 levels of arguments:
# top-level 'parameters' (href, rel etc.)
# 2nd-level 'options' (autosize, ajax etc.)
# Lightview.show({
# href: '/ajax/',
# rel: 'ajax',
# title: 'Login',
# caption: 'Enter your username and password to login',
# options: {
# autosize: true,
# topclose: true,
# ajax: {
# method: 'get',
# onComplete: function(){ $('name').focus(); }
# }
# }
# });
# link to a light view.
# name: the text to display in the link
# options: url options for the content to show in the lightview
# html_options: html_options to pass to link_to
# lightview_params: parameters (top-level arguments) for lightview, that differ from or add to defaults
# lightview_options: options (2nd-level arguments) for lightview, that differ from or add to the defaults
def link_to_lightview( name, options = {}, html_options = {}, lightview_params = {}, lightview_options = {} )
# get hold of the js for showing the lightview
show_lightview_js = show_lightview( options, lightview_params, lightview_options )
# merge in the javascipt on the onclick event, keeping any other onclick code intact.
html_options.merge!({
:onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{show_lightview_js} return false;"
})
# return a link which points to #, but has the onclick event in the html options.
link_to( name, "#", html_options )
end
# returns javascript for showing a lightview.
# url_options : hash of options to pass to url_for
# parameters: parameters (top-level arguments) for lightview, that differ from or add to defaults
# options: options (2nd-level arguments) for lightview, that differ from or add to the defaults
def show_lightview( url_options, parameters, options = {} )
# get hold of the default options and merge in what is passed in to this method
lightview_options = get_default_lightview_options
lightview_options.merge!( options )
# get hold of the default parameters
lightview_params = get_default_lightview_params
# merge in any paramters passed in
lightview_params.merge!(parameters)
#merge in the href and options(from above)
lightview_params.merge!(
{
:href => "'#{escape_javascript(url_for(url_options))}'",
:options => options_for_javascript(lightview_options)
}
)
# return js to show the lightview
"
Lightview.show(
#{options_for_javascript(lightview_params)}
);
"
end
# returns js to hide a lightview
def close_lightview_js
"Lightview.hide();"
end
# appends javsascript to hide a lightview to the page
def close_lightview_rjs
page<<close_lightview_js
end
private
# a default set of parameters for showing a lightview
# (top-level arguments -see comments at top of file)
def get_default_lightview_params
{
:rel => "'ajax'",
}
end
# a default set of options for showing a lightview
# (2nd-level arguments -see comments at top of file)
def get_default_lightview_options
{
:autosize => true,
:ajax => options_for_javascript({ :evalScripts => true }) # this is so any js in the displayed page is available (e.g. for autocompleter)
}
end
end
4. Change the get_default_lightview_params and get_default_lightview_options methods to reflect what you want your defaults to be.
5. Add a method to the application helper:
def use_lightview
# Avoid multiple inclusions
@content_for_lightview_css = ""
@content_for_lightview_js = ""
content_for :lightview_css do
stylesheet_link_tag "/javascripts/lightview/css/lightview.css"
end
content_for :lightview_js do
javascript_include_tag "/javascripts/lightview/js/lightview.js"
end
end6. In the application layout (views/layouts/application.rhtml), yield the appropriate contents.
<head>
<!-- ... -->
<%= yield :lightview_css %>
<%= javascript_include_tag "prototype" %>
<%= javascript_include_tag "scriptaculous" %>
<%= javascript_include_tag "effects" %>
<%= javascript_include_tag "controls" %>
<%= yield :lightview_js %>
<!-- ... -->
</head>7. Now, just use the helper methods in your views. e.g.
<% use_lightview -%>
<%= link_to_lightview(
"link text",
{:controller=>'my_controller', :action=>'my_action', :id=>my_id}, #url options
{:class => "whatever"}, # html options
{:title => "'Add type'"}, # lightview params
{:autosize => false, :width => 100, :height => 200} # lightview options
)%> See the Lightview website for details of all the configuration options. Note that the configuration in the lightview.js file (in public/javascripts/lightview/js) is for all lightviews. If you need multiple versions of these settings, just make another lightview folder with a different name, and adapt the helper methods.
If you want to open two (or more) lightviews in succession (e.g. for a wizard), just link to another lightview from inside the first. The lightview will just resize and show the new content.