1

I have a datatable that is displaying 6 total columns of data from my database. The 7th column is a "+" button that expands down and displays a further 7 pieces of data about that specific entry. I am also using Codeigniter for my framework. Originally I was using colspan to make the row hidden and squeeze down, and found out that was a no-no, so I looked into Datatables child rows here: https://datatables.net/examples/api/row_details.html but it appears that the data needs to be there after the generation of the table.

I have the HTML/PHP to generate the table and populate it with data now. The data is sent from my model to controller and to this view:

<div class="table-responsive">
    <table class="table table-striped table-bordered table-hover" id="dataTables-listlg">
        <thead>
            <th>ItemID</th>
            <th>Name</th>
            <th>Quantity</th>
            <th>Identified?</th>
            <th>Item Type</th>
            <th>Unique ID</th>
            <th>Details</th>
        </thead>
        <tbody>
            <?php foreach ($storage_items as $storageItem) { ?>
                <tr>
                    <td><?php echo $storageItem['nameid']; ?></td>
                    <td><?php echo $storageItem['name']; ?></td>
                    <td><?php echo $storageItem['amount']; ?></td>
                    <td><?php echo $storageItem['identify']; ?></td>
                    <td><?php echo $item_types[$storageItem['type']]; ?></td>
                    <td><?php echo $storageItem['unique_id']; ?></td>
                    <td><center><a data-toggle="collapse" data-parent="#accordion" href="#storagedetails<?php echo $storageItem['id']; ?>"><button type="button" class="btn btn-primary btn-circle btn-sm"><i class="fa fa-plus"></i></button></a></center></td>
                    <td>
                    <?php   echo form_open('/account/edititem', array('class' => 'form-inline'), array('id' => $storageItem['id'], 'item_loc' => "inventory", 'acctid' => $acct_data->account_id)); ?>
                    </td>
                </tr>
                <tr>
                    <td colspan="8" class="hiddenRow">
                        <div id="storagedetails<?php echo $storageItem['id']; ?>" class="panel-collapse collapse">
                            <div class="panel-body">
                                <div class="row">
                                    <div class="col-xs-2">
                                        <strong>Refine level:</strong>&nbsp;<input type="number" name="refine" class="form-control" value="<?php echo $storageItem['refine']; ?>" <?php if ($storageItem['type'] != 4 && $storageItem['type'] != 5) { echo "readonly"; } ?> />
                                    </div>
                                    <div class="col-xs-2">
                                        <strong>Broken?:</strong>&nbsp;<input type="checkbox" name="attribute" class="form-control" value="1" <?php if ($storageItem['attribute'] == 1) { echo "checked"; } if ($storageItem['type'] != 4 && $storageItem['type'] != 5) { echo "disabled"; } ?> />
                                    </div>
                                    <div class="col-xs-2">
                                        <strong>Bound?:</strong>&nbsp;<input type="checkbox" name="bound" class="form-control" value="1" <?php if ($storageItem['bound'] == 1) { echo "checked"; } ?> />
                                    </div>
                                </div>
                                <br />
                                <div class="row">
                                    <div class="col-xs-2">
                                        <strong>Card 1:</strong>&nbsp;<input type="number" name="card0" class="form-control" value="<?php echo $storageItem['card0']; ?>" <?php if ($storageItem['type'] != 4 && $storageItem['type'] != 5) { echo "readonly"; } ?> /></br>
                                    </div>
                                    <div class="col-xs-2">
                                        <strong>Card 2:</strong>&nbsp;<input type="number" name="card1" class="form-control" value="<?php echo $storageItem['card1']; ?>" <?php if ($storageItem['type'] != 4 && $storageItem['type'] != 5) { echo "readonly"; } ?> /></br>
                                    </div>
                                    <div class="col-xs-2">
                                        <strong>Card 3:</strong>&nbsp;<input type="number" name="card2" class="form-control" value="<?php echo $storageItem['card2']; ?>" <?php if ($storageItem['type'] != 4 && $storageItem['type'] != 5) { echo "readonly"; } ?> /></br>
                                    </div>
                                    <div class="col-xs-2">
                                        <strong>Card 4:</strong>&nbsp;<input type="number" name="card3" class="form-control" value="<?php echo $storageItem['card3']; ?>" <?php if ($storageItem['type'] != 4 && $storageItem['type'] != 5) { echo "readonly"; } ?> /></br>
                                    </div>
                                </div>
                                <?php echo form_close(); ?>
                            </div>
                        </div>
                    </td>
                </tr>
            <?php } ?>
        </tbody>
    </table>
</div>

The javascript I'm using now to make the table is:

<script>
$(document).ready(function() {
    $('#dataTables-listlg').DataTable({
        "responsive": true,
        "lengthMenu": [ [25, 50, 100, -1], [25, 50, 100, "All"] ],
        "searching": false,
        "defaultContent": "",
    });
});
</script>

