alfajango / jquery-dynatable

A more-fun, semantic, alternative to datatables
http://www.dynatable.com
Other
2.77k stars 361 forks source link

colspan support in head #30

Open gowhari opened 10 years ago

gowhari commented 10 years ago

Seems that colspan is not supported in dynatable.

http://jsfiddle.net/47j3C/ http://i.imgur.com/KbEvEcw.png

The result is same with or without data-dynatable-column

JangoSteve commented 10 years ago

Interesting. Though your example would actually require support for both rowspan and colspan. I'm not 100% sure how I'd implement this, as I'm not sure how one would generally expect dynatable to interpret the given structure.

c1 c2 c3 c4
s1 s2

Also, in the above table you have "c2", "s1", and "s2" all labeled with dinstinct data-dynatable-column attributes, which are meant to correspond with the attributes of the JSON collection. So when dynatable is filling in the table cell under "s1", should it be looking for the c2 property or the s1 property, because that cell would be under both headings simultaneously.

Assuming we figured that out, we'd still need to consider how it'd behave differently with:

c1 c2 c3 c4
s1

And then, if we were to do that, why stop at 2 rows?

c1 c2 c3 c4
s1 s2
t1 t2
gowhari commented 10 years ago

ExtJs handle it in a completely different way. I think a simple answer is: Dynatable don't try to figure out the structure yourself. Developer should use thead to define her structure of colspan, rowspans. And then she should explicitly define the order of columns in json, js or html.

c1 [0] c2 c3 [3] c4 [4]
s1 [1] s2 [2]
c1 [0] c2 [1] c3 [2] c4 [3]
s1
c1 [0] c2 c3 [4] c4 [5]
s1 [1] s2
t1 [2] t2 [3]
JangoSteve commented 10 years ago

And then she should explicitly define the order of columns in json, js or html.

Yeah we're definitely not doing that. That's one of the things I couldn't stand about e.g. datatables, which was one of the main motivations of building dynatable in the first place.

I mean it is defined already in the HTML, I don't want the user to have to redefine it in the JS.

The developer should not have to explicitly define the order of the columns. That tightly couples the presentation of the data to the structure of the data, which leads to bad maintainability. It would mean that if you wanted to add a column to your table, you'd have to update your HTML, and then also update the logic in your JavaScript or JSON structure to accommodate the new column format.

Keep in mind, I haven't really used extjs, so if my understanding is off, please correct me.

Let's take a look at an example with extjs vs dynatable. Let's say you have a small table like this in your HTML:

<table id="my-table">
  <thead><tr><th>Notice Period></th><th>Email Address</th></tr></thead>
  <tbody></tbody>
</table>

h3. ExtJS

Keep in mind, extjs does some things dynatable doesn't do, so I only included the relevant code to accomplish with extjs what dynatable does do.

