6

I am using the ajax-datatables-rails. Below is the JS for my datatable. I want to convert the javascript into equivalent Opal.rb.

jQuery(document).ready(function() {
  var table = $('#organizations-datatable');
  var token = $('meta[name=csrf-token]').attr('content');
  table.DataTable({
    "processing": true,
    "serverSide": true,
    "ajax": {
      "url": "/organizations/datatable.json",
      "type": 'POST',
      "beforeSend": function (xhr) {
          xhr.setRequestHeader('X-CSRF-Token', token)
        }
    },
    "pagingType": "full_numbers",
    "destroy": true,
    "columns": [
      {"data": "name"},
      {"data": "desc"},
      {"data": "industry"},
      {"data": "tags"}
    ],
    "iDisplayLength": 25
  });
});

It includes adding the CSRF token to the ajax request for data.

I would like all of our javascript to be written as opal for readability, etc. Please don't debate that choice, I only care to understand how to get this working as Opalrb via opal/rails-ujs/opal-jquery

The below Opal does not achieve the same as the above JS. Can anyone help me understand why?

Element.expose :DataTable

Document.ready? do
  token     = Element['meta[name=csrf-token]'].attr('content');
  settings  = {
      "processing": true,
      "serverSide": true,
      "ajax": {
        "url": "/organizations/datatable.json",
        "type": 'POST',
        "beforeSend": lambda do
          xhr = `new window.XMLHttpRequest()`
          xhr.setRequestHeader('X-CSRF-Token', token)
        end 
      },
      "pagingType": "full_numbers",
      "destroy": true,
    "columns": [
      {"data": "name"},
      {"data": "desc"},
      {"data": "industry"},
      {"data": "tags"}
    ]
    }
  Element['#organizations-datatable'].DataTable(settings.to_n)

end

What am I missing here? Why isn't this Opal any good?

Edit: This is what my route for this looks like, this ensures POST works for this:

Rails.application.routes.draw do
    concern :with_datatable do
        post 'datatable', on: :collection
    end
    resources :organizations,   concerns: [:with_datatable]
end

This is my related controller and datatable code using the gem:

class OrganizationsController < ApplicationController
    def index
        @title              = "Organizations"
        @page_description   = "Organization data warehouse"
        @page_icon          = "institution"
        @organization       = Organization.new
        @load               = {data_table: true}
        @menu               = Menu.new 

      respond_to do |format|
        format.html
        format.json { render json: OrganizationDatatable.new(params) }
      end

    end

    def datatable
        logger.ap "datatable params: #{params}"
        respond_to do |format|
            format.json { render json: OrganizationDatatable.new(params) }
        end
    end

    def get_raw_records
        Organization.all
    end
    def create

    end
    def edit

    end
    def destroy

    end

    def show    
    end

    def update
    end

    def new
    end
end

Here is the datatable

class OrganizationDatatable < AjaxDatatablesRails::ActiveRecord

  extend Forwardable
  include ActionView::Helpers::TextHelper

  def_delegator :@view, :link_to

  def view_columns
    # Declare strings in this format: ModelName.column_name
    # or in aliased_join_table.column_name format

    @view_columns ||= {
      # id:       { source: "Organization.id"           },
      name:     { source: "Organization.name",cond: :like, searchable: true, orderable: true          },
      desc:     { source: "Organization.description",cond: :like, searchable: true, orderable: true   },
      industry: { source: "Organization.industry",cond: :like, searchable: true, orderable: true      },
      tags:     { source: "Organization.tag_list", searchable: false, orderable: false      }
    }
  end

  def data
    records.map do |record|
      {
        id:       record.id,
        name:     record.name,
        desc:     truncate(record.description,length: 240, separator: ' '),
        industry: record.industry,
        tags:     record.decorate.buttonize_tags,
        DT_RowId: record.id
      }
    end
  end

  def get_raw_records
    Organization.all
  end

end