Where the <td colspan="8" class="hiddenRow"> is, is where I want to make the child row drop down (UNDER the entry showing the additional information). I've seen the example but I have no idea how to get my data into the proper format to put it into the datatable and where it should go. Here is the controller with the relevant part:

$data['storage_items'] = $this->accountmodel->get_storage_items($aid);
$this->load->view('account/details',$data);
$this->load->view('footer'); // Where the javascript is above

And the model:

function get_storage_items($aid) {
    $this->db->select('*');
    $this->db->from('storage')->order_by('storage.id', 'asc');
    $this->db->where('storage.account_id', $aid); // This is just sorting out the results from that specific account
    $q = $this->db->get();
    return $q->result_array();
}

It looks like I need to get my array of results from my model into json/ajax but have no idea how I'd get this all the way to my footer AFTER the table is generated. Any help you can provide would be appreciated.

---Edit--- After looking over the answer below and mulling over thoughts, I have changed things to the following. Here is the complete view from foreach loop to the end, including the Javascript to put stuff in the 'content' array:

<div class="table-responsive">
    <table class="table table-striped table-bordered table-hover display" id="dataTables-listlg">
        <thead>
            <th>ItemID</th>
            <th>Name</th>
            <th>Quantity</th>
            <th>Identified?</th>
            <th>Item Type</th>
            <th>Unique ID</th>
            <th>Details</th>
            <th>Options</th>
        </thead>
        <tbody>
            <?php foreach ($storage_items as $storageItem) { ?>
                <tr>
                    <td><?php echo $storageItem['nameid']; ?></td>
                    <td><?php echo $storageItem['name']; ?></td>
                    <td><?php echo $storageItem['amount']; ?></td>
                    <td><?php echo $storageItem['identify']; ?></td>
                    <td><?php echo $item_types[$storageItem['type']]; ?></td>
                    <td><?php echo $storageItem['unique_id']; ?></td>
                    <td class="details-control"></td>
                    <td>
                        <button type="submit" class="btn btn-success btn-sm <?php if ($check_perm['editstorageitem'] == 0) { echo "disabled"; } ?>" >Edit</button>&nbsp;
                        <button type="button" class="btn btn-danger btn-sm <?php if ($check_perm['editstorageitem'] == 0) { echo "disabled"; } ?>">Delete</button>
                    </td>
                </tr>
                <script>
                    var content = [];
                    content[<?php echo $storageItem["id"]; ?>] = '"'<?php echo form_open("/account/edititem", array("class" => "form-inline"), array("id" => $storageItem["id"], "item_loc" => "inventory", "acctid" => $acct_data->account_id)); ?> \
                            <div class="panel-body"> \
                                <div class="row"> \
                                    <div class="col-xs-2"> \
                                        <strong>Refine level:</strong>&nbsp;<input type="number" name="refine" class="form-control" value="<?php echo $storageItem["refine"]; ?>" <?php if ($storageItem["type"] != 4 && $storageItem["type"] != 5) { echo "readonly"; } ?> /> \
                                    </div> \
                                    <div class="col-xs-2"> \
                                        <strong>Broken?:</strong>&nbsp;<input type="checkbox" name="attribute" class="form-control" value="1" <?php if ($storageItem["attribute"] == 1) { echo "checked"; } if ($storageItem["type"] != 4 && $storageItem["type"] != 5) { echo "disabled"; } ?> /> \
                                    </div> \
                                    <div class="col-xs-2"> \
                                        <strong>Bound?:</strong>&nbsp;<input type="checkbox" name="bound" class="form-control" value="1" <?php if ($storageItem["bound"] == 1) { echo "checked"; } ?> /> \
                                    </div> \
                                </div> \
                                <br /> \
                                <div class="row"> \
                                    <div class="col-xs-2"> \
                                        <strong>Card 1:</strong>&nbsp;<input type="number" name="card0" class="form-control" value="<?php echo $storageItem["card0"]; ?>" <?php if ($storageItem["type"] != 4 && $storageItem["type"] != 5) { echo "readonly"; } ?> /></br> \
                                    </div> \
                                    <div class="col-xs-2"> \
                                        <strong>Card 2:</strong>&nbsp;<input type="number" name="card1" class="form-control" value="<?php echo $storageItem["card1"]; ?>" <?php if ($storageItem["type"] != 4 && $storageItem["type"] != 5) { echo "readonly"; } ?> /></br> \
                                    </div> \
                                    <div class="col-xs-2"> \
                                        <strong>Card 3:</strong>&nbsp;<input type="number" name="card2" class="form-control" value="<?php echo $storageItem["card2"]; ?>" <?php if ($storageItem["type"] != 4 && $storageItem["type"] != 5) { echo "readonly"; } ?> /></br> \
                                    </div> \
                                    <div class="col-xs-2"> \
                                        '<strong>Card 4:</strong>&nbsp;<input type="number" name="card3" class="form-control" value="<?php echo $storageItem["card3"]; ?>" <?php if ($storageItem["type"] != 4 && $storageItem["type"] != 5) { echo "readonly"; } ?> /></br> \
                                    </div> \
                                </div> \
                            <?php echo form_close(); ?> \
                        </div>'"';
                </script>
                <tr item_id="<?php echo $storageItem['id']; ?>">
                </tr>
            <?php } ?>
        </tbody>
    </table>