// ...
columns:[{
        text: 'Notice period',
        dataIndex: 'noticePeriod',
        groupable: false,
        filter: {
            type: 'list'
        }
    }, {
        text: 'Email address',
        dataIndex: 'email',
        width: 200,
        groupable: false,
        renderer: function(v) {
            return '<a href="mailto:' + v + '">' + v + '</a>';
    }]
// ...

And now, let's say you want to switch the order of the two columns and add a third column between them. You'd first need to change the HTML so that the columns are switched and the new one is added:

<table id="my-table">
  <thead><tr><th>Email Address</th><th>Name</th><th>Notice Period></th></tr></thead>
  <tbody></tbody>
</table>

And then change your JS to also switch the order of the columns and add the new one:

// ...
columns:[{
        text: 'Email address',
        dataIndex: 'email',
        width: 200,
        groupable: false,
        renderer: function(v) {
            return '<a href="mailto:' + v + '">' + v + '</a>';
        }
    }, {
        text: 'Name',
        dataIndex: 'name',
        groupable: false,
        filter: {
            type: 'list'
        }
    }, {
        text: 'Notice period',
        dataIndex: 'noticePeriod',
        groupable: false,
        filter: {
            type: 'list'
        }
    }]
// ...

h3. Dynatable

To start off with dynatable, you'd do this:

// Don't need to specify columns since dynatable figures it out from your HTML markup.
// We'll just do the same renderer for the mailto link as with the extjs example.
$('#my-table').dynatable({
  writers: {
    emailAddress: function(v) {
      return '<a href="mailto:' + v.email + '">' + v.email + '</a>';
    }
});

Now let's try making that same change again, where we switch the order of the columns and add a third column between the two, at the second index, but with Dynatable. First, we'll change the HTML again:

<table id="my-table">
  <thead><tr><th>Email Address</th><th>Name</th><th>Notice Period></th></tr></thead>
  <tbody></tbody>
</table>

Now, we're done. No change in the JavaScript necessary. Our presentation is mostly decoupled from our JS logic (I say "mostly" because of that email address render function, which I'm not sure we can effectively get rid of).

h3. However...

All that being said, I'm not experienced with extjs, so it could be that you're simply asking if it's possible to define your column structure either in the HTML or the JS, but not both (is that how extjs does it)? If that's the case, then you're right, dynatable requires you to specify the column structure in the HTML by default. I wanted to go ahead and write all this out so it's clear why that is.

If you wanted to be able to have an arbitrary column structure by not specifying it at all in your HTML, and to specify how each row gets written explicitly (akin to how you manually configure each column and their order in extjs), then you can certainly do that with dynatable by redefining the default row writer function:

$('#my-table').dynatable({
  writers: {
    _rowWriter: function(rowIndex, record, columns) {
      return '<tr><td>' + record.email + '</td><td>' + record.noticePeriod + '</td></tr>';
    }
  });

Of course, the above is just a function that returns an HTML string to concatenate and append to the table body, so it could be as complex as it needs to be. The columns argument in the above would be empty since you aren't defining your column headers in the HTML, but you don't necessarily need it if your function manually builds the HTML however it wants from the object.

You would also lose the automatic column header sorting functionality with the above.

JangoSteve commented 10 years ago

By the way, having explained all that, I think we could make a simple change to dynatable which would achieve what you want. Right now, you can configure which row dynatable uses to extract the columns to associate with the records:

$('#my-table').dynatable({
  table: {
    headRowSelector: 'thead tr'
  }
 });

And then dynatable always grabs the $(headRowSelector).children('th,td') elements for the columns.

We could instead make it so that you can pass in the direct column selector:

$('#my-table').dynatable({
  table: {
    columnSelector: 'thead tr th:not([colspan])'
  }
 });

And your HTML would look like this:

<table id="my-table">
  <thead>
    <tr>
      <th rowspan="2" data-dynatable-column="c1">c1</th>
      <!-- Since the c2 column doesn't actually map to an attribute, we don't need the data-dynatable-column attribute //-->
      <th colspan="2">c2</th>
      <th rowspan="2" data-dynatable-column="c3">c3</th>
      <th rowspan="2" data-dynatable-column="c4">c4</th>
    </tr>
    <tr>
      <th data-dynatable-column="s1">s1</th>
      <th data-dynatable-column="s2">s2</th>
    </tr>
  </thead>
  <tbody>
  </tbody>
</table>

Note in the above example, you don't actually need all those data-dynatable-column attributes, if the value of the attribute matches the cell text. I left it in there, because I'm assuming it was just for example purposes and the attribute names may not match the column header text.

gowhari commented 10 years ago

It's very good and quite better than specifying column orders. Even in the more complex cases user can use another selector than works.

c1 c2 c3 c4 c5
s1 s2
t1 t2
JangoSteve commented 10 years ago

Sweet, I think that sounds like a plan. I'll put this in the queue.

brandondrew commented 10 years ago

@gowhari I assume you're looking for something kind of like this http://jsfiddle.net/brandonzylstra/47j3C/3/ but where the Dynatable version has data matching the static version.

@JangoSteve how about if we could just mark some column headers to be ignored, like for example with something like data-dynatable-ignore='true' Then those <th> elements would be skipped.

JangoSteve commented 10 years ago

@brandondrew You can kind of already do that. You would be able to include data-dynatable-ignore on the headers and then call dynatable with this configuration if we had this suggestion implemented from another issue:

$('#my-example').dynatable({
  table: {
    columnSelector: 'thead tr th:not([data-dynatable-ignore])'
  }
});

So maybe that's another point for that feature request.

vertexclique commented 10 years ago

Any improvement on this issue? I am getting data with ajax support of dynatable and I am suffering from this.

gabriellupu commented 7 years ago

table.settings.columnSelector is no longer supported in dynatable 0.3.1. In order to fix this, in my case, I used the following workaround: added a hidden row that contains the actual columns, and select it using table.settings.headRowSelector:

<table id="my-table">
  <thead>
    <tr>
      <th rowspan="2">c1</th>
      <th colspan="2">c2</th>
    </tr>
    <tr>
      <th>s1</th>
      <th>s2</th>
    </tr>
    <!-- Will be used for a proper column rendering order -->
    <tr class="actual-header-columns" style="display: none;">
        <th data-dynatable-column="c1"></th>
        <th data-dynatable-column="s1"></th>
        <th data-dynatable-column="s2"></th>
    <tr>
  </thead>
  <tbody>
  </tbody>
</table>
$('#my-table').dynatable({
  table: {
    headRowSelector: 'thead tr.actual-header-columns'
  }
);