replicating Parent / child editing in child rows blog

replicating Parent / child editing in child rows blog

cpshartcpshart Posts: 246Questions: 49Answers: 5

Link to test case:
Debugger code (debug.datatables.net):
Error messages shown:
Description of problem:

I am trying to replicate the Parent / child editing in child rows blog on my server.

https://datatables.net/blog/2019-01-11

The section of the client file commented out results in an error when enabled i.e. uncommented.

    var siteTable = $("#sites").DataTable({
        dom: "Bfrtip",
        ajax: "../../Editor-PHP-1.9.4/controllers/sites.php",
        order: [1, "asc"],
        columns: [
/*          {
                className: "details-control",
                orderable: false,
                data: null,
                defaultContent: "",
                width: "10%"
            },
*/          
            { data: "name" },
            {
                data: "users",
                render: function(data) {
                    return data.length;
                }
            }
        ],

the error is as follows

jquery-3.3.1.js:3827 Uncaught TypeError: Cannot read property 'style' of undefined
    at Fa (jquery.dataTables.min.js:62)
    at ha (jquery.dataTables.min.js:48)
    at e (jquery.dataTables.min.js:93)
    at HTMLTableElement.<anonymous> (jquery.dataTables.min.js:93)
    at Function.each (jquery-3.3.1.js:354)
    at jQuery.fn.init.each (jquery-3.3.1.js:189)
    at jQuery.fn.init.n [as dataTable] (jquery.dataTables.min.js:83)
    at jQuery.fn.init.h.fn.DataTable (jquery.dataTables.min.js:165)
    at HTMLDocument.<anonymous> ((index):472)

I have created the client file as shown below, the server files are exactly as shown in blog users.php and sites.php.

I have added the CSS and pointed to my server

/* Paren Child CSS */
td.child-table {
    background-color: #bfdbff;
}
td.details-control {
    background: url('https://dividendlook.co.uk/Editor-PHP-1.9.4/examples/resources/details_open.png') no-repeat center center;
    cursor: pointer;
}
tr.shown td.details-control {
    background: url('https://dividendlook.co.uk/Editor-PHP-1.9.4/examples/resources/details_open.png') no-repeat center center;
}

Any help in this would be appreciated. I can provide access to my system via PM to investigate if required, with thanks.

To invoke the script

https://www.dividendlook.co.uk/testparentchilde/

PHP script below

https://www.dividendlook.co.uk/wp-admin/post.php?post=25438&action=edit

Best Regards

Colin

Client File below

<head>
<title>Sites Users</title>
<!-- 
https://editor.datatables.net/examples/advanced/parentChild
https://datatables.net/blog/2016-03-25
-->
<!-- Basic Datatables Editor Initilisation -->
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css" />
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/buttons/1.5.6/css/buttons.dataTables.min.css" />
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/select/1.3.0/css/select.dataTables.min.css" />
<link rel="stylesheet" type="text/css" href="https://www.dividendlook.co.uk/Editor-PHP-1.9.4/css/editor.dataTables.min.css" />
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/css/select2.min.css">

<!-- Basic Datatables Editor Initilisation -->
<script type="text/javascript"   src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script type="text/javascript"   src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
<script type="text/javascript"   src="https://cdn.datatables.net/buttons/1.5.4/js/dataTables.buttons.min.js"></script>
<script type="text/javascript"   src="https://cdn.datatables.net/select/1.3.0/js/dataTables.select.min.js"></script>
<script type="text/javascript"   src="https://www.dividendlook.co.uk/Editor-PHP-1.9.4/js/dataTables.editor.js"></script>    
<script type="text/javascript"   src="https://cdn.datatables.net/responsive/2.2.3/js/dataTables.responsive.min.js"></script>
<script type="text/javascript"   src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/js/select2.min.js"></script> 

</head>

<table id="sites" class="display">
        <thead>
            <tr>
                <th>Name</th>
                <th>Users</th>
            </tr>
        </thead>
<tbody>     
<?php
global $wpdb;    
global $current_user;
get_currentuserinfo();
$user_id = $current_user->ID;
    
$rows = $wpdb->get_results("
SELECT 
sites.name AS site,
'1' AS users,
country.name AS country,
first_name AS firstname,
last_name AS lastname,
phone AS phone,
FROM
users
INNER JOIN sites ON (sites.id = users.site) 
INNER JOIN country ON (country.id = sites.country_id)
");
        
foreach ($rows as $row ){
    echo "<tr>";
    echo "<td>$row->site</td>";
    echo "<td>$row->users</td>";
    echo "</tr>";
}

<?php
>


?>


<table id="users" class="display">
        <thead>
            <tr>
                <th>First name</th>
                <th>Last name</th>
                <th>Phone #</th>
                <th>Location</th>
            </tr>
        </thead>
<tbody>     
<?php
global $wpdb;    
global $current_user;
get_currentuserinfo();
$user_id = $current_user->ID;
    
$rows = $wpdb->get_results("
SELECT 
sites.name AS site,
'1' AS users,
first_name AS firstname,
last_name AS lastname,
phone AS phone
FROM
users
INNER JOIN sites ON (sites.id = users.site) 
");
        
foreach ($rows as $row ){
    echo "<tr>";
    echo "<td>$row->firstname</td>";
    echo "<td>$row->lastname</td>";
    echo "<td>$row->phone</td>";
    echo "<td>$row->location</td>";
    echo "</tr>";
}

<?php
>
        

?>


<input type='hidden' id='passuserid' value='<?php echo $current_user->ID; ?>'>
    
<script type="text/javascript">
(function($) {
var editor; // use a global for the submit and return data rendering in the examples
 
$(document).ready(function() {
function createChild(row) {
    var rowData = row.data();

    // This is the table we'll convert into a DataTable
    var table = $('<table class="display" width="100%"/>');

    // Display it the child row
    row.child(table).show();

    // Editor definition for the child table
    var usersEditor = new $.fn.dataTable.Editor({
        ajax: {
            url: "../../Editor-PHP-1.9.4/controllers/users.php",
            data: function(d) {
                d.site = rowData.id;
            }
        },
        table: table,
        fields: [
            {
                label: "First name:",
                name: "users.first_name"
            },
            {
                label: "Last name:",
                name: "users.last_name"
            },
            {
                label: "Phone #:",
                name: "users.phone"
            },
            {
                label: "Site:",
                name: "users.site",
                type: "select",
                placeholder: "Select a location",
                def: rowData.id
            }
        ]
    });

    // Child row DataTable configuration, always passes the parent row's id to server
    var usersTable = table.DataTable({
        dom: "Bfrtip",
        pageLength: 5,
        ajax: {
            url: "../../Editor-PHP-1.9.4/controllers/users.php",
            type: "post",
            data: function(d) {
                d.site = rowData.id;
            }
        },
        columns: [
            { title: "First name", data: "users.first_name" },
            { title: "Last name", data: "users.last_name" },
            { title: "Phone #", data: "users.phone" },
            { title: "Location", data: "sites.name" }
        ],
        select: true,
        buttons: [
            { extend: "create", editor: usersEditor },
            { extend: "edit", editor: usersEditor },
            { extend: "remove", editor: usersEditor }
        ]
    });

    // On change, update the content of the parent table's host row
    // This isn't particularly efficient as it requires the child row
    // to be regenerated once the main table has been reloaded. A
    // better method would be to query the data for the new user counts
    // and update each existing row, but that is beyond the scope of
    // this post.
    usersEditor.on( 'submitSuccess', function (e, json, data, action) {
        row.ajax.reload(function () {
            $(row.cell( row.id(true), 0 ).node()).click();
        });
    } );
}

function updateChild(row) {
    $("table", row.child())
        .DataTable()
        .ajax.reload();
}

function destroyChild(row) {
    // Remove and destroy the DataTable in the child row
    var table = $("table", row.child());
    table.detach();
    table.DataTable().destroy();

    // And then hide the row
    row.child.hide();
}
    
    
$(document).ready(function() {
    var siteEditor = new $.fn.dataTable.Editor({
        ajax: "../../Editor-PHP-1.9.4/controllers/sites.php",
        table: "#sites",
        fields: [
            {
                label: "Site name:",
                name: "name"
            }
        ]
    });

    var siteTable = $("#sites").DataTable({
        dom: "Bfrtip",
        ajax: "../../Editor-PHP-1.9.4/controllers/sites.php",
        order: [1, "asc"],
        columns: [
            {
                className: "details-control",
                orderable: false,
                data: null,
                defaultContent: "",
                width: "10%"
            },
//*/            
            { data: "name" },
            {
                data: "users",
                render: function(data) {
                    return data.length;
                }
            }
        ],
        select: {
            style: "os",
            selector: "td:not(:first-child)"
        },
        buttons: [
            { extend: "create", editor: siteEditor },
            { extend: "edit", editor: siteEditor },
            { extend: "remove", editor: siteEditor }
        ]
    });

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

        if (row.child.isShown()) {
            // This row is already open - close it
            destroyChild(row);
            tr.removeClass("shown");
        } else {
            // Open this row
            createChild(row);
            tr.addClass("shown");
        }
    });

    // When updating a site label, we want to update the child table's site labels as well
    siteEditor.on("submitSuccess", function() {
        siteTable.rows().every(function() {
            if (this.child.isShown()) {
                updateChild(this);
            }
        });
    });
});

});
    
}(jQuery));</script>

This question has an accepted answers - jump to answer

Answers

  • kthorngrenkthorngren Posts: 21,343Questions: 26Answers: 4,954
    Answer ✓

    Uncaught TypeError: Cannot read property 'style' of undefined

    This is usually a mismatch of the number of columns defined in your HTML table and the number in Datatables. You have this in your table:

    <table id="sites" class="display">
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Users</th>
                </tr>
            </thead>
    

    But in Datatables you have:

            columns: [
                {
                    className: "details-control",
                    orderable: false,
                    data: null,
                    defaultContent: "",
                    width: "10%"
                },
    //*/           
                { data: "name" },
                {
                    data: "users",
                    render: function(data) {
                        return data.length;
                    }
                }
            ],
    

    Looks like you need to define a column for the details-control.

    Kevin

  • cpshartcpshart Posts: 246Questions: 49Answers: 5

    Hi Kevin

    Thanks for your quick response, that does make sense now to define a column for the details_open.png file icon.

    I will take a look and get back to you.

    Best Regards Colin

  • cpshartcpshart Posts: 246Questions: 49Answers: 5

    Hi Kevin

    All working now, great, so now I will build the equivalent for my website, changes to the client script shown below

    <table id="sites" class="display">
            <thead>
                <tr>
                    <th></th>
                    <th>Name</th>
                    <th>Users</th>
                </tr>
            </thead>
    <tbody>     
    <?php
    global $wpdb;    
    global $current_user;
    get_currentuserinfo();
    $user_id = $current_user->ID;
        
    $rows = $wpdb->get_results("
    SELECT 
    sites.id AS id,
    sites.name AS site,
    '1' AS users,
    country.name AS country,
    first_name AS firstname,
    last_name AS lastname,
    phone AS phone,
    FROM
    users
    INNER JOIN sites ON (sites.id = users.site) 
    INNER JOIN country ON (country.id = sites.country_id)
    ");
            
    foreach ($rows as $row ){
        echo "<tr>";
        echo "<td></td>";
        echo "<td>$row->site</td>";
        echo "<td>$row->users</td>";
        echo "</tr>";
    }
    
    <?php
    >
    
    
    ?>
    
    
    

    also useful reading and HTML example on the following link

    https://datatables.net/examples/api/row_details.html

    thanks again.

    Best Regards

    Colin

This discussion has been closed.