{"id":15288827,"url":"https://github.com/odedd/table_sortable","last_synced_at":"2025-04-13T08:11:26.691Z","repository":{"id":59157136,"uuid":"92740779","full_name":"odedd/table_sortable","owner":"odedd","description":"Use jquery-tablesorter.js in your Rails project with server side filtering, pagination and sorting.","archived":false,"fork":false,"pushed_at":"2017-10-24T05:07:06.000Z","size":107,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-08T19:52:27.016Z","etag":null,"topics":["filtering","gem","ruby","ruby-on-rails","server-side","sorting","tablesorter"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/odedd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-29T13:06:19.000Z","updated_at":"2024-06-22T08:52:33.000Z","dependencies_parsed_at":"2022-09-13T20:03:27.176Z","dependency_job_id":null,"html_url":"https://github.com/odedd/table_sortable","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/odedd%2Ftable_sortable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/odedd%2Ftable_sortable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/odedd%2Ftable_sortable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/odedd%2Ftable_sortable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/odedd","download_url":"https://codeload.github.com/odedd/table_sortable/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248681491,"owners_count":21144700,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["filtering","gem","ruby","ruby-on-rails","server-side","sorting","tablesorter"],"created_at":"2024-09-30T15:53:19.701Z","updated_at":"2025-04-13T08:11:26.664Z","avatar_url":"https://github.com/odedd.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TableSortable\n\n[![Build Status](https://travis-ci.org/odedd/table_sortable.svg?branch=master)](https://travis-ci.org/odedd/table_sortable)\n\nTableSortable adds multi-column, **server-side** filtering, sorting and pagination \nto the **tableSorter jQuery plugin**, so you don't have to worry about interpreting the query parameters,\ncombining multiple queries, columns to sort by, or figuring out how to send the correct page back to the client.\n\nIt is a Rails backend complementation to the frontend tableSorter.js.\n\n### The Problem\nThe jQuery tableSorter plugin is an excellent tool for filtering and sorting tables. \nOften, when dealing with lots of rows, we may want to split the table into multiple pages. tableSorter.js has a nifty [widget](https://mottie.github.io/tablesorter/docs/example-pager-ajax.html) for that, which requires using [mottie's fork](https://mottie.github.io/tablesorter/docs/index.html) of tableSorter.\n\nUsually this is a scenario where we don't want to send our entire set of records to the frontend,\nwhich consequently means that the frontend no longer knows the entire set of records to filter and sort through,\nwhich eventually requires our *server* to handle all that stuff as well as the pagination of the results.\n\n### The Solution: TableSortable\nTableSortable will handle all the backend filtering, sorting and pagination for you.\n\nNOTICE: This gem is in very early stages of development, and is not yet fully documented.  Any input will be more than welcome.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'table_sortable'\n```\n\nThen run `bundle install` and you're ready to start\n\nYou should also probably be using jquery-tablesorter.  \nFor information regarding integration or tableSorter.js into your Rails project, \nplease see the [jQuery tablesorter plugin for Rails](https://github.com/themilkman/jquery-tablesorter-rails) page.\n\n## Usage\n\nFirst, we need to setup our controller. For this example this will be a users controller.  \nLet's `include TableSortable::Controller` so that we can use its methods.\n\n```ruby\n#controllers/users_controller.rb\nclass UsersController \u003c ApplicationController\n  include TableSortable::Controller\n```\n\nNext, let's define our columns.\n```ruby\n#controllers/users_controller.rb\nclass UsersController \u003c ApplicationController\n  include TableSortable::Controller\n  \n  define_colunns :first_name, :last_name, :email, :created_at\n```\nThat's just the basic setup of columns. For more configuration options, please see [advanced configuration](#advanced-configuration).\n\nNow we need to make sure our `index` action filters, sorts and paginates the records.  \nWe can do that using the `filter_and_sort` method.\n```ruby\n#controllers/users_controller.rb\ndef index\n  \n  @users = filter_and_sort(User.all)\n  \n  respond_to do |format|\n    format.html {}\n    format.json {render layout: false}\n  end\nend\n```\n\nLet's write the `index` view. We can use TableSortable's [view helpers](#view-helpers) to render our table.\n```erb\n\u003c!-- views/users/index.html.erb --\u003e\n\u003cdiv id=\"usersPager\"\u003e\n    \u003c%= table_sortable_pager %\u003e\n\u003c/div\u003e\n\n\u003ctable id=\"usersTable\" data-query=\"\u003c%= users_path %\u003e\"\u003e\n    \u003cthead\u003e\n        \u003ctr\u003e\n            \u003c%= table_sortable_headers %\u003e\n        \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody\u003e\n    \u003c/tbody\u003e\n\u003c/table\u003e\n```\nNotice how `index.html` doesn't render the actual rows. They will later be polled via ajax by tableSorter.js.\n\nLet's create `index.json.jbuilder` to send the users' info back to the frontend.\n```ruby\n# views/users/index.json.jbuilder\n# Since we only send out the current page of users, \n# we must also send the total number of records.\n# We'll use TableSortable's total_count method for that.\njson.total @users.total_count\njson.rows @users.map{|user| render partial: 'user_row.html.erb', locals: {user: user}}.join\njson.pager_output 'Users {startRow} to {endRow} of {totalRows}'\n```\nWe should also create the _user_row.html partial. In it, we may also use TableSortable's [helpers](#view-helpers).\n```erb\n\u003c!-- views/users/_user_row.html.erb --\u003e\n\u003ctr\u003e\n    \u003c%= table_sortable_columns user %\u003e\n\u003c/tr\u003e\n```\n\nNow that we are done configuring the backend - let's continue to frontend. Here's a simple tableSorter.js configuration example:\n```javascript\nvar table = $('#usersTable');\ntable.tablesorter({\n    widgets: ['filter', 'pager'],\n    widgetOptions: {\n        // show 10 records at a time\n        pager_size: 10,\n        // Poll our users_index_path, which we receive from the table's data-query attribute.\n        pager_ajaxUrl: table.data('query') + '?pagesize={size}\u0026page={page}\u0026{filterList:fcol}\u0026{sortList:scol}',\n        // Parse the incoming result\n        pager_ajaxProcessing: function (data) {\n            if (data \u0026\u0026 data.hasOwnProperty('rows')) {\n                // Update the pager output\n                this.pager_output = data.pager_output;\n                // return total records, the rows HTML data,\n                // and the headers information.\n                return [data.total, $(data.rows)];\n            }\n        }\n    }\n});\n```\nThat's it! The results fetched from the server are now filtered, sorted and paginated by TableSortable.\n\nFor full documentation of the usage of tableSorter.js please go [here](https://mottie.github.io/tablesorter/docs/index.html) for the very popular fork by mottie, or [here](http://tablesorter.com/docs/) for the original version of the plugin.\n\nOf course there are many more configuration options that make TableSortable flexible and adaptable. For those, please see [advanced configuration](#advanced-configuration).\n\n## Advanced Configuration\n\nTableSortable has several advanced configuration settings, which allow for a more fine-grained control over its behaviour.\n\n- [define_column](#define_column)\n- [define_column_order](#define_column_order)\n- [define_column_offset](#define_column_offset)\n\n#### define_column  \n\nTableSortable lets you define the columns one by one with many custom attributes, using the `define_column` method.\n```ruby\n#controllers/users_controller.rb\nclass UsersController \u003c ApplicationController\n  include TableSortable::Controller\n  \n  define_column :full_name, \n                 value: -\u003e (user) {\"#{user.first_name} #{user.last_name}\"}\n  define_column :email\nend\n```\n##### Syntax: `define_column column_name, [arguments]`  \n\n- `column_name` \u003csub\u003e(required)\u003c/sub\u003e  \n    A symbol representing the column_name. Can be anything, but should usually be the same as the column name. \n- `arguments` \u003csub\u003e(optional)\u003c/sub\u003e  \n    - `value: (symbol|proc)`  \n        \u003csub\u003edefault: same as column name\u003c/sub\u003e   \n        Accepts either a symbol representing a method that the record responds to, like an ActiveRecord attribute, \n        or a proc the returns the record's value. for example:\n        ```ruby\n        define_column :full_name, \n                      value: -\u003e (user) {\"#{user.first_name} #{user.last_name}\"}\n        ```\n    - `content: (symbol|proc)`  \n        \u003csub\u003edefault: same as value\u003c/sub\u003e   \n        Works the same way as `value`, but allows specifying a different value to be displayed in the table cells.\n        The difference between `value` and `content` is that the former determines the value by which the scope will be filtered and sorted,\n        while the latter only affects the displayed cell contents.\n        ```ruby\n        define_column :full_name, \n                      value:   -\u003e (user) {\"#{user.first_name} #{user.last_name}\"},\n                      content: -\u003e (user) {\"#{user.last_name}, #{user.first_name}\"}\n        ```\n    - `label: (string)`  \n        \u003csub\u003edefault: 'Titleized' version of the column name. Supports I18n internationalization. see [translateion_key](#translation-key).\u003c/sub\u003e   \n        Allows specifying a string to be used as the column label, displayed at the table header.\n    - `placeholder: (string|false)`  \n        \u003csub\u003edefault: same as label\u003c/sub\u003e  \n        Allows specifying a string to be used as a placeholder for the column's filter input field.\n        You may also specify `false`, in which case no placeholder will be displayed.\n    - `filter: (proc|false)`  \n        \u003csub\u003edefault: free case-insensitive text search on the column's value\u003c/sub\u003e   \n        By default, the column will be filtered according to the column's value. However, you may specify a proc to perform the search on that column. \n        The proc itself can contain either ActiveRecord operations (eg. `where`) or array operations (eg. `select`), \n        and will be passed the current filter query when run.\n        ```ruby\n        define_column :full_name, \n                      filter: -\u003e(query) {where('LOWER(CONCAT(first_name,\" \", last_name)) LIKE (?)', \"%#{query.downcase}%\")}\n        ```\n        If no filtering is to be performed on that column you can set it to `false`. When using TableSortable's [view helpers](#view-helpers),\n        this also means that no filter input will be shown on that column.\n    - `filter_method: (:array|:active_record)`  \n        \u003csub\u003edefault: automatically detects method based on the record given\u003c/sub\u003e   \n        Determines whether the default filter function relies on an ActiveRecord `where` method or an Array `select` method.  \n        When no method and no filters are supplied, TableSortable will check if the column_name corresponds to a database column of the record. \n        If it does, it will use an ActiveRecord `where` method, otherwise it will perform an array `select` operation.  \n        _Only applies when no `filter` option has been specified._\n        - `:array` \u003csub\u003e(default)\u003c/sub\u003e  \n            Filter using the `select` method. While being slower, it selects based on the column's value, whatever it might be, and so fits every scenario.\n        - `:active_record`  \n            Filter using the `where` method. While being faster, it only applies to cases where the column name matches the database column name. \n    - `filter_initial_value: (string)`\n        \u003csub\u003edefault: nil\u003c/sub\u003e   \n        You may specify an initial filter query for each column.\n    - `sort: (proc|false)`  \n        \u003csub\u003edefault: sorting based on the column's value\u003c/sub\u003e   \n        By default, the record set will be sorted according to the selected column's value. \n        However, you may specify a proc to perform the sorting when this column is selected as the sort base. \n        The proc itself can contain either ActiveRecord operations (eg. `order`) or array operations (eg. `sort`), \n        and will be passed the current sort order as a symbol (`:asc` or `:desc`) when run.\n        ```ruby\n        define_column :full_name, \n                      sort: -\u003e (sort_order) { sort{ |a,b| (sort_order == :asc ? a : b) \u003c=\u003e (sort_order == :asc ? b : a) } }\n        ```\n        If no sorting is to be performed based on that column you can set it to `false`. When using TableSortable's [view helpers](#view-helpers),\n        this also means that no sorting handle will be shown on the client side on that column as well.\n    - `sort_method: (:array|:active_record)`  \n        \u003csub\u003edefault: :array\u003c/sub\u003e   \n        Determines whether the default sort function relies on an ActiveRecord `order` method or an Array `sort` method.  \n        When no method and no filters are supplied, TableSortable will check if the column_name corresponds to a database column of the record. \n        If it does, it will use an ActiveRecord `order` method, otherwise it will perform an array `sort` operation.  \n        _Only applies when no `sort` option has been specified._\n        - `:array` \u003csub\u003e(default)\u003c/sub\u003e  \n            Sort using the `sort` method. While being slower, it sorts based on the column's value, whatever it might be, and so fits every scenario.\n        - `:active_record`  \n            Sort using the `order` method. While being faster, it only applies to cases where the column name matches the database column name. \n    - `template: (string)`  \n        \u003csub\u003edefault: same as column_name\u003c/sub\u003e  \n        Allows setting a custom template name to look for when rendering the header or column contents. For more information see [view helpers](#view-helpers).\n\n##### Dynamic Column Definitions\n\nIf the column definitions themselves need to be dynamic (eg. if you're using \ndynamic fields in your model) you can also use `define column` in a `before action` callback like this\n```ruby\n#controllers/users_controller.rb\n  \n  before_action :define_columns\n\n  private\n  \n  def define_columns\n    @user.custom_fields.each do |cf|\n      define_column cf, \n                    value: -\u003e (user) {user.get_custom_field(cf)}\n    end\n  end\n```\n\n#### define_column_order  \nColumns are displayed in the order they are defined using `define_method`. If you wish, you may override this order by specifying your own using the `define_column_order` method.  \nIt also allows hiding columns that you need to include in the filtering and sorting process, eg. when you want to have an external search box filtering based on a column that you don't necessarily want to display.\n##### Syntax: `define_column_order column1, [column2], [column3] ...`  \n```ruby\n#controllers/users_controller.rb\n#define a full_name column, allowing to create a custom search box that operates on the 4th column\ndefine_columns :first_name, :last_name, :email, :full_name\ndefine_column_order :last_name, :first_name, :email\n```\n#### define_column_offset  \nSometimes you wish to manually add columns in the views before adding the defined TableSortable columns, eg. to have a checkbox next to each row.  \n```erb\n\u003c!-- views/users/index.html.erb --\u003e\n\u003ctable id=\"usersTable\" data-query=\"\u003c%= users_path %\u003e\"\u003e\n    \u003cthead\u003e\n        \u003ctr\u003e\u003c/tr\u003e   \u003c!-- EXTRA COLUMN --\u003e\n        \u003ctr\u003e\n            \u003c%= table_sortable_headers %\u003e\n        \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody\u003e\n    \u003c/tbody\u003e\n\u003c/table\u003e\n\n\u003c!-- views/users/_user_row.html.erb --\u003e\n\u003ctr\u003e \u003c%= check_box_tag :checked %\u003e \u003c/tr\u003e   \u003c!-- EXTRA COLUMN --\u003e\n\u003ctr\u003e\n    \u003c%= table_sortable_columns user %\u003e\n\u003c/tr\u003e\n```\nSince the column numbers that appear in tableSorter.js' ajax request will represent their actual number, you should let TableSortable know that the columns you defined are offset.  \nSo, in the above case you should define an offset of 1.\n\n##### Syntax: `define_column_offset offset`  \n```ruby\ndefine_column_offset 1\n```\nYou may define it either using the `define_column_offset` method shown above, \nor using the all inclusive `define_columns` method, as an `offset: \u003cinteger\u003e` argument.\n```ruby\ndefine_column :first_name, :last_name, :email, offset: 1\n```\n\n### View Helpers\nTableSortable offers several view helpers, to help maintain the connection between the view layer and the controller layer, and make your code DRYer and less error prone.\n- [table_sortable_headers](#table_sortable_headers)\n- [table_sortable_columns](#table_sortable_columns)\n- [table_sortable_pager](#table_sortable_pager)\n\n#### table_sortable_headers\n##### Syntax: `table_sortable_headers [html_attributes]`  \nRenders the table header columns, each one inside a \\\u003cth\u003e tag.  \nNotice that it _does not_ wrap a \\\u003ctr\u003e tag around the headers, allowing you to add columns before of after TableSortable's headers. \n\nIt also renders data attributes that let tableSorter.js know the columns behaviour:\n- `data-filter=\"false\"` if the column's `filter` attribute is set to `false`.\n- `data-sorter=\"false\"` if the column's `sort` attribute is set to `false`.\n- `data-placeholder=\"?\"` if a placeholder has been defined (defaults to be the same as the column's label).\n- `data-value=\"?\"` if an initial filter value has been defined.\n\nYou may also specify any html attribute, as well as additional data attributes to be added to each \\\u003cth\u003e element.\n```erb\n\u003ctr\u003e \u003c%= table_sortable_headers class: 'text-center' %\u003e \u003c/tr\u003e\n```\n\nIf you wish to render your own version of a specific header (eg. to add an icon), \nyou may create a partial inside a `table_sortable` folder nested inside the controller's views folder. The partial should be named according to the following naming scheme:  \n`_\u003ctemplate\u003e_header.html`, with `template` corresponding to the column's template attribute.  \nNotice that in this case you need to manually specify the different data attributes that correspond to the behaviour you wish this column to have.\n\nThe view will be supplied with two locals:\n- `label`: the column's label\n- `column`: the TableSortable::Column object\n- `index`: the column's index (useful for manually setting the `data-col` attribute)\n\nHere is an example of a full name header partial, which includes a font-awesome icon:\n```erb\n\u003c!-- views/users/table_sortable/_full_name_header.html.erb --\u003e\n\u003cth data-placeholder=\"\u003c%= column.placeholder %\u003e\"\u003e \n    \u003ci class=\"fa fa-user\" aria-hidden=\"true\"\u003e\u003c/i\u003e\n    \u003c%= label %\u003e \n\u003c/th\u003e\n```\n\nBy using the template attribute you may render several columns using the same template.\n\n_Notice that whether using slim or erb, you must include .html before the extension._\n\n#### table_sortable_columns\n##### Syntax: `table_sortable_columns record, [html_attributes]`  \nRenders the table columns for a specific `record`, each one inside a \\\u003ctd\u003e tag.  \nNotice that it _does not_ wrap a \\\u003ctr\u003e tag around the headers, allowing you to add columns before of after TableSortable's columns. \n\nIt also renders data attributes that let tableSorter.js know the columns behaviour:\n- `data-text=\"?\"` if the column's `value` attribute is different than the column's `content` attribute, the value will be set in the `data-text` attribute, and the content will be displayed inside the \\\u003ctd\u003e element.\n\nYou may also specify any html attribute, as well as additional data attributes to be added to each \\\u003ctd\u003e element.\n```erb\n\u003ctr\u003e \u003c%= table_sortable_columns @user, class: 'text-info' %\u003e \u003c/tr\u003e\n```\n\nIf you wish to render your own version of a specific column (eg. to include a link inside of it), \nyou may create a partial inside a `table_sortable` folder nested inside the controller's views folder. The partial should be named according to the following naming scheme:  \n`_\u003ctemplate\u003e_column.html`, with `template` corresponding to the column's template attribute.  \nNotice that in this case you need to manually specify the different data attributes that correspond to the behaviour you wish this column to have.\n\nThe view will be supplied with four locals:\n- `record`: the source ActiveRecord object\n- `column`: the TableSortable::Column object\n\nHere is an example of a full name column partial, in which the name links to the edit_user_path:\n```erb\n\u003c!-- views/users/table_sortable/_full_name_column.html.erb --\u003e\n\u003ctd\u003e \n    \u003c%= link_to content, edit_user_path(source) %\u003e \n\u003c/td\u003e\n```\nBy using the template attribute you may render several columns using the same template.\n\n_Notice that whether using slim, haml or erb, you must include .html before the extension._\n\n#### table_sortable_pager\ndocumentation coming soon...\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/odedd/table_sortable. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n\n## Code of Conduct\n\nEveryone interacting in the TableSortable project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/table_sortable/blob/master/CODE_OF_CONDUCT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fodedd%2Ftable_sortable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fodedd%2Ftable_sortable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fodedd%2Ftable_sortable/lists"}