Skip to content

giniedp/fancygrid

Repository files navigation

Fancygrid

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

Features

  • Ajax data fetch
  • Pagination
  • Simple search
  • Complex search with 20 different conditions
  • Column sorting
  • View state caching
  • ActiveRecord supported. MongoDB coming (some day).
  • Column values from attributes, methods, method chains or even custom blocks

Requirements

  • jQuery >= 1.4.2
  • jQuery-ui (required if column sorting is wanted)
  • Rails 3
  • Haml

Installation

In your gemfile

gem 'fancygrid'

Run

bundle install

If you use Rails3 with asset pipeline enabled, you can just require the javascript and css

// = require fancygrid

If your asset pipeline is disabled, you have to copy the assets from the gems lib directory. There is no generator for this task.

Getting started

Basic Setup

In any controller in any action you can define a fancygrid for a specific model. Here is an example for a simple table for the Users model:

  # UsersController
  def index

    fancygrid_for :users do |g|
      # specify attributes to display
      g.attributes :id, :username, :email
      # specify the callback url for ajax loading
      g.ajax_url = users_path
      # finally call find to query the data
      g.find
    end
  end

To render the fancygrid in the view, use the same name that you passed in the setup

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

Changing default comparison operator

The default SQL string comparison operator is LIKE '%term%'. You can update this to Postgresql's case-insensitive ILIKE as seen below:

  def index
    fancygrid_for :users do |g|
      g.search_operator = :insensitive_like
      g.attributes :id, :email, :created_at
      # ...
    end
  end

The LIKE options include :like (the default), :starts_with, :ends_with, :insensitive_like, :insensitive_starts_with, and :insensitive_ends_with.

Static tables

If you dont want to have an ajax table, dont specify the ajax_url. The data will be queried and the table will be rendered without pagination.

  def index
    fancygrid_for :users do |g|
      # ...
      g.attributes :id, :username, :email
      # don't set the ajax_url and just call find
      g.find
    end
  end

Table names and model names

Usually fancygrid tries to resolve the models class and table name from given name. If you happen to use namespaced models, you must pass the class as an option.

  def index
    fancygrid_for :user, :class => Namespace::User do |g|
      # ...
    end
  end

Optionally you can also pass a specific table name. However, if the class responds to #table_name, this is not necessary.

  def index
    fancygrid_for :user, :class => Namespace::User, :table_name => "users" do |g|
      # ...
    end
  end

Add class and id values to table and tr and td table elements

To add a class or id to either the table, TR, or TD elements you add it to the fancygrid_for block like this:

  def index
    fancygrid_for :user do |g|
    #...
    g.tr_id do |record|
      "row-#{record.id}"
    end
    # or
    g.tr_id = :row_id
    # same with tr_class, td_id and td_class
    g.table_class = "user_table"
    #same with table_id
  end

Define columns

To display attributes as columns use the #attributes method for setup like this:

  def index
    fancygrid_for :users do |g|
      # ...
      g.attributes :id, :email, :created_at
      # ...
    end
  end

For everything else use the #columns method. You can have method names, method chains and procs to resolve column values.

  def index
    fancygrid_for :users do |g|
      # ...
      # methods
      g.columns :full_name, :some_other_method
      # method chains
      g.columns "orders.count"
      # procs
      g.columns :roles do |record|
        record.roles.map(&:name).join(", ")
      end
      # ...
    end
  end

For more complex output you have to format the cell value in the view or a formatter method.

Columns formatting

Add a block to the fancygrid call in the view. In there you can use a switch condition on the columns name to determine what to render. Do not forget to add the else case to render all unformatted values.

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

belongs_to or has_one associations

To define columns for associations, use the #columns_for method.

  def index
    fancygrid_for :users do |g|
      # ...
      g.columns_for :contact do |contact|
        contact.attributes :first_name, :last_name
      end
      # ...
      g.find do |query|
        # eager loading of the association
        query.select :contact_id
        query.includes :contact
      end
    end
  end

Mention that in the query block the contact_id is selected. This is required, since fancygrid tries to use optimized sql queries by default. Therefore it will select only those attributes that have been used during the fancygrid setup. If the contact_id is missing, it will not be possible to include the contact association.

If your association name is different from the models name, pass the model class as option.

  def index
    fancygrid_for :users do |g|
      # ...
      g.columns_for :invoice_address, :class => Address do |adr|
        adr.attributes :street, :zipcode, :city
      end
      # ...
    end
  end

has_many or has_and_belongs_to_many associations

If you have Users that has_many Orders, you should rather define a fancygrid for the Orders than for Users. However, if it must be a Users table and you want to search on the associations attributes, you can do that:

  def index
    fancygrid_for :users do |g|
      # ...
      g.columns_for :roles do |roles|
        roles.attributes :name
      end
      # ...
    end
  end

The definition is valid, and you can already search for users with a specific role. But nothing is going to be rendered in the roles.name column. This is because roles is a collection of records, and not a single record. You can now format the column in the view like this

  = fancygrid :users do |column, record, value|
      - case column.identifier
      - when "roles.name"
        = record.roles.map(&:name).join("|")
      - else
        = value

Here the column identifier is used to identify the column. This is useful if you have more columns that are named the same.

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.

  def index
    fancygrid_for :users, :persist => true do |grid|
      # ...
    end
  end

Copyright

Copyright (c) 2010 Alexander Graefenstein. See LICENSE for details.