Michael K Madison
  • 2,242
  • 3
  • 21
  • 35
  • 1
    what is the difference between the two results? I advise you to set a `binding.pry` and check every variables values in your `Opal` script – Fabrizio Bertoglio Apr 22 '19 at 19:15
  • what is exactly not working properly? could it be related with `beforeSend`? – Ana María Martínez Gómez Apr 23 '19 at 14:17
  • The datatable is not populated. Yes. It's basically 3 lines of code in JS and 5 in Opal. I don't see a mistake. There aren't any errors in console that I see. – Michael K Madison Apr 24 '19 at 17:11
  • the problem is not included in the question you posted. You need to add more information and better troubleshoot this issue. Currently you are guessing that the problem is in those 3 lines of Opal code, but you have no clue until you correctly troubleshoot it. Try creating a simple `Opal` example and see if it works, then try to `console.log` with `Opal` code the `DataTable` object... and step by step add functionalities until you see exactly what breaks so that you have clear clue of what is happening.. otherwise we could just try to guess. – Fabrizio Bertoglio Apr 25 '19 at 07:40
  • You have a silent error which is hard to troubleshoot. Which are the possible mistakes in `Javascript` which cause silent errors? – Fabrizio Bertoglio Apr 25 '19 at 07:42

1 Answers1

2

I will start with pointing few mistakes in your code ,

  1. The code in beforeSend is problematic please refer to supplying-an-xhr-method, change it to the below code,

    "beforeSend": lambda do |xhr|

    `xhr.setRequestHeader('X-CSRF-Token', token)`

