Shield UI jQuery Grid Integration with Ruby on Rails

The Shield UI JavaScript framework allows for an easy and straightforward integration with various RESTful frameworks. In this article we will demonstrate how the Shield UI jQuery Grid Component can be integrated with a standard Ruby on Rails application. The integration takes about 30 minutes to complete from scratch and the complete project is available on GitHub.

Project Setup

Let’s start with setting up a new Rails project assuming the latest Ruby version is installed in RVM and the environment is properly configured as described in the documentation. First we need to create a directory that will contain our new Rails application and install the latest Rails framework and the Bundler gem for managing the application packages:

$ mkdir shieldui-grid-rails
$ echo '2.3.1' > shieldui-grid-rails/.ruby-version
$ echo 'shieldui-grid' > shieldui-grid-rails/.ruby-gemset
$ cd shieldui-grid-rails
$ gem install bundler rails

Once all the dependencies are installed we have to initialize the application:

$ rails new .

This will create the application structure in the current directory and add all necessary files. To test that the application setup is successful let's create the database and start the server.

$ rake db:create
$ rails server

If everything is properly configured we should have a working Ruby on Rails application that renders the built-in welcome page in the browser:

Ruby on Rails Welcome Page

To facilitate the application views rendering we will add the Twitter Bootstrap responsive layout framework by following the installation instructions for the bootstrap-sass gem.

Finally we will generate a new scaffold that will create all Rails components and expose the RESTful routes that the Shield UI Grid will use to handle the data. The scaffold in our case will represent the expenses for a personal finance tracking application:

$ rails generate scaffold expense date:date title:string amount:decimal
$ rake db:migrate db:test:prepare

Shield UI Components Setup

We are going to use the Shield UI Lite version licensed under the MIT license as described in the GitHub repository. First we will install the framework in vendor/shieldui-lite following the instructions:

$ mkdir vendor/shieldui-lite
$ git clone git@github.com:shieldui/shieldui-lite.git vendor/shieldui-lite
$ cd vendor/shieldui-lite
$ npm install
$ grunt build

After the build has finished we need to copy the files from the dist/ folder and some of the external dependencies to vendor/assets/ and include them in the application JavaScript manifest and the application SCSS files:

$ cp dist/js/shieldui-lite-all.min.js ../assets/javascripts/shieldui-lite-all.min.js
$ cp -r external/{globalize,sinon} ../assets/javascripts/
#  app/assets/javascripts/application.js

//= require jquery
//= require bootstrap-sprockets
//= require jquery_ujs
//= require turbolinks
//= require globalize/globalize
//= require sinon/sinon
//= require shieldui-lite-all.min
//= require_tree .

As an alternative we can use the Shield UI Components trial version in our new Rails application by skipping the lite version setup and simply including a JavaScript include tag in the app/views/layouts/application.html.erb layout file

# app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
  <head>
    <title>Grid</title>
    <%= csrf_meta_tags %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag '//shieldui.com/shared/components/latest/js/shieldui-all.min.js', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <div class=“container”>
      <%= yield %>
    </div>
  </body>
</html>

For styling the jQuery Grid and the rest of the Shield UI components we need to add the CSS dependencies in the app/assets/stylesheets/application.scss SCSS file:

# app/assets/stylesheets/application.scss

@import '//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.0/normalize.min.css';
@import '//shieldui.com/shared/components/latest/css/shieldui-all.min.css';
@import '//shieldui.com/shared/components/latest/css/light/all.min.css';
@import 'bootstrap-sprockets';
@import 'bootstrap';

Preparing the application

After the Shield UI Components are setup let’s create a new dashboard controller and a default action for rendering the Grid that will use the expenses controller RESTful actions for managing the expenses data:

$ rails generate controller dashboard default

We will use the newly created default dashboard controller action as the default route:

# config/routes.rb

Rails.application.routes.draw do
  root to: 'dashboard#default'
  resources :expenses
end

The last step for integrating the Shield UI Grid Component is to add a placeholder <div id="expenses"></> for the data to the the default dashboard view:

# app/views/dashboard/default.html.erb

Expenses

Seeding the database

Testing the Grid widget integration in our Rails application will require some sample data. We can add some expense records using the database seed helper and to do that we will add the Faker gem to our Gemfile and update the seeds file:

$ echo "gem 'faker'" >> Gemfile
$ bundle install
# db/seeds.rb

Array.new(20) do
  Expense.create(title: Faker::Lorem.sentence, date: Faker::Date.between(1.week.ago, Date.today), amount: Faker::Number.decimal(2))
end

We can now easily populate the application database with 20 new expense records that can be used for testing:

$ rake db:seed

Integrating the Shield UI Grid Component

Once we have the sample data added to the application database we can proceed with the Grid integration. In the next sections we will implement the CRUD actions using the expenses controller RESTful actions.

Read

Reading the expenses data and rendering it in the Grid Component is as easy as specifying the DataSource remote option and pointing it to the expenses controller index action. We will also have to define the DataSource schema that maps each of the exposed expense attributes and the formatting option for each column:

# app/assets/javascripts/dashboard.coffee

$ ->
  $('#expenses').shieldGrid
    dataSource:
      schema:
        fields:
          id: { path: 'id', type: Number }
          date: { path: ‘date’, type: Date }
          title: { path: 'title', type: String }
          amount: { path: 'amount', type: Number }
      remote:
        read: '/expenses.json'
    columns: [
      { field: 'date', title: 'Date', width: 100, format: "{0:MM/dd/yyyy}" },
      { field: 'title', title: 'Title', width: 200 },
      { field: 'amount', title: 'Amount', width: 80 }
    ]

If we want to add pagination and sorting to our results we just need to specify the paging: true and sorting: true options:

# app/assets/javascripts/dashboard.coffee

$ ->
  $('#expenses').shieldGrid
    dataSource:
      schema:
        fields:
          id: { path: 'id', type: Number }
          date: { path: 'date', type: Date }
          title: { path: 'title', type: String }
          amount: { path: 'amount', type: Number }
      remote:
        read: '/expenses.json'
    paging: true
    sorting: true
    columns: [
      { field: 'date', title: 'Date', width: 100, format: "{0:MM/dd/yyyy}" },
      { field: 'title', title: 'Title', width: 200 },
      { field: 'amount', title: 'Amount', width: 80 }
    ]

Having done that the Shiled UI Grid will automatically load and render the expenses from the database when we open the application in the browser:

Shield UI jQuery Grid Read

Update

Editing the jQuery Grid rows is available as a built-in action so we will first add a new Actions column that will render the edit button and enable the row editing option:

# app/assets/javascripts/dashboard.coffee

$ ->
  $('#expenses').shieldGrid
    dataSource:
      schema:
        fields:
          id: { path: 'id', type: Number }
          date: { path: 'date', type: Date }
          title: { path: 'title', type: String }
          amount: { path: 'amount', type: Number }
      remote:
        read: '/expenses.json'
    paging: true
    sorting: true
    columns: [
      { field: 'date', title: 'Date', width: 100, format: "{0:MM/dd/yyyy}" },
      { field: 'title', title: 'Title', width: 200 },
      { field: 'amount', title: 'Amount', width: 80 },
      {
        title: 'Actions',
        width: 70,
        buttons: [
          { cls: 'editButton', commandName: 'edit', caption: 'Edit' }
        ]
      }
    ]
    editing:
      enabled: true
      type: 'row'

Saving the data from the Grid is easily configured by adding a modify section to the DataSource options that defines an update callback which will trigger the AJAX request to the REST controller:

# app/assets/javascripts/dashboard.coffee

$ ->
  $('#expenses').shieldGrid
    dataSource:
      schema:
        fields:
          id: { path: 'id', type: Number }
          date: { path: 'date', type: Date }
          title: { path: 'title', type: String }
          amount: { path: 'amount', type: Number }
      remote:
        read: '/expenses.json'
        modify:
          update: (items, success, error) ->
            newItem = items[0]
            $.ajax
              url: "/expenses/#{newItem.data.id}"
              type: 'PUT'
              dataType: 'json'
              data: { expense: newItem.data }
            .then(success, error)
    paging: true
    sorting: true
    columns: [
      { field: 'date', title: 'Date', width: 100, format: "{0:MM/dd/yyyy}" },
      { field: 'title', title: 'Title', width: 200 },
      { field: 'amount', title: 'Amount', width: 80 },
      {
        title: 'Actions',
        width: 70,
        buttons: [
          { cls: 'editButton', commandName: 'edit', caption: 'Edit' }
        ]
      }
    ]
    editing:
      enabled: true
      type: 'row'

Shield UI jQuery Grid Update

Create

The Shield UI jQuery Grid allows to easily implement creation of new entries that get automatically added to the grid. To implement the create functionality we just need to define the create callback in the DataSource modify options section and add a toolbar button for triggering the new row action:

# app/assets/javascripts/dashboard.coffeee

$ ->
  $('#expenses').shieldGrid
    dataSource:
      schema:
        fields:
          id: { path: 'id', type: Number }
          date: { path: 'date', type: Date }
          title: { path: 'title', type: String }
          amount: { path: 'amount', type: Number }
      remote:
        read: '/expenses.json'
        modify:
          create: (items, success, error) ->
            newItem = items[0]
            $.ajax
              url: '/expenses'
              type: 'POST'
              dataType: 'json'
              data: { expense: newItem.data }
              complete: (xhr) ->
                if xhr.readyState == 4 and xhr.status == 201
                  newItem.data.id = xhr.responseJSON.id
                  return success()
                error()
          update: (items, success, error) ->
            newItem = items[0]
            $.ajax
              url: "/expenses/#{newItem.data.id}"
              type: 'PUT'
              dataType: 'json'
              data: { expense: newItem.data }
            .then(success, error)
    paging: true
    sorting: true
    columns: [
      { field: 'date', title: 'Date', width: 100, format: "{0:MM/dd/yyyy}" },
      { field: 'title', title: 'Title', width: 200 },
      { field: 'amount', title: 'Amount', width: 80 },
      {
        title: 'Actions',
        width: 70,
        buttons: [
          { cls: 'editButton', commandName: 'edit', caption: 'Edit' }
        ]
      }
    ]
    editing:
      enabled: true
      type: 'row'
    toolbar: [
      {
        buttons: [
          commandName: 'insert'
          caption: 'Add Expense'
        ]
        position: 'top'
      }
    ]

