Ruby on Rails is currently the hottest, and most hyped web development
framework. The Rails framework includes tight integration with the
Prototype and Scriptaculous JavaScript libraries.
Using Ext Grid with Ruby on Rails
Ruby on Rails is currently the hottest, and most hyped web development
framework. The Rails framework includes tight integration with the
Prototype and Scriptaculous JavaScript libraries. With the addition of
Prototype and Scriptaculous as a backend choice for the Ext JavaScript
library, the integration of Ext with Rails has become much easier to
accomplish, with less overall included code. This tutorial will show
you step-by-step all the steps that are necessary to use the Ext Grid
component within a Rails web application. The tutorial uses JSON as the
mechanism to send the grid data to the browser. Remote sorting of grid
data is also included in the tutorial. The tutorial assumes basic
familiarity with both Ext and Rails.
Let's Get Started
We will create a web page that displays a list of movies in a grid
view. The movie grid that we create will be implemented using the Ext
Grid component. The first thing we will do is create an application
layout template that includes the necessary Ext JavaScript files. In
our application.rhtml template file, we include the ext-all.css
stylesheet, and the prototype.js, scriptaculous.js, effects.js,
ext-prototype-adapter.js, and ext-all-debug.js JavaScript files. The
home directory for all Ext files is assumed to be a subfolder named
'ext' in the public/javascripts directory of your Rails project. We use
the appropriate Rails Helpers to include each of these files. The yield
statement is where our main page content will be inserted.
Application Layout (application.rhtml)
<html>
<head>
<title>Movie Manager</title>
<%= stylesheet_link_tag "../javascripts/ext/resources/css/ext-all.css" %>
<%= javascript_include_tag "ext/adapter/prototype/prototype.js" %>
<%= javascript_include_tag "ext/adapter/prototype/scriptaculous.js" %>
<%= javascript_include_tag "ext/adapter/prototype/effects.js" %>
<%= javascript_include_tag "ext/adapter/prototype/ext-prototype-adapter.js" %>
<%= javascript_include_tag "ext/ext-all-debug.js" %>
</head>
<body>
<%= yield %>
</body>
</html>
After we have our application template created, our next step is to
create the view template for our movie list screen. In the view
template, list.rhtml, we create a div element to hold the Ext Grid that
will be rendered with JavaScript. We also include a JavaScript file
called grid-paging.js. We will put our grid related JavaScript code
into the grid-paging.js file. We give the grid div an id of
'movies_grid'. This id will be referenced later in our JavaScript code
that we will use to initialize the grid with.
View Template (list.rhtml)
<%= javascript_include_tag "grid-paging.js" %>
<div id="movies_grid" style="border:5px solid #99bbe8; overflow:hidden; width:650px;"></div>
Setting Up the Grid - JavaScript
Next, we need some JavaScript code to initialize and render the Ext
Grid. We create a file called grid-paging.js which will hold the
JavaScript code that we write to initialize our webpage. By using the
Ext.onReady function, our grid is initialized at page load time. In our
initialization code, we first create an Ext.data.Store object to serve
as a client-side data store for our movie data. In the Store object, we
create an Ext.data.HttpProxy that holds the url the grid will access to
get its data. We also specify a JsonReader as the mechanism for reading
the data returned by the backend. We pass the schema of our JSON data
to the JsonReader as we create it. Finally, by setting remoteSort to
true, we are telling the Store that sorting of the data will be done on
the server and each time a column header is clicked, a new data request
should be made to the server.
After the Store object is created, we next create an
Ext.grid.ColumnModel object to specify the layout of our grid columns.
After specifying the layout of our columns, we set the defaultSortable
property of the ColumnModel to true. This makes the columns sortable by
clicking on the column headers.
Finally, we create the Ext.grid.Grid object, passing in the Store
and ColumnModel objects created previously as initialization
parameters. We also specify a single selection RowSelectionModel for
our grid. We then render the grid, and call the load method on our
Store object to perform the initial data load.
JavaScript Code (grid-paging.js)
var grid;
var ds;
Ext.onReady(function(){
init_grid();
});
function init_grid() {
ds = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({url: '/movie/grid_data'}),
reader: new Ext.data.JsonReader({
root: 'Movies',
totalProperty: 'Total',
id: 'id'
}, [
{name: 'title', mapping: 'title'},
{name: 'plot', mapping: 'plot'},
{name: 'release_year', mapping: 'date'},
{name: 'genre', mapping: 'genre'},
{name: 'mpaa', mapping: 'mpaa'},
{name: 'directed_by', mapping: 'directed_by'}
]),
// turn on remote sorting
remoteSort: true
});
var cm = new Ext.grid.ColumnModel
([{
id: 'title',
header: "Title",
dataIndex: 'title',
width: 250
},{
header: "Release Year",
dataIndex: 'release_year',
width: 75
},{
header: "MPAA Rating",
dataIndex: 'mpaa',
width: 75
},{
header: "Genre",
dataIndex: 'genre',
width: 100
},{
header: "Director",
dataIndex: 'directed_by',
width: 150
}]);
cm.defaultSortable = true;
grid = new Ext.grid.Grid('movies_grid', {
ds: ds,
cm: cm,
selModel: new Ext.grid.RowSelectionModel({singleSelect:true}),
autoExpandColumn: 'title'
});
grid.render();
ds.load({params:{start:0, limit:20}});
}
Implementing the Server Side
Now we have the front-end code in place, but we have no backend
code yet to actually get the movie data and perform the sorting. For
the backend code, we now create a Rails controller class,
MovieController. Remember, when we wrote the grid's initialization
JavaScript, we used a url of '/movie/grid_data' as the data source. In
a Rails application, this will route to a grid_data method of the
MovieController class. The MovieController class uses a model class
called Movie. A migration file for creating the database schema to
support this model is included with the downloadable files for this
tutorial. The MovieController class contains two methods, list, and
grid_data. The list method is called when the page is requested. This
method is empty which causes Rails to automatically render a template
named list.rhtml, which we showed earlier in this tutorial.
The real work on the server side is done in the grid_data method.
This method is called via an AJAX call from the Ext Grid component when
it is initialized, when the data is re-sorted, or when a new page of
the data is selected. The data is rendered as JSON text with no layout
applied to it. JSON data is what is expected on the browser side by the
Ext Grid component that we setup earlier in the tutorial.
When a column header is clicked on the grid, another AJAX request
is made to the grid_data method to retrieve the data in the specified
sort order. The Ext Grid component automatically passes the following
four parameters: start, limit, sort, and dir. The 'start' parameter
specifies the row index to begin at, the 'limit' parameter specifies
the number of rows to retrieve, the 'sort' parameter specifies the
column being sorted by, and the 'dir' parameter specifies the direction
of the sort, equal to either 'ASC' for ascending sort, or 'DESC' for
descending sort. In our implementation, we use the Rails Paginator
object to implement the paging behaviour. After retrieving the data
from the database, a Hash called return_data is created with two
elements, :Total, and :Movies. These are the two elements that the Ext
Grid is expecting to find in the returned JSON hash. The :Total element
specifies the total number of records available, and the :Movies
element contains an array of all the movie data.
Rails Controller Code (movie_controller.rb)
class MovieController < ApplicationController
def list
end
# Called from the list page to get the movie list data to populate the grid.
def grid_data
start = (params[:start] || 1).to_i
size = (params[:limit] || 20).to_i
sort_col = (params[:sort] || 'id')
sort_dir = (params[:dir] || 'ASC')
page = ((start/size).to_i)+1
@movie_pages = Paginator.new(self, Movie.count, size, page)
@movies = Movie.find(:all,
:select => "id, title, plot, date, genre, mpaa, directed_by",
:limit=>@movie_pages.items_per_page,
:offset=>@movie_pages.current.offset,
:order=>sort_col+' '+sort_dir)
return_data = Hash.new()
return_data[:Total] = @movie_pages.item_count
return_data[:Movies] = @movies.collect{|u| {:id=>u.id,
:title=>u.title,
:plot=>u.plot,
:date=>u.date,
:genre=>u.genre,
:mpaa=>u.mpaa,
:directed_by=>u.directed_by} }
render :text=>return_data.to_json, :layout=>false
end
end
|