Tag Archive ตาราง

Byphunsanit

DataTable: แสดงเลขที่รายการ

เมื่อมีข้อมูลจำนวนมาก โดยที่ไม่ได้แสดง primary key หรือข้อมูลมาจากการ join ข้อมูลหลายๆ ตาราง การอ้างอิงแถวในตารางก็จะทำไม่สะดวก เดิมจะใช้วิธี query โดยใช้ :rownum หรือ row_number ให้แสดงแถวของข้อมูลตาม database ที่ใช้ แต่ข้อเสียก็คือ มันจะเพิ่มภาระให้ฐานข้อมูล ซึ่งจริงๆแล้วสามารถผลักภาระส่วนนี้ไปให้ทางฝั่ง client ได้

<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="author" content="Pitt Phunsanit">
    <title>DataTables: row number</title>
    <link href="../vendor/twbs/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" type="text/css">
    <link href="../vendor/twbs/bootstrap/dist/css/bootstrap-theme.min.css" rel="stylesheet" type="text/css">
    <link href="../vendor/datatables/datatables/media/css/dataTables.bootstrap.min.css" rel="stylesheet" type="text/css">
</head>

<body>
    <table class="table table-bordered table-hover table-striped" id="tableA" width="100%"></table>
    <script src="../vendor/components/jquery/jquery.min.js"></script>
    <script src="../vendor/datatables/datatables/media/js/jquery.dataTables.min.js"></script>
    <script src="../vendor/datatables/datatables/media/js/dataTables.bootstrap.min.js"></script>
    <script src="row_number.js"></script>
</body>

</html>

$(function() {

    tableA = $('#tableA');

    datatable = tableA.DataTable({
        "ajax": {
            "data": function(parameters) {},
            "method": "POST",
            "url": "data.json.php",
        },
        "columns": [{
                "orderable": false,
                "render": function render(data, type, row, meta) {
                    var row_number = (parseInt(meta.settings._iDisplayStart) + parseInt(meta.row) + 1);
                    return String(row_number).replace(/(\d)(?=(\d{3}))/g, '$1,');;
                },
                "searchable": false,
                "targets": 0,
                "title": "No.",
            },
            {
                "orderable": false,
                "render": function(data, type, row, meta) {
                    return parseInt(meta.row) + parseInt(meta.settings._iDisplayStart) + 1;
                },
                "title": 'No.',
                "width": "10px",
            },
            {
                "orderable": false,
                "render": function(data, type, row, meta) {
                    return '<input type="checkbox" value="' + row.DISTRICT_CODE + '">';
                },
                "title": '<input class="checkAll" type="checkbox">',
                "width": "10px",
            },
            {
                "orderable": false,
                "render": function(data, type, row, meta) {
                    if (row.enable == '1') {
                        return '<span class="glyphicon glyphicon-ok"></span>';
                    } else {
                        return '<span class="glyphicon glyphicon-remove"></span>';
                    }
                },
                "title": "Enable",
                "width": "10px",
            }, {
                "data": "DISTRICT_CODE",
                "title": "District Code",
                "width": "90px",
            }, {
                "data": "DISTRICT_NAME",
                "title": "District Name",
            }, {
                "data": "PROVINCE_NAME",
                "title": "Province Name",
            }
        ],
        /* default sort */
        "order": [
            [3, "asc"],
            [4, "asc"],
        ],
        "processing": true,
        "serverSide": true,
        "stateSave": true,
    });

    $('.checkAll', tableA).click(function() {
        $('input:checkbox', tableA).not(this).prop('checked', this.checked);
    });

});

<?php
/* https://datatables.net/manual/server-side */

$output = [
    'data' => [],
    'debug' => [
        'length' => $_REQUEST['length'],
        'post' => $_REQUEST,
        'sqlCount' => '',
        'sqlResult' => '',
        'start' => $_REQUEST['start'],
    ],
    'draw' => $_REQUEST['draw'],
    'recordsFiltered' => $_REQUEST['length'],
    'recordsTotal' => 0,
];

$dns = new PDO('mysql:host=localhost;dbname=snippets', 'root', '', [
    //PDO::ATTR_EMULATE_PREPARES => false,
    PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
]);

