Fancygrid

Fancygrid mades it easy to create and render tables for database records in rails.

Features

  • ActiveRecord and ActiveResource are supported

  • Ajax data fetch

  • Pagination

  • Simple search with LIKE condition

  • Complex search with 17 different conditions

  • Frontend column sorting

  • View state caching

  • May be rendered anywhere, not restricted to the index action

  • Column values may be attributes, methods or even custom blocks

  • Custom templates for column formatting

Requirements

  • jQuery >= 1.4.2

  • jQuery-ui (required if column sorting is wanted)

  • Rails 3

  • Haml

Installation

In your gemfile

gem 'fancygrid', :git => 'git://github.com/giniedp/fancygrid.git'

or for specific tag

gem 'fancygrid', :git => 'git://github.com/giniedp/fancygrid.git', :tag => "1.0.0-pre"

Run

bundle install

and

rails g fancygrid:install

then follow the instructions

Getting started

Basic Setup

In any controller in any action you can define a fancygrid for a specific model. A controller is the place where you define what data should be queried from the database and what columns will be visible. For example you could define a table for your users like this:

Example

# UsersController
def index
  # setup fancygrid to display users
  fancygrid_for :users do |user|

    # specify attributes to display  
    user.attributes( :id, :username, :email )

    # specify the callback url for ajax loading
    user.url = users_path

    # finally call find with some customized find options
    user.find( :order => "users.created_at DESC" )

  end
end

In your View you have to render the fancygrid. Use the name that you have used in your controller

Example

# app/views/users/index.html.haml
= fancygrid :users

Static tables

If you dont want to have an ajax table, you can set the data directly without providing a callback url.

Example

def index
  fancygrid_for :users do |user|

    # ...

    # dont set the url and find options like in the first example
    # instead set the data directly
    user.data= User.find(:all)

  end
end

Table names and model names

Usually fancygrid takes the passed name and tries to resolve the models class and its database table name. If you need to use a name that is different from your models name which is the case when you have namespaced models, you can pass the models constant and its table name to fancygrid

Example

def index
  fancygrid_for :user, Namespace::User, "users" do |user|

    # ...

  end
end

Using methods on records

You are not limited to the models attributes to display in the fancygrid. You can provide method names to display a models properties

Example

def index  
  fancygrid_for :users do |user|

    # ...

    # specify methods to call on each record
    user.methods(:full_name, :some_other_method)

    # ...

  end
end

You can even pass a proc to a single column

Example

def index  
  fancygrid_for :users do |user|

    # ...

    user.proc( :roles ) do |record|
      record.roles.map{ |r| r.name }.join(", ")
    end

    # ...

  end
end

For more complex output you must have to render a column with custom template

Rendering columns with templates

For custom cell rendering create a template at some place like app/views/fancygrid/users.html.haml In your fancygrid definition do:

Example

def index  
  fancygrid_for :users do |user|

    # ...

    # specify cells that will be rendered with custom code
    user.rendered(:actions)

    # set the templates name 
    user.template = "fancygrid/users"

    # ...

  end
end

In your template you can use the following locals: table, column, record and value so you can render your cell like this:

Example

- case table.name
- when :users
  - case column.name
  - when :actions
    = link_to "Show", user_path(record)
    = link_to "Edit", edit_user_path(record)

Rendering columns with a haml block

In your view you can give a grid a block that should be used for rendering

Example

= fancygrid(:users) do |column, record, value|
  - case column.name
  - when :actions
    = link_to "Show", user_path(record)
    = link_to "Edit", edit_user_path(record)

Display associated data (belongs_to or has_one)

To display an associated data you have to build the nodes for that data and specify the include option for the find method

Example

def index  
  fancygrid_for :users do |user|

    # ...

    user.columns_for(:contact) do |contact|
      contact.attributes( :first_name, :last_name )
    end

    # ...

    user.find( :include => :contact )

  end
end

If your association name is different from the models name pass the model constant into the columns_for method.

Example

def index  
  fancygrid_for :users do |user|

    # ...

    user.columns_for(:work_address, Address) do |address|
      address.attributes( :street, :zipcode, :city )
    end

    # ...

    user.find( :include => :work_address )

  end
end

Display associated data (has_many or has_and_belongs_to_many)

Similar to the previous example you have to build the nodes for the associations and specify the include option

Example

def index  
  fancygrid_for :users do |user|

    # ...

    user.columns_for(:roles) do |roles|
      roles.attributes( :name )
    end

    # ...

    user.find( :unclude => :roles )

  end
end

However this would not work as expected. Its the same as calling user.roles.name which would call name on a collection of roles.

Instead you should rather define a proc to return all role names or render that cell with custom code.

Example

def index  
  fancygrid_for :users do |user|

    # ...

    user.proc( :roles ) do |record|
      record.roles.map{ |r| r.name }.join(", ")
    end

    # ...

  end
end

Order your conditions

If you have a large condition to make or you want to use a condition that depends on a users role, for example when some of your users are not allowed to see specific data, then you can give the find method a block and use the query generator

Example

fancygrid_for :users do |grid|

  # ...

  grid.find do |query|
    # all the conditions are joined with an AND operator
    query.conditions(["users.first_name = ?", "some name"])
    query.conditions(["users.last_name = ?", "some name"])

    # use other finder options as methods
    query.select #...
    query.order  #...
    # ...
  end
end

Caching the view state

To make your users life easier you can enable the view state caching. This way the user can search for data, leave the site, come back and have his last search back on screen. Here is an example of how to store the view in the users session:

Example

# ensure that there is a hash in the session
session[:users_table_view_state] ||= {}

fancygrid_for :users do |grid|

  # ...

  # specify a proc to load the view state
  grid.load_view_proc do |grid_instance|
    session["fancygrid_users"] || {}
  end

  # specify a proc to store the view state
  grid.store_view_proc do |grid_instance, dump|
    session["fancygrid_users"] = dump
  end

end

Its up to your business logic where and how you store the dumped view state. If you have lots of tables i would recommend to enable database session store.

Copyright

Copyright © 2010 Alexander Graefenstein. See LICENSE for details.