</div>

and my javascript in the footer looks like this now:

<script>
$(document).ready(function() {
    var table = $('#dataTables-listlg').DataTable({
        "responsive": true,
        "lengthMenu": [ [25, 50, 100, -1], [25, 50, 100, "All"] ],
        "searching": false,
        "defaultContent": "",
    });

// Add event listener for opening and closing details
    $('#dataTables-listlg tbody').on('click', 'td.details-control', function () {
        var tr = $(this).closest('tr');
        var row = table.row( tr );

        if ( row.child.isShown() ) {
            // This row is already open - close it
            row.child.hide();
            tr.removeClass('shown');
        }
        else {
            // Open this row
            row.child(content[tr.attr('item_id')]).show();
            tr.addClass('shown');
        }
    });
 });
</script>

In fact, the 'unique_id' in this table is not as "unique" as I thought (I didn't design it, just writing the backend for it). There is however a unique key on the table 'id', so that's what I'm using to make sure I get the right value.

This however is not working. I am getting an error in the console:

SyntaxError: missing ; before statement - appears on the line that starts content[<?php echo $storageItem["id"]; ?>] =... in each iteration of the foreach

I'm also getting the error from the webpage warning about DataTables: DataTables warning: table id=dataTables-listlg - Requested unknown parameter '0' for row 1. For more information about this error, please see http://datatables.net/tn/4

Additionally, the child row doesn't drop down. Did I misunderstand or make a silly mistake?

1 Answer 1

1

dataTables child rows dynamically injects a row on the form

<tr colspan="number of columns">
   <td>
      ... user content ... 
   </td>
</tr>

user content is passed to the injected row upon the call (from the example) :

row.child(<user content>).show();

You cannot use hidden rows as a base for child rows. I would suggest you collect all the content you output to hidden rows in the loop into a javascript array :

var content = [];

content[<? echo $storageItem['unique_id']; ?>] = '"'+<? echo form_open(...) + everything from your .hiddenRow. Now follow the example, populate each <tr> with the unique_id

<tr uniqueId="<? echo $storageItem['unique_id']; ?>">

dedicate one column to activate the child row by adding a .details-control and instead of calling the function format() as in the example, do a

row.child(content[tr.attr('uniqueId')]).show();

Update. Jguy, what about

1) fist make your code to work only by inserting simple content. For example row.child('hello').show(); By that you will know that part is working.

2) Then, build up the content array separetely. You do not have to do it all in the same foreach(). This may improve the chance of success if you split the things up in "logical" operations.

3) remember to set an item_id on the <tr>'s

4) your dataTables version cannot be outdated in this context as long as you are using 1.10.x

Sign up to request clarification or add additional context in comments.

7 Comments

So if I read your answer correctly, inside my foreach loop I should have all the stuff inside the <td colspan="8" class="hiddenRow"> </td> tags in a javascript array with the key being <? echo $storageItem['unique_id']; ?>? So in other words, like this: var content = [<? echo $storageItem['unique_id']; ?> : '"' and then everything from the <td hiddenRow> tag?
@Jguy, you should output everything you now are outputting to the hidden row (including the <form> opener in the parent row, if you want to make stable code) into strings you assign to an array. And then uses that array as the source for the row.child() content.
I got what you were trying to point out, thanks. I've edited my question reflecting new code per the information from your answer. any additional assistance you can provide would be appreciated.
@Jguy, I cannot test it of course - but it looks exactly as what I had in mind, except you should move var content = []; outside the loop. As it is now you will end up have a content only holdning the very last item, because you redeclare it over and over. Also, you lack to populate the <tr>'s with the item_id you are referring to in the click-handler. But otherwise it looks great, as far as I can tell (with reservation for having overlooked something)
I thought perhaps my DataTables was out of date, so I went ahead and updated it. I just noticed when I click on my expand button on my column, I get TypeError: row.child(...) is undefined on the line with row.child(content[tr.attr('item_id')]).show();. I've tried both the CDN copy of DataTables (which fails completely, DataTables doesn't even load) and my own local copy (which loads DataTables) but then I get this error.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.