enclose xhr statements in ( ` ) backticks like above statement

  1. columns should have two dimensional array rather than array of objects,

    "columns": [ ["data": "name"], ["data": "desc"], ["data": "industry"], ["data": "tags"] ]

Rest of the code looks fine.

Below is the tested code,

Element.expose :DataTable
Document.ready? do
  token     = Element['meta[name=csrf-token]'].attr('content');
  settings  = {
      "ajax": {
        "url": "/data.json",
        "type": 'POST',
        "beforeSend": lambda do |xhr|
          `xhr.setRequestHeader('X-CSRF-Token', token)`
        end 
      },
      "pagingType": "full_numbers",
      "destroy": true,
    "columns": [
      ["data": "Name"],
      ["data": "Position"],
      ["data": "Office"],
      ["data": "Extn."],
      ["data": "Start"],
      ["data": "Salary"]
    ]
    }
  Element['#organizations-datatable'].DataTable(settings.to_n)

end

Make sure you have data.json file in public folder of rails app, and contents as follows,

{
    "data": [
      [
        "Tiger Nixon",
        "System Architect",
        "Edinburgh",
        "5421",
        "2011/04/25",
        "$320,800"
      ],
      [
        "Garrett Winters",
        "Accountant",
        "Tokyo",
        "8422",
        "2011/07/25",
        "$170,750"
      ],
      [
        "Ashton Cox",
        "Junior Technical Author",
        "San Francisco",
        "1562",
        "2009/01/12",
        "$86,000"
      ],
      [
        "Cedric Kelly",
        "Senior Javascript Developer",
        "Edinburgh",
        "6224",
        "2012/03/29",
        "$433,060"
      ],
      [
        "Airi Satou",
        "Accountant",
        "Tokyo",
        "5407",
        "2008/11/28",
        "$162,700"
      ],
      [
        "Brielle Williamson",
        "Integration Specialist",
        "New York",
        "4804",
        "2012/12/02",
        "$372,000"
      ],
      [
        "Herrod Chandler",
        "Sales Assistant",
        "San Francisco",
        "9608",
        "2012/08/06",
        "$137,500"
      ],
      [
        "Rhona Davidson",
        "Integration Specialist",
        "Tokyo",
        "6200",
        "2010/10/14",
        "$327,900"
      ],
      [
        "Colleen Hurst",
        "Javascript Developer",
        "San Francisco",
        "2360",
        "2009/09/15",
        "$205,500"
      ],
      [
        "Sonya Frost",
        "Software Engineer",
        "Edinburgh",
        "1667",
        "2008/12/13",
        "$103,600"
      ],
      [
        "Jena Gaines",
        "Office Manager",
        "London",
        "3814",
        "2008/12/19",
        "$90,560"
      ],
      [
        "Quinn Flynn",
        "Support Lead",
        "Edinburgh",
        "9497",
        "2013/03/03",
        "$342,000"
      ],
      [
        "Charde Marshall",
        "Regional Director",
        "San Francisco",
        "6741",
        "2008/10/16",
        "$470,600"
      ],
      [
        "Haley Kennedy",
        "Senior Marketing Designer",
        "London",
        "3597",
        "2012/12/18",
        "$313,500"
      ],
      [
        "Tatyana Fitzpatrick",
        "Regional Director",
        "London",
        "1965",
        "2010/03/17",
        "$385,750"
      ],
      [
        "Michael Silva",
        "Marketing Designer",
        "London",
        "1581",
        "2012/11/27",
        "$198,500"
      ],
      [
        "Paul Byrd",
        "Chief Financial Officer (CFO)",
        "New York",
        "3059",
        "2010/06/09",
        "$725,000"
      ],
      [
        "Gloria Little",
        "Systems Administrator",
        "New York",
        "1721",
        "2009/04/10",
        "$237,500"
      ],
      [
        "Bradley Greer",
        "Software Engineer",
        "London",
        "2558",
        "2012/10/13",
        "$132,000"
      ],
      [
        "Dai Rios",
        "Personnel Lead",
        "Edinburgh",
        "2290",
        "2012/09/26",
        "$217,500"
      ],
      [
        "Jenette Caldwell",
        "Development Lead",
        "New York",
        "1937",
        "2011/09/03",
        "$345,000"
      ],
      [
        "Yuri Berry",
        "Chief Marketing Officer (CMO)",
        "New York",
        "6154",
        "2009/06/25",
        "$675,000"
      ],
      [
        "Caesar Vance",
        "Pre-Sales Support",
        "New York",
        "8330",
        "2011/12/12",
        "$106,450"
      ],
      [
        "Doris Wilder",
        "Sales Assistant",
        "Sidney",
        "3023",
        "2010/09/20",
        "$85,600"
      ],
      [
        "Angelica Ramos",
        "Chief Executive Officer (CEO)",
        "London",
        "5797",
        "2009/10/09",
        "$1,200,000"
      ],
      [
        "Gavin Joyce",
        "Developer",
        "Edinburgh",
        "8822",
        "2010/12/22",
        "$92,575"
      ],
      [
        "Jennifer Chang",
        "Regional Director",
        "Singapore",
        "9239",
        "2010/11/14",
        "$357,650"
      ],
      [
        "Brenden Wagner",
        "Software Engineer",
        "San Francisco",
        "1314",
        "2011/06/07",
        "$206,850"
      ],
      [
        "Fiona Green",
        "Chief Operating Officer (COO)",
        "San Francisco",
        "2947",
        "2010/03/11",
        "$850,000"
      ],
      [
        "Shou Itou",
        "Regional Marketing",
        "Tokyo",
        "8899",
        "2011/08/14",
        "$163,000"
      ],
      [
        "Michelle House",
        "Integration Specialist",
        "Sidney",
        "2769",
        "2011/06/02",
        "$95,400"
      ],
      [
        "Suki Burks",
        "Developer",
        "London",
        "6832",
        "2009/10/22",
        "$114,500"
      ],
      [
        "Prescott Bartlett",
        "Technical Author",
        "London",
        "3606",
        "2011/05/07",
        "$145,000"
      ],
      [
        "Gavin Cortez",
        "Team Leader",
        "San Francisco",
        "2860",
        "2008/10/26",
        "$235,500"
      ],
      [
        "Martena Mccray",
        "Post-Sales support",
        "Edinburgh",
        "8240",
        "2011/03/09",
        "$324,050"
      ],
      [
        "Unity Butler",
        "Marketing Designer",
        "San Francisco",
        "5384",
        "2009/12/09",
        "$85,675"
      ],
      [
        "Howard Hatfield",
        "Office Manager",
        "San Francisco",
        "7031",
        "2008/12/16",
        "$164,500"
      ],
      [
        "Hope Fuentes",
        "Secretary",
        "San Francisco",
        "6318",
        "2010/02/12",
        "$109,850"
      ],
      [
        "Vivian Harrell",
        "Financial Controller",
        "San Francisco",
        "9422",
        "2009/02/14",
        "$452,500"
      ],
      [
        "Timothy Mooney",
        "Office Manager",
        "London",
        "7580",
        "2008/12/11",
        "$136,200"
      ],
      [
        "Jackson Bradshaw",
        "Director",
        "New York",
        "1042",
        "2008/09/26",
        "$645,750"
      ],
      [
        "Olivia Liang",
        "Support Engineer",
        "Singapore",
        "2120",
        "2011/02/03",
        "$234,500"
      ],
      [
        "Bruno Nash",
        "Software Engineer",
        "London",
        "6222",
        "2011/05/03",
        "$163,500"
      ],
      [
        "Sakura Yamamoto",
        "Support Engineer",
        "Tokyo",
        "9383",
        "2009/08/19",
        "$139,575"
      ],
      [
        "Thor Walton",
        "Developer",
        "New York",
        "8327",
        "2013/08/11",
        "$98,540"
      ],
      [
        "Finn Camacho",
        "Support Engineer",
        "San Francisco",
        "2927",
        "2009/07/07",
        "$87,500"
      ],
      [
        "Serge Baldwin",
        "Data Coordinator",
        "Singapore",
        "8352",
        "2012/04/09",
        "$138,575"
      ],
      [
        "Zenaida Frank",
        "Software Engineer",
        "New York",
        "7439",
        "2010/01/04",
        "$125,250"
      ],
      [
        "Zorita Serrano",
        "Software Engineer",
        "San Francisco",
        "4389",
        "2012/06/01",
        "$115,000"
      ],
      [
        "Jennifer Acosta",
        "Junior Javascript Developer",
        "Edinburgh",
        "3431",
        "2013/02/01",
        "$75,650"
      ],
      [
        "Cara Stevens",
        "Sales Assistant",
        "New York",
        "3990",
        "2011/12/06",
        "$145,600"
      ],
      [
        "Hermione Butler",
        "Regional Director",
        "London",
        "1016",
        "2011/03/21",
        "$356,250"
      ],
      [
        "Lael Greer",
        "Systems Administrator",
        "London",
        "6733",
        "2009/02/27",
        "$103,500"
      ],
      [
        "Jonas Alexander",
        "Developer",
        "San Francisco",
        "8196",
        "2010/07/14",
        "$86,500"
      ],
      [
        "Shad Decker",
        "Regional Director",
        "Edinburgh",
        "6373",
        "2008/11/13",
        "$183,000"
      ],
      [
        "Michael Bruce",
        "Javascript Developer",
        "Singapore",
        "5384",
        "2011/06/27",
        "$183,000"
      ],
      [
        "Donna Snider",
        "Customer Support",
        "New York",
        "4226",
        "2011/01/25",
        "$112,000"
      ]
    ]
  }

add opal to Gemfile,

gem 'opal-rails'

gem 'opal-jquery'

application.js.rb contents,

require 'opal'
require 'jquery'
require 'opal-jquery'
require 'datatables.min'
require_tree '.'

add datatables.min.js file to app/assets/javascripts folder

html,

<table id="organizations-datatable" class="display" style="width:100%">
    <thead>
        <tr>
            <th>Name</th>
            <th>Position</th>
            <th>Office</th>
            <th>Extn.</th>
            <th>Start date</th>
            <th>Salary</th>
        </tr>
    </thead>
    <tfoot>
        <tr>
            <th>Name</th>
            <th>Position</th>
            <th>Office</th>
            <th>Extn.</th>
            <th>Start date</th>
            <th>Salary</th>
        </tr>
    </tfoot>
</table>

NOTE :- If you want it to work with POST you have to adjust your config/routes.rb,

Add the following entry in routes.rb for your json file,

For me it was,

post '/data.json', :to => redirect('/data.json')

For you it will be,

post '/organizations/datatable.json', :to => redirect('/organizations/datatable.json')

Full code here https://gitlab.com/shoyebsheikh/opal-datatables

Ajax datatables rails gem solution:

You can add the following line to your routes assuming thats the data source,

post '/organizations/datatable.json', :to => redirect('/organizations/datatable.json')

Your controller should be like,

class OrganizationsController < ApplicationController
    def datatable
        params["columns"] ||= { "0" => {"data" => "" } }
        params["length"]  ||= -1
        respond_to do |format|
            format.html
            format.json { render json: OrganizationDatatable.new(params) }
        end
    end
end

And your datatable as follows,

class OrganizationDatatable < AjaxDatatablesRails::ActiveRecord

  def view_columns
    # Declare strings in this format: ModelName.column_name
    # or in aliased_join_table.column_name format

    @view_columns ||= {
      # id:       { source: "Organization.id"           },
      name:     { source: "Organization.name",cond: :like, searchable: true, orderable: true          },
      desc:     { source: "Organization.description",cond: :like, searchable: true, orderable: true   },
      industry: { source: "Organization.industry",cond: :like, searchable: true, orderable: true      },
      tags:     { source: "Organization.tag_list", searchable: false, orderable: false      }
    }
  end

  def data
    records.map do |record|
      {
        # id:       record.id,
        name:     record.name,
        desc:     truncate(record.description,length: 240, separator: ' '),
        industry: record.industry,
        tags:     record.decorate.buttonize_tags,
        DT_RowId: record.id
      }
    end
  end

  def get_raw_records
    Organization.all
  end

end

In your organizations.js.rb goes the opal code,

Document.ready? do
  token     = Element['meta[name=csrf-token]'].attr('content');
  settings  = {
      "ajax": {
        "url": Element['#organizations-datatable'].data('source'),
        "type": 'POST',
        "beforeSend": lambda do |xhr|
          `xhr.setRequestHeader('X-CSRF-Token', token)`
        end 
      },
      "pagingType": "full_numbers",
      "destroy": true,
    "columns": [
      {"data": "name"},
      {"data": "desc"},
      {"data": "industry"},
      {"data": "tags"}
    ]
    }
  Element['#organizations-datatable'].dataTable(settings.to_n)

end

And in html,

<table id="organizations-datatable" data-source="<%= organizations_index_path(format: :json) %>">
  <thead>
    <tr>
      <th>Name</th>
      <th>Desc</th>
      <th>Industry</th>
      <th>tags</th>
    </tr>
  </thead>
  <tbody>
  </tbody>
</table>

organizations_index_path can be organizations_path or in singular like organization_index_path or organization_path depending on your controller name

Please refer to https://gitlab.com/shoyebsheikh/ajax-datatables-rails-with-opal for more info.

Note:

  1. Used mysql2 gem.
  2. Run migrations and add entries to the table from rails CLI or manually in the table.
  3. If you run the project after installing all the dependencies and ran the migration, visit http://localhost:3000/user/index or http://localhost:3000/hello_world/index
Shoyeb Sheikh
  • 2,659
  • 2
  • 10
  • 19
  • POST is required to avoid 414 Request-URI Too Large error. – Michael K Madison Apr 26 '19 at 12:31
  • All the examples I have through of datatables uses GET, when I used POST in this example it was not recognizing the json file. – Shoyeb Sheikh Apr 26 '19 at 12:34
  • Using POST comes from [this](https://github.com/jbox-web/ajax-datatables-rails/issues/278) and [this](https://github.com/jbox-web/ajax-datatables-rails/issues/308#issuecomment-424897335) ... I am using the ajax-datatables-rails gem and for what it is worth I have datatables working well with POST using javascript. In fact, the javascript snippet in the OP works just fine in Rails. datatable is populated without a problem, even with the array of objects. I am literally trying to get Opal to generate the same output. – Michael K Madison Apr 26 '19 at 12:56
  • I am trying this now to figure this out. Thanks for the response. – Michael K Madison Apr 26 '19 at 12:56
  • I am still stuck on this. I wanted to award you but it didn't solve the issue, which is baffling me. – Michael K Madison Apr 28 '19 at 10:38
  • Yes. I've done that. As I mentioned in the OP, it works for perfectly fine as the OPs Javascript. I want to understand how to get it to work as Opal. – Michael K Madison Apr 29 '19 at 06:26
  • @MichaelKMadison its opal and you told me POST is required so I added a code to work it for ajax POST have you seen my edited code ? – Shoyeb Sheikh Apr 29 '19 at 06:30
  • See my routes in the edit, it covers POST and works in javascript but not for Opal. I'm truly baffled by this. – Michael K Madison Apr 30 '19 at 07:47
  • I am not sure what you are asking my opal works and populates datatables. – Shoyeb Sheikh Apr 30 '19 at 08:01
  • I will evaluate and if so I will open another bounty and give to you. I have no idea what it means for my environment if this doesn't work for me. – Michael K Madison Apr 30 '19 at 14:05
  • This does not work for me. Can you post yours on Github. I really want to get to the bottom of this. What version of Opal are you using, etc. – Michael K Madison Apr 30 '19 at 20:07
  • Dude. Totally thanks so much. Once I look at it will issue another bounty and award to you... about to clone it now. – Michael K Madison May 02 '19 at 01:23
  • Thanks Shoyebh. This is completely crazy. Either one of my gems or something else in my environment is messing this up, datatable version. Whatever. (Maybe rails_ujs? I don't know. I will update this when I do know.) What I know for sure is that you solved this and your repository will help me narrow down whatever it is causing the problem. For your diligence I am giving twice the original bounty so instead of 50, I will give 100 (on top of losing the original 50). You went out of your way to help me and that needs to be rewarded. Thank you so so so much. 100pts is yours after 24h wait. – Michael K Madison May 02 '19 at 10:11
  • Oh, I will add that I was using a lower version of ruby as well. I will zero in on the variable and figure out what is happening. And actually your implementation includes the ajax-datatables-rails gem but may not even use it from what I can see. I may take it out completely if it's the reason. – Michael K Madison May 02 '19 at 10:14
  • @MichaelKMadison yes you can take out ajax-datatables-rails gem, I included it coz you mentioned that in your question, but I realized your also not using it, but when I tried to remove that gem it was giving me error so I left it as it is, and yes my code is not using it, glad my code worked for you though. – Shoyeb Sheikh May 02 '19 at 10:47
  • Removed ajax-datatables-rails gem successfully and pushed to git project https://gitlab.com/shoyebsheikh/opal-datatables – Shoyeb Sheikh May 04 '19 at 04:18
  • Shoyeb yes I am using the ajax-datatables-rails gem I will add my code for it and give you another 200 points if you can get the two to work together (Opal and ajax-datatables-rails) – Michael K Madison May 04 '19 at 10:05
  • OK. I've added details above. Help me with this and earn another 200 points – Michael K Madison May 04 '19 at 10:12
  • @MichaelKMadison answer updated, used ajax-datatables-rails gem, and added a gitlab project link, try cloning the project and make changes to your code accordingly. – Shoyeb Sheikh May 05 '19 at 06:00
  • Shoyeb, just the idea has made me very happy. Thank you for your diligence, of course I will review and award if it works. – Michael K Madison May 05 '19 at 07:48
  • did you push the update to the same repo? That link takes me to the old repo that doesn't have the gem and hasn't generated the datatable folder or datatable class. – Michael K Madison May 05 '19 at 09:35
  • ok so I see it under your user on gitlab. Just so others also find it -- it is here: https://gitlab.com/shoyebsheikh/ajax-datatables-rails-with-opal – Michael K Madison May 05 '19 at 09:38
  • OK. I populated the User table and it frickin' works. I truly have no idea what is wrong with mine but it does use ajax-datatables-rails and Opal. Sheesh. I'm a man of my word. Those 200pts. are yours. I assume you will leave the repo up for others or describe it well enough above. – Michael K Madison May 05 '19 at 10:41
  • Thanks again my friend. Well desserved. – Michael K Madison May 05 '19 at 10:41
  • @MichaelKMadison yeah I am not deleting those two repos, leaving it for others to learn, I am glad I could help, wish you get your project working soon. – Shoyeb Sheikh May 05 '19 at 11:41