$condition = [];
$from = ' FROM district AS d LEFT JOIN province AS p ON d.PROVINCE_ID = p.PROVINCE_ID';
$parameters = [];
$where = '';

if (isset($_REQUEST['filters']) || isset($_REQUEST['search']['value'])) {

    if (isset($_REQUEST['search']['value'])) {
        if ($_REQUEST['search']['value'] != '') {

            $parameter = ':d_DISTRICT_NAME';

            $parameters[$parameter] = '%' . $_REQUEST['search']['value'] . '%';
            array_push($condition, 'd.DISTRICT_NAME LIKE ' . $parameter);
        }
    }

    if (isset($_REQUEST['filters'])) {
        foreach ($_REQUEST['filters'] as $tableAlias => $filter) {
            foreach ($filter as $field => $value) {
                if ($value != '') {
                    $parameter = ':' . $tableAlias . '_' . $field;

                    $parameters[$parameter] = '%' . $value . '%';
                    array_push($condition, $tableAlias . '.' . $field . ' LIKE ' . $parameter);
                }
            }
        }
    }

}

if (isset($_REQUEST['geo_id']) && $_REQUEST['geo_id'] != '') {
    $parameter = ':d_geo_id';

    $parameters[$parameter] = $_REQUEST['geo_id'];
    array_push($condition, 'd.GEO_ID = ' . $parameter);
}

if (count($parameters)) {
    $where = ' WHERE ' . implode("\n\t AND ", $condition);
}

if (isset($_REQUEST['order']) && isset($_REQUEST['order'][0])) {
    $columns = [
        0 => 'DISTRICT_NAME',
        3 => 'DISTRICT_CODE',
        4 => 'DISTRICT_NAME',
        5 => 'PROVINCE_NAME',
    ];

    $order = ' ORDER BY ' . $columns[$_REQUEST['order'][0]['column']] . ' ' . strtoupper($_REQUEST['order'][0]['dir']);
} else {
    $order = ' ORDER BY DISTRICT_NAME ASC';
}

$output['debug']['parameters'] = $parameters;

/* Total records, before filtering */
$sql = 'SELECT COUNT(d.DISTRICT_ID)' . $from;
try {
    $output['debug']['sqlCount'] = $sql;
    $stmt = $dns->prepare($sql);
    $stmt->execute($parameters);
    $output['recordsTotal'] = (int) $stmt->fetchColumn(0);
} catch (PDOException $e) {
    exit($e->getMessage());
}

/* Total records, after filtering */
$sql = 'SELECT COUNT(d.DISTRICT_ID)' . $from . $where;
try {
    $output['debug']['sqlCount'] = $sql;
    $stmt = $dns->prepare($sql);
    $stmt->execute($parameters);
    $output['recordsFiltered'] = (int) $stmt->fetchColumn(0);
} catch (PDOException $e) {
    exit($e->getMessage());
}

