Displaying multiple images in child rows?

Displaying multiple images in child rows?

hamlet1964hamlet1964 Posts: 20Questions: 4Answers: 1

I'm hacking away at potential solutions, coming up empty. I'm trying to load multiple images into a child row and cannot figure it out. I'd like to preserve the functionality present in the Editor (handling of PDFs vs. JPGs). Any advice much appreciated!!!!

HTML:

var editor; // use a global for the submit and return data rendering in the examples
 
$(document).ready(function() {
    editor = new $.fn.dataTable.Editor( {
        ajax: "examples/php/upload-many.php",
        table: "#example",
        fields: [ {
                label: "date:",
                name: "stuff.date",
                type: "datetime",
                def:   function () { return new Date();
                }
            }, {
                label: "category",
                name: "stuff.category",
                type: "select"
            }, {
                label: "person:",
                name: "stuff.person",
                type: "select"
            }, {
                label: "notes:",
                name: "stuff.notes"
            }, {
                label: "files:",
                name: "files[].id",
                type: "uploadMany",
                display: function ( fileId, counter ) 
                {
                    if (fileId !== null) {
                    var ext = editor.file('files', fileId).filetype.toLowerCase();
                    if (ext == "pdf") {
                        return '<a href ="' + editor.file('files', fileId).web_path + '" target=_blank><i class ="fa fa-file-pdf-o fa-3x"></i>' + '</center></a>';
                        }
                    
                    if ((ext == "png") || (ext == "jpg") || (ext == "gif")) {
                    
                    return fileId ?
                        '<img src="'+editor.file( 'files', fileId ).web_path+'" class="img-circle" width="60" height="60"/>' :
                        null;
                }
                }},
                clearText: "Clear",
                noFileText: 'No images' 
            }
        ]
    } );
 function format ( d ) {
     console.info(d);
    return ('<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">' +
                '<tr>' +
                       '<tr>' +
                    '<td>Extra info:</td>' +
                    '<td>And any further details here (images etc)...</td>' +
                '</tr>' + 
            '</table>'); 
    
}

    var table = $('#example').DataTable( {
        scrollY:        '60vh',
        scrollCollapse: true,
        paging:         false,
        select: { style: 'single',
                         selector: 'td:not(:first-child)'
                        },
        responsive: { 
            "details": {
                        "type": 'column',
                        "target": 'tr'
        }
        },
        dom: "Bfrtip",
        order: [ 1, 'desc' ],
        ajax: "examples/php/upload-many.php",
        columns: [
             {
                "className":      'details-control',
                "orderable":      false,
                "data":           null,
                "defaultContent": '',
                "render": function () {
                    return '<i class="fa fa-plus-square" aria-hidden="true"></i>';
                        },
                    width: "15px"
            },
            { "className": "dt-right", data: "stuff.date" },
            { "className": "dt-right", data: "persons.person" },
            { "className": "dt-right", data: "categories.category" },
            { "className": "dt-right", data: "stuff.notes" },
            {
                "className": "dt-right", data: "files[].id",
                render: function ( d ) {
                    return d.length ?
                        d.length+' image(s)' :
                        'No image';
                },
                title: "Image"
            }
        ],
        select: true,
        buttons: [
            { extend: "create", editor: editor },
            { extend: "edit",   editor: editor },
            { extend: "remove", editor: editor,
                formMessage: function ( e, dt ) {
                    var rows = dt.rows( e.modifier() ).data().pluck('notes');
                    return 'Are you sure you want to delete the entries for the '+
                        'following record(s)? <ul><li>'+rows.join('</li><li>')+'</li></ul>';
                } 
            }
        ],
            initComplete: function () {
                // Add event listener for opening and closing details
                $('#example tbody').on('click', 'td.details-control', function () {
                    var tr = $(this).closest('tr');
                    var tdi = tr.find("i.fa");
                    var row = table.row(tr);

                    if (row.child.isShown()) {
                        // This row is already open - close it
                        row.child.hide();
                        tr.removeClass('shown');
                        tdi.first().removeClass('fa-minus-square');
                        tdi.first().addClass('fa-plus-square');
                    }
                    else {
                        // Open this row
                        row.child(format(row.data())).show();
                        tr.addClass('shown');
                        tdi.first().removeClass('fa-plus-square');
                        tdi.first().addClass('fa-minus-square');
                    }              
    } );   
}
    } );
 $('#example').on("user-select", function (e, dt, type, cell, originalEvent) {
            
          if ($(cell.node()).hasClass("details-control")) {
                e.preventDefault();
            }
        });   
} );
    </script>