Shield UI jQuery Grid Create

Delete

Deleting items is implemented by adding a delete button to the Actions column that will be used to remove each row. Sending the delete request to the controller is again handled by the DataSource modify section where we just need to add a remove callback:

# app/assets/javascripts/dashboard.coffeee

$ ->
  $('#expenses').shieldGrid
    dataSource:
      schema:
        fields:
          id: { path: 'id', type: Number }
          date: { path: 'date', type: Date }
          title: { path: 'title', type: String }
          amount: { path: 'amount', type: Number }
      remote:
        read: '/expenses.json'
        modify:
          create: (items, success, error) ->
            newItem = items[0]
            $.ajax
              url: '/expenses'
              type: 'POST'
              dataType: 'json'
              data: { expense: newItem.data }
              complete: (xhr) ->
                if xhr.readyState == 4 and xhr.status == 201
                  newItem.data.id = xhr.responseJSON.id
                  return success()
                error()
          update: (items, success, error) ->
            newItem = items[0]
            $.ajax
              url: "/expenses/#{newItem.data.id}"
              type: 'PUT'
              dataType: 'json'
              data: { expense: newItem.data }
            .then(success, error)
          remove: (items, success, error) ->
            newItem = items[0]
            $.ajax
              url: "/expenses/#{newItem.data.id}"
              type: 'DELETE'
              dataType: 'json'
            .then(success, error)
    paging: true
    sorting: true
    columns: [
      { field: 'date', title: 'Date', width: 100, format: "{0:MM/dd/yyyy}" },
      { field: 'title', title: 'Title', width: 200 },
      { field: 'amount', title: 'Amount', width: 80 },
      {
        title: 'Actions',
        width: 70,
        buttons: [
          { cls: 'editButton', commandName: 'edit', caption: 'Edit' }
          { cls: 'deleteButton', commandName: 'delete', caption: 'Delete' }
        ]
      }
    ]
    editing:
      enabled: true
      type: 'row'
    toolbar: [
      {
        buttons: [
          commandName: 'insert'
          caption: 'Add Expense'
        ]
        position: 'top'
      }
    ]

To avoid accidentally deleting items we can easily setup a confirmation message for the delete action by adding a confirmation option to the editing options:

# app/assets/javascripts/dashboard.coffeee

$ ->
  $('#expenses').shieldGrid
    dataSource:
      schema:
        fields:
          id: { path: 'id', type: Number }
          date: { path: 'date', type: Date }
          title: { path: 'title', type: String }
          amount: { path: 'amount', type: Number }
      remote:
        read: '/expenses.json'
        modify:
          create: (items, success, error) ->
            newItem = items[0]
            $.ajax
              url: '/expenses'
              type: 'POST'
              dataType: 'json'
              data: { expense: newItem.data }
              complete: (xhr) ->
                if xhr.readyState == 4 and xhr.status == 201
                  newItem.data.id = xhr.responseJSON.id
                  return success()
                error()
          update: (items, success, error) ->
            newItem = items[0]
            $.ajax
              url: "/expenses/#{newItem.data.id}"
              type: 'PUT'
              dataType: 'json'
              data: { expense: newItem.data }
            .then(success, error)
          remove: (items, success, error) ->
            newItem = items[0]
            $.ajax
              url: "/expenses/#{newItem.data.id}"
              type: 'DELETE'
              dataType: 'json'
            .then(success, error)
    paging: true
    sorting: true
    columns: [
      { field: 'date', title: 'Date', width: 100, format: "{0:MM/dd/yyyy}" },
      { field: 'title', title: 'Title', width: 200 },
      { field: 'amount', title: 'Amount', width: 80 },
      {
        title: 'Actions',
        width: 70,
        buttons: [
          { cls: 'editButton', commandName: 'edit', caption: 'Edit' }
          { cls: 'deleteButton', commandName: 'delete', caption: 'Delete' }
        ]
      }
    ]
    editing:
      enabled: true
      type: 'row'
      confirmation:
        delete:
          enabled: true
          template: (item) ->
            "Are you sure you want to delete expense \"#{item.title}\"?"
    toolbar: [
      {
        buttons: [
          commandName: 'insert'
          caption: 'Add Expense'
        ]
        position: 'top'
      }
    ]

Shield UI jQuery Grid Delete

This way way have a full coverage of all the CRUD actions for our expenses records and we can proceed with implementing the ShieldUI jQuery Chart Component to visualize the expenses data as part of a sample personal finance tracking application.