/* data */
if ($output['recordsTotal'] > 0) {
    $sql = 'SELECT d.enable, d.DISTRICT_ID, d.DISTRICT_CODE, d.DISTRICT_NAME, p.PROVINCE_NAME' . $from . $where . $order . " LIMIT " . $_REQUEST['start'] . ", " . $_REQUEST['length'] . ";";

    try {
        $output['debug']['sqlResult'] = $sql;
        $stmt = $dns->prepare($sql);
        $stmt->execute($parameters);
        $output['data'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $e) {
        exit($e->getMessage());
    }
}

/* unset debug for security */
unset($output['debug']);

header('Content-type: application/json; charset=utf-8');
echo json_encode($output);

Byphunsanit

DataTable: ดัดแปลง / คัดลอก ข้อมูล

แนวคิดคือ คัดลอกข้อมูลที่เลือกเอาไว้จาก DataTables ตัวหนึ่งไปยังอีกตัวหนึง

<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="author" content="Pitt Phunsanit">
    <title>DataTables: Transfer Data To Another Table</title>
    <link href="../vendor/twbs/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" type="text/css">
    <link href="../vendor/twbs/bootstrap/dist/css/bootstrap-theme.min.css" rel="stylesheet" type="text/css">
    <link href="../vendor/datatables/datatables/media/css/dataTables.bootstrap.min.css" rel="stylesheet" type="text/css">
    <link href="../assets/fronteed/icheck/skins/minimal/red.css" rel="stylesheet" type="text/css">
</head>

<body>
    <table class="table table-bordered table-hover table-striped" id="filtersTableA" width="100%"></table>
    <button class="btn btn-success" id="copyBtn" type="button">Copy Data To Table</button>
    <table class="table table-bordered table-hover table-striped" id="tableA" width="100%"></table>
    <script src="../vendor/components/jquery/jquery.min.js"></script>
    <script src="../vendor/datatables/datatables/media/js/jquery.dataTables.min.js"></script>
    <script src="../vendor/datatables/datatables/media/js/dataTables.bootstrap.min.js"></script>
    <script src="../assets/fronteed/icheck/icheck.min.js"></script>

    <script src="DataTables.js"></script>

    <script src="data.json.transferDataToAnotherTable.js"></script>
</body>

</html>

ส่วนที่เขียนเป็นฟังก์ชั่นกลางไว้ให้ไฟล์อื่นเรียกใช้

function iCheckBulk(dataTableArea, dataTableObject) {
    dataTableArea.on('ifChanged', '.checkAll', function(event) {

        var datas = dataTableObject.data();
        var inputs = $('input:checkbox, input:radio', dataTableArea);

        if (event.target.checked) {
            var enable = '1';
            var state = 'check';
        } else {
            var enable = '0';
            var state = 'uncheck';
        }

        $.each(datas, function(index, value) {
            value.enable = enable;

            dataTableObject.row(index).data(value);
        });

        iCheckInit($('input:checkbox, input:radio', dataTableArea));
    });

}

/* change filtersTable data value on input name enables is change */
function iCheckChange(filtersTableArea, filtersTableObject, dataTableObject) {
    $('tbody', filtersTableArea).on('ifChanged', 'input[name="enables[]"]', function(event) {

        event.stopPropagation();

        var row = $(this).closest('tr');

        var data = filtersTableObject.row(row).data();

        if ($(this).is(':checked')) {
            $(this).attr('checked', 1);
            data.enable = true;
        } else {
            $(this).attr('checked', 0);
            data.enable = false;
        }
        filtersTableObject.row(row).data(data);

        iCheckInit(row);
    });
}

function iCheckCopy(dataTableObject, filtersTableObject, pkField) {
    $('#copyBtn').click(function() {

        /* loop current data (pkField) in current dataTableObject */
        var datas = dataTableObject.data();
        var hasKeys = new Array();
        $.each(datas, function(index, value) {
            hasKeys.push(value[pkField]);
        });

        var datasChoose = filtersTableObject.data();

        $.each(datasChoose, function(index, value) {
            /* add row to filtersTableObject if input name enables[] is checked */
            if (value.enable == true && hasKeys.indexOf(value[pkField]) == -1) {
                dataTableObject
                    .row.add(value)
                    .draw()
                    .node();
            }
        });

    });

}

function iCheckInit(selector) {
    selector.iCheck({
        checkboxClass: 'icheckbox_minimal-red',
        radioClass: 'iradio_minimal-red',
    });
}

ไฟล์ที่ทำหน้าที่ควบคุมการคัดลอกข้อมูล

$(function() {

    dataTableA = $('#dataTableA');
    filtersTableA = $('#filtersTableA');
    tableA = $('#tableA');

    filtersTable = filtersTableA
        .DataTable({
            "ajax": {
                "data": function(parameters) {},
                "method": "POST",
                "url": "data.json.php",
            },
            "columns": [{
                    "orderable": false,
                    "render": function(data, type, row, meta) {
                        return parseInt(meta.row) + parseInt(meta.settings._iDisplayStart) + 1;
                    },
                    "title": 'No.',
                    "width": "10px",
                },
                {
                    "orderable": false,
                    "render": function(data, type, row, meta) {
                        if (row.enable == '1') {
                            var checked = ' checked';
                        } else {
                            var checked = '';
                        }

                        return '<input' + checked + ' name="enables[]" type="checkbox" value="' + row.DISTRICT_CODE + '">';
                    },
                    "title": '<input class="checkAll" type="checkbox">',
                    "width": "10px",
                },
                {
                    "orderable": false,
                    "render": function(data, type, row, meta) {
                        if (row.enable == '1') {
                            return '<span class="glyphicon glyphicon-ok"></span>';
                        } else {
                            return '<span class="glyphicon glyphicon-remove"></span>';
                        }
                    },
                    "title": "Enable",
                    "width": "10px",
                }, {
                    "data": "DISTRICT_CODE",
                    "title": "District Code",
                    "width": "90px",
                }, {
                    "data": "DISTRICT_NAME",
                    "title": "District Name",
                }, {
                    "data": "PROVINCE_NAME",
                    "title": "Province Name",
                }
            ],
            "processing": true,
            "serverSide": true,
            "stateSave": true
        })
        .on('draw', function(event, settings, json, xhr) {
            /* add style to checkbox, radio */
            iCheckInit($('input:checkbox, input:radio', settings.nTable));
        });

    iCheckBulk(filtersTableA, filtersTable);

    dataTable = tableA
        .DataTable({
            "columns": [{
                    "orderable": false,
                    "render": function(data, type, row, meta) {
                        return parseInt(meta.row) + parseInt(meta.settings._iDisplayStart) + 1;
                    },
                    "title": 'No.',
                    "width": "10px",
                },
                {
                    "orderable": false,
                    "render": function(data, type, row, meta) {
                        if (row.enable == 1) {
                            var checked = ' checked';
                        } else {
                            var checked = '';
                        }

                        return '<input' + checked + ' name="enables[]" type="checkbox" value="' + row.DISTRICT_CODE + '">';
                    },
                    "title": '<input class="checkAll" type="checkbox">',
                    "width": "10px",
                },
                {
                    "orderable": false,
                    "render": function(data, type, row, meta) {
                        if (row.enable) {
                            return '<span class="glyphicon glyphicon-ok"></span>';
                        } else {
                            return '<span class="glyphicon glyphicon-remove"></span>';
                        }
                    },
                    "title": "Enable",
                    "width": "10px",
                }, {
                    "data": "DISTRICT_CODE",
                    "title": "District Code",
                    "width": "90px",
                }, {
                    "data": "DISTRICT_NAME",
                    "title": "District Name",
                }, {
                    "data": "PROVINCE_NAME",
                    "title": "Province Name",
                }
            ]
        })
        .on('draw', function(event, settings, json, xhr) {
            /* add style to checkbox, radio */
            iCheckInit($('input:checkbox, input:radio', settings.nTable));
        });

    iCheckBulk(tableA, dataTable);

    iCheckChange(filtersTableA, filtersTable, dataTable);

    iCheckCopy(dataTable, filtersTable, 'DISTRICT_CODE');

});

อธิบาย

  • มี 2 ตารางคือ id=”filtersTableA” เป็นต้นฉบับ และ id=”tableA” เป็นตารางที่จะรับค่าที่จะคัดลอก
  • เพราะว่าอาจจะแบ่งข้อมูลไว้หลายหน้าและ DataTable จะ render ที่ละหน้าเท่านั้น ถ้า user เลือกรายการโดยใช้ input enables[] แล้วเปลี่ยนไปหน้าอื่น input นั้นจะหายไป ทำให้ต้องใช้วิธี update กลับไปที่ Data ของตัว DataTable filtersTableA โดยเปลี่ยนค่าใน object data เช่น data.enable = true; หรือ data.enable = false;
  • เมือคลิก ปุ่ม id=”copyBtn” ให้คัดลอกข้อมูล เพื่อป้องกันการเก็บข้อมูลซ้ำจึงต้อง ดึงรายการที่เก็บข้อมูลเอาไว้แล้ว มาเทียบว่าที่จะเข้ามาใหม่มีรึยังโดยใช้ primary key คือ DISTRICT_CODE

อ่านเพิ่มเติม