</head>

PHP:

Editor::inst( $db, 'stuff' )
    ->fields(
        Field::inst( 'stuff.date' ),
        Field::inst( 'stuff.notes' ),
        Field::inst( 'stuff.category' )
        ->options( Options::inst()
                ->table( 'categories' )
                ->value( 'id' )
                ->label( 'category' )
            ),
        Field::inst( 'categories.category'),
        Field::inst( 'stuff.person' )
            ->options( Options::inst()
                ->table( 'persons' )
                ->value( 'id' )
                ->label( 'person' )
            ),
        Field::inst( 'persons.person' )
    )
    ->join(
        Mjoin::inst( 'files' )
            ->link( 'stuff.id', 'stuff_files.stuff_id' )
            ->link( 'files.id', 'stuff_files.file_id' )
            ->fields(
                Field::inst( 'id' )
                    ->setFormatter( 'Format::ifEmpty', null )
                    ->upload( Upload::inst( $_SERVER['DOCUMENT_ROOT'].'/upload_test/__ID__.__EXTN__' )
                        ->db( 'files', 'id', array(
                            'filename'    => Upload::DB_FILE_NAME,
                            'filesize'    => Upload::DB_FILE_SIZE,
                            'web_path'    => Upload::DB_WEB_PATH,
                            'system_path' => Upload::DB_SYSTEM_PATH,
                            'filetype'    => Upload::DB_EXTN
                        ) )
                        ->validator( Validate::fileSize( 2000000, 'Files must be smaller that 2M' ) )
                        ->validator( Validate::fileExtensions( array( 'png', 'jpg', 'jpeg', 'gif', 'pdf' ), "Please upload an image" ) )
                    )
            )
    )
    ->leftJoin( 'categories', 'categories.id', '=', 'stuff.category' )
    ->leftJoin( 'persons', 'persons.id', '=', 'stuff.person' )
    ->process( $_POST )
    ->json();

This question has an accepted answers - jump to answer

