jbox-web / ajax-datatables-rails

A wrapper around DataTable's ajax methods that allow synchronization with server-side pagination in a Rails app
MIT License
590 stars 227 forks source link

Is orthogonal data supported with this gem? #269

Closed gs2589 closed 6 years ago

gs2589 commented 6 years ago

In my data table I have the following column definition:

columns:[
       {data: "supplier",
             render: function(data, type, row){
              return data ? data.name : null
             }
]

In my SearchResultDatatable#data method I have

def data
    records.map do |record|
      {
        supplier: {name: record.supplier.try(:name)},

      }
end

This is not working - I get the datatables.net/tn/4 error because

{name: record.supplier.try(:name)}

gets serialized as a string not an object- so data.name returns undefined.

Any thoughts on how to fix this?

n-rodriguez commented 6 years ago

Add this in your JS:

data = JSON.parse(data);
gs2589 commented 6 years ago

Thanks n-rodriguez. I tried your suggestion, but got this error:

Uncaught SyntaxError: Unexpected token # in JSON at position 0 at JSON.parse () at eval (eval at format (purchases:12), :1:6) at format (purchases:265) at HTMLTableCellElement. (purchases:355) at HTMLTableSectionElement.dispatch (jquery.self-bd7ddd393353a8d2480a622e80342adf488fb6006d667e8b42e4c0073393abee.js?body=1:5227) at HTMLTableSectionElement.elemData.handle (jquery.self-bd7ddd393353a8d2480a622e80342adf488fb6006d667e8b42e4c0073393abee.js?body=1:4879)

It looks like the issue arises because of the #sanitize method

def sanitize(data)
      data.map do |record|
        if record.is_a?(Array)
          record.map { |td| ERB::Util.html_escape(td) }
        else
          record.update(record){ |_, v| ERB::Util.html_escape(v) }
        end
      end
    end

Is there a way to go around this? Is it a bad idea to bypass this method? Is there a way to get standard serialization into json?

gs2589 commented 6 years ago

Ok, Im making some progress:

Here is some new code:

def data
    records.map do |record|
      {id: record.id,
       scan_timestamp: raw({data:record.effective_date.in_time_zone('Eastern Time (US & Canada)').strftime("%I:%M%p %Z %d-%b-%y"), display:"hello"}.to_json)
end

then in js, I can do data = JSON.parse(row.scan_timestamp) and that converts it into an object. Im wondering if there is a way to format the data in Datatable#data to ensure that the js datatables automatically turns orthogonal data into nested objects?

Thanks!

MrMoins commented 6 years ago

I'm also trying to use orthogonal data but can't figure how to do with this gem.

MrMoins commented 6 years ago

I'm trying to modify sanitize method like this, it return a json object correctly formatted but now I've some issue on JS side.

def sanitize(data)
  data.map do |record|
    if record.is_a?(Hash)
      record
    elsif record.is_a?(Array)
      record.map { |td| ERB::Util.html_escape(td) }
    else
      record.update(record){ |_, v| ERB::Util.html_escape(v) }
    end
  end
end
gs2589 commented 6 years ago

@n-rodriguez Any thoughts on this? I'd love to figure out a 'standard' way to do this with this gem and add it to the readme. Orthogonal data is pretty core to datatables, so I think many users would benefit. I'd be happy to write up the guide for the readme, at this moment, I've not been able to get this to work.

n-rodriguez commented 6 years ago

Hi! It could be supported. Nowadays the data is assumed to be html content so it's html escaped to avoid XSS attacks. What you want is json all the way so :

Override the sanitize method in your datatable :


  def data
    records.map do |record|
      {
        id:    record.id,
        title: { title: record.title }
      }
    end
  end

  private

    def sanitize(data)
      data.map do |record|
        if record.is_a?(Array)
          record.map { |td| ERB::Util.html_escape(td) }
        elsif record.is_a?(Hash)
          if options[:json_content]
            record
          else
            record.update(record) { |_, v| ERB::Util.html_escape(v) }
          end
        end
      end
    end

and in the controller :

  def index
    respond_to do |format|
      format.html
      format.json { render json: PostDatatable.new(params, json_content: true) }
    end
  end

And in Coffee/JS :

$ ->
  $('#posts-datatable').dataTable
    processing: true
    serverSide: true
    ajax: $('#posts-datatable').data('source')
    pagingType: 'full_numbers'
    columns: [
      { data: 'id' }
      {
        data: 'title',
        render: (data, type, row)->
          console.log(data)
          if data?
            data.title
          else
            ''
      }
    ]
n-rodriguez commented 6 years ago

BTW I would use https://github.com/jbox-web/ajax-datatables-rails#using-view-decorators to avoid this kind of lengty methods :

# before
def data
  records.map do |record|
    {
      id: record.id,
      scan_timestamp: raw({ data: record.effective_date.in_time_zone('Eastern Time (US & Canada)').strftime("%I:%M%p %Z %d-%b-%y"), display:"hello" }.to_json
    }
  end
end

# after
def data
  records.map do |record|
    {
      id: record.id,
      scan_timestamp: { data: record.decorate.effective_date, display: 'hello' }
    }
  end
end
n-rodriguez commented 6 years ago

Added in the README : https://github.com/jbox-web/ajax-datatables-rails#tutorial

calaway commented 5 years ago

I'm trying to specify a data-sort (or data-order) tag on a column and can't quite get it working from these instructions. Can you help me see what I've done wrong?

  def data
    records.map do |record|
      {
        id: record.id,
        build_number: { display: record.build_number, sort_by: record.build_number.to_i },
      }
    end
  end
$(document).ready(function(){
  $('#builds-datatable').DataTable({
    // ...
    columns: [
      { data: "id"},
      { data: "build_number",
        render: {
          _: 'display',
          sort: 'sort_by',
       }
      }
    ]
  });
});

display is showing correctly, but I can't get it to add the data-sort attribute. I know both display and sort_by are coming through to the client side correctly, since I can access both of them.

I have added json_content: true to the controller and copied the sanitize method into the datatable class.

Any help you can offer is greatly appreciated. Thank you for creating and maintaining such a useful gem!