Answers

  • allanallan Posts: 48,114Questions: 1Answers: 6,924 Site admin

    In your format function you get the d parameter which is the row data, so in this case it will have a files properties which you can loop over. For example:

    for ( var i=0 ; i<d.files.length ; i++ ) {
      console.log( 'File id:', files[i].id );
    }
    

    Then with the id you can use whatever method you want to use to look up information about the file - e.g. file() like you are in the image display function.

    Allan

  • hamlet1964hamlet1964 Posts: 20Questions: 4Answers: 1

    Thank you. When I incorporate the following into the HTML, I receive an "Uncaught SyntaxError: Unexpected token var?? Any thoughts on what I'm doing wrong?

    HTML

    function format ( d ) {
        return ('<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">' +
                    '<tr>' +
                           '<tr>' +
                {for ( var i=0 ; i<d.length ; i++ )
                                {
                        if (files[i].id !== null) { 
                        var ext = editor.file('files', files[i]).filetype.toLowerCase();
                        if (ext == "pdf") {
                            return '<a href ="' + editor.file('files', files[i]).web_path + '" target=_blank><i class ="fa fa-file-pdf-o fa-3x"></i>' + '</center></a>';
                            }
                        
                        if ((ext == "png") || (ext == "jpg") || (ext == "gif")) {
                        
                        return fileId ?
                            '<img src="'+editor.file( 'files', files[i] ).web_path+'" class="img-circle" width="60" height="60"/>' :
                            null;
                    }
                    }}
                } +
             '</tr>' +    
     '</table>');    
    }
    
  • allanallan Posts: 48,114Questions: 1Answers: 6,924 Site admin

    You can't have a for loop inside a string. That isn't valid Javascript. You'll need to build the string in the form loop and then insert it into the rest of the HTML.

    Allan

  • hamlet1964hamlet1964 Posts: 20Questions: 4Answers: 1

    Thanks!!

    So I moved the HTML inside the FOR STATEMENT:

     function format ( d ) {
        
        for ( var i=0 ; i<d.length ; i++ ) {
            
            console.log( 'File id:', files[i].id );      
                return ('<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">' +
                    '<tr>' +
                           '<tr>' +
                        '<td>Extra info:</td>' +
                        '<td>And any further details here (images etc)...</td>' +
                    '</tr>' +
                '</table>');
    }
     }
       
    

    I receive the following error message:

    Uncaught TypeError: Cannot read property 'show' of undefined

    The console identifies line 134 of my original HTML post as the error source. I'm hoping to resolve this basic code before moving to the images.

    Any thoughts???

  • kthorngrenkthorngren Posts: 3,709Questions: 19Answers: 828
    edited August 11

    I think what you want to do is use the for loop to build a concatenated string of rows then use that string to build the table. Something like this:

    function format ( d ) {
    
         var rows = '';
         for ( var i=0 ; i<d.files.length ; i++ ) {
         
            rows = rows + '<tr>'+
                '<td>File id:' + files[i].id + '</td>'+
            '</tr>';
    
         }
    
        return '<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">' + 
        rows +
        '</table>';
    }
    

    Haven't tired this so it might need some debugging but should get you started.

    Kevin

  • hamlet1964hamlet1964 Posts: 20Questions: 4Answers: 1

    Many thanks!! Yes, I've been able to get the loop to work correctly.

     function format ( d ) {
     
         var rows = '';
         var i;
       for ( i=1 ; i<(d.files.length+1) ; i++ ) {
          
            rows = rows + '<tr>'+
                '<td>File id:' + editor.file('files',id=i).web_path + '</td>'+
            '</tr>';
     
         }
        return ('<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">' +
        rows +
        '</table>');
    }
    

    I will keep tweeking to see if I can get the images to display properly.

  • hamlet1964hamlet1964 Posts: 20Questions: 4Answers: 1
    edited August 11

    I feel like I'm zeroing in on the final answer. Now the question is how do I get the looped results to sync with the database? Right now, it keeps looping over the same data items, regardless of row.

    Some database background:
    I have information spread across the following MySQL files:
    stuff
    files
    stuff_files

    So,
    stuff.id contains the datatables row number,
    files.id contains the file number that winds up being appended to the end of the file address,
    stuff_files is where the row number (stuff_files.stuff.id) identifies the files associated with that row (stuff_files.file_id).

    How do I configure this loop so that the proper files(s) are returned that match the selected child row?

    Here's the latest code (which does not work properly):

    function format ( d ) {
         var rows = '';
         var i;
         var ext1;
       for ( i=1 ; i<(d.files.length+1) ; i++ ) {
          
           ext1 = editor.file('files', id=i).filetype.toLowerCase();
                        if (ext1 == "pdf") {
                                   rows = rows + '<tr>'+
                '<a href ="' + editor.file('files', id=i).web_path + '" target=_blank><i class ="fa fa-file-pdf-o fa-3x"></i>' + '</center></a>'+
                
            '</tr>'; 
                    } 
                        if ((ext1 == "png") || (ext1 == "jpg") || (ext1 == "gif")) {
                         rows = rows + '<tr>'+
                            '<img src="'+editor.file( 'files', id=i ).web_path+'" class="img-circle" width="60" height="60"/>'+    
            '</tr>';
                    }
         }
        return ('<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">' +
        rows +
        '</table>');
    }
    
  • hamlet1964hamlet1964 Posts: 20Questions: 4Answers: 1
    edited August 11

    .........

  • hamlet1964hamlet1964 Posts: 20Questions: 4Answers: 1

    To assist with the last point, here's a look at the data in the browser:

  • kthorngrenkthorngren Posts: 3,709Questions: 19Answers: 828
    edited August 11 Answer ✓

    You have this:

       for ( i=1 ; i<(d.files.length+1) ; i++ ) {
           
           ext1 = editor.file('files', id=i).filetype.toLowerCase();
           .....
    

    More specifically you have id=i. i is the loop counter not the value of the data in d.files. Instead you need id=d.files[i] to get the value of that position in the array. You will need to change this in multiple places.

    Another thing you will need to do is start your for loop at 0 not 1: for ( i=0 ; i<(d.files.length+1) ; i++ ) {. The first element in an array is element 0. And remove the +1 for d.files.length+1.

    Something more like this:

       for ( i=0 ; i<(d.files.length) ; i++ ) {
           
           ext1 = editor.file('files', id=d.files[i]).filetype.toLowerCase();
           ....
    

    Let us know if this helps.

    Kevin

  • hamlet1964hamlet1964 Posts: 20Questions: 4Answers: 1

    Thanks. With one small change, it works. Here's a simplified version:

     function format ( d ) {
      
         var rows = '';
         var i;
       for ( i=0 ; i<(d.files.length) ; i++ ) { 
            rows = rows + '<tr>'+
                '<td>File:' + editor.file('files', id=d.files[i].id).web_path + '</td>'+
            '</tr>';
         }
        return ('<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">' +
        rows +
        '</table>');
    }
    

    Assuming I can get the complete version to work, I'll post that.

  • hamlet1964hamlet1964 Posts: 20Questions: 4Answers: 1

    Thanks for all the help!! I'm appending the complete HTML file so folks can see an example of how to incorporate multiple images into both child rows and the editor. The PHP remains unchanged from the original, above. The child rows need additional formatting, however, the code is functional.

    Thanks for your wonderful assistance!!

    HTML:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <link rel="shortcut icon" type="image/ico" href="http://www.datatables.net/favicon.ico">
        <meta name="viewport" content="initial-scale=1.0, maximum-scale=2.0">
        <title>Test</title>
        
         <?php require '../datatables_update/cdn_css.php';?>
    
        <link rel="stylesheet" type="text/css" href="../datatables_editor/css/editor.dataTables.min.css">
        <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
        <link rel="stylesheet" type="text/css" href="examples/resources/syntax/shCore.css">
        <link rel="stylesheet" type="text/css" href="examples/resources/demo.css">
        <style type="text/css" class="init">
            body {
        background-color: #F7F7F7;
    }
        table.right-all td,th{
        text-align :right;
            }
                td.details-control {
                text-align:center;
                color:forestgreen;
        cursor: pointer;
    }
    tr.shown td.details-control {
        text-align:center; 
        color:red;
    }
    
        </style>
         <?php require '../datatables_update/cdn_js.php';?>
    
        <script type="text/javascript" language="javascript" src="../datatables_editor/js/dataTables.editor.min.js">
        </script>
        <script type="text/javascript" language="javascript" src="examples/resources/syntax/shCore.js">
        </script>
        <script type="text/javascript" language="javascript" src="examples/resources/demo.js">
        </script>
        <script type="text/javascript" language="javascript" src="examples/resources/editor-demo.js">
        </script>
        
        <script type="text/javascript" language="javascript" src="https://cdn.datatables.net/plug-ins/1.10.15/api/sum().js">
        </script>
    
        <script type="text/javascript" language="javascript" class="init">
        
    var editor; // use a global for the submit and return data rendering in the examples
     
    $(document).ready(function() {
        editor = new $.fn.dataTable.Editor( {
            ajax: "examples/php/upload-many.php",
            table: "#example",
            fields: [ {
                    label: "date:",
                    name: "stuff.date",
                    type: "datetime",
                    def:   function () { return new Date();
                    }
                }, {
                    label: "category",
                    name: "stuff.category",
                    type: "select"
                }, {
                    label: "person:",
                    name: "stuff.person",
                    type: "select"
                }, {
                    label: "notes:",
                    name: "stuff.notes"
                }, {
                    label: "files:",
                    name: "files[].id",
                    type: "uploadMany",
                    display: function ( fileId, counter ) 
                    {
                        if (fileId !== null) {
                        var ext = editor.file('files', fileId).filetype.toLowerCase();
                        if (ext == "pdf") {
                            return '<a href ="' + editor.file('files', fileId).web_path + '"i class ="fas fa-file-pdf fa-3x"></i>' + '</center></a>';
                            }
                        
                        if ((ext == "png") || (ext == "jpg") || (ext == "gif")) {
                        
                        return fileId ?
                            '<img src="'+editor.file( 'files', fileId ).web_path+'" class="img-circle" width="60" height="60"/>' :
                            null;
                    }
                    }},
                    clearText: "Clear",
                    noFileText: 'No images' 
                }
            ]
        } );
    
     function format ( d ) {
         var rows = '';
         var i;
         var ext1;
       for ( i=0 ; i<(d.files.length) ; i++ ) { 
          ext1 = editor.file('files', id=d.files[i].id).filetype.toLowerCase(); 
           if (ext1 =="pdf") {
               rows = rows + '<tr>'+
                '<a href ="' + editor.file('files', id=d.files[i].id).web_path + '" target=_blank><i class ="fas fa-file-pdf fa-3x"></i>' + '</center></a>'+
            '</tr>';
           }
           if ((ext1 == "png") || (ext1 == "jpg") || (ext1 == "gif")) {
                         rows = rows + '<tr>'+
                            '<img src="'+editor.file( 'files', id=d.files[i].id ).web_path+'" class="img-circle" width="60" height="60"/>'+   
            '</tr>';
                    }
         }
        return ('<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">' +
        rows +
        '</table>');
    }
        
        var table = $('#example').DataTable( {
            scrollY:        '60vh',
            scrollCollapse: true,
            paging:         false,
            select: { style: 'single',
                             selector: 'td:not(:first-child)'
                            },
            responsive: { 
                "details": {
                            "type": 'column',
                            "target": 'tr'
            }
            },
            dom: "Bfrtip",
            order: [ 1, 'desc' ],
            ajax: "examples/php/upload-many.php",
            columns: [
                 {
                    "className":      'details-control',
                    "orderable":      false,
                    "data":           null,
                    "defaultContent": '',
                    "render": function () {
                        return '<i class="fa fa-plus-square" aria-hidden="true"></i>';
                            },
                        width: "15px"
                },
                { "className": "dt-right", data: "stuff.date" },
                { "className": "dt-right", data: "persons.person" },
                { "className": "dt-right", data: "categories.category" },
                { "className": "dt-right", data: "stuff.notes" },
                {
                    "className": "dt-right", data: "files[].id",
                    render: function ( d ) {
                        return d.length ?
                            d.length+' image(s)' :
                            'No image';
                    },
                    title: "Image"
                }
            ],
            select: true,
            buttons: [
                { extend: "create", editor: editor },
                { extend: "edit",   editor: editor },
                { extend: "remove", editor: editor,
                    formMessage: function ( e, dt ) {
                        var rows = dt.rows( e.modifier() ).data().pluck('notes');
                        return 'Are you sure you want to delete the entries for the '+
                            'following record(s)? <ul><li>'+rows.join('</li><li>')+'</li></ul>';
                    } 
                }
            ],
            
                initComplete: function () {
                    // Add event listener for opening and closing details
                    $('#example tbody').on('click', 'td.details-control', function () {
                        var tr = $(this).closest('tr');
                        var tdi = tr.find("i.fa");
                        var row = table.row(tr);
    
                        if (row.child.isShown()) {
                            // This row is already open - close it
                            row.child.hide();
                            tr.removeClass('shown');
                            tdi.first().removeClass('fa-minus-square');
                            tdi.first().addClass('fa-plus-square');
                        }
                        else {
                            // Open this row
                            row.child(format(row.data())).show();
                            tr.addClass('shown');
                            tdi.first().removeClass('fa-plus-square');
                            tdi.first().addClass('fa-minus-square');
                        }         
        } );
    }
        } );
     $('#example').on("user-select", function (e, dt, type, cell, originalEvent) {
              if ($(cell.node()).hasClass("details-control")) {
                    e.preventDefault();
                }
            });    
    } );
        </script>
    </head>
    <body class="dt-example">
        <div class="container">
            <section>
                <h1>Filing Cabinet<span>  file manager</span></h1>
                <div class="demo-html"></div>
                <table id="example" class="display" cellspacing="0" width="100%">
            <thead>
                <tr>
                    <th></th>
                    <th>date</th>
                    <th>category</th>
                    <th>person</th>
                    <th>notes</th>
                    <th>Image</th>
                </tr>
            </thead>
        </table>
            </section>
        </div>
    </body>
    </html>
    
Sign In or Register to comment.