Tag Archive แก้ไข

jQuery: upload แบบ ajax

ตัวอย่างการใช้ jQuery upload ไฟล์แบบ ajax

<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <title>jQuery: form > send file</title>
    <link href="../vendor/twbs/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" type="text/css">
    <style>
        .progressbar {
            background: #c00000;
            clear: both;
            height: 10px;
            width: 0;
        }

        .progressLoaded {
            width: 50px;
        }

        .progressLoaded::after {
            content: " bytes";
        }

        .progressPercent {
            width: 50px;
        }

        .progressPercent::after {
            content: " %";
        }

        .progressTotal {
            float: left;
            width: 50px;
        }

        .progressTotal::after {
            content: " bytes";
        }

        .progressTotal::before {
            content: " of ";
        }
    </style>
</head>

<body>
    <h1>upload file with ajax</h1>
    <form action="values.php" class="form-horizontal" enctype="multipart/form-data" id="formA" method="post">
        <div class="form-group">
            <label class="col-sm-2 control-label" for="socialidI">หมายเลขบัตรประชาชน</label>
            <div class="col-sm-10">
                <input class="form-control" id="socialidI" maxlength="13" name="socialid" placeholder="หมายเลขบัตรประชาชน" type="number">
            </div>
        </div>
        <div class="form-group">
            <label class="col-sm-2 control-label" for="photoI">ภาพถ่าย</label>
            <div class="col-sm-10">
                <input class="form-control" id="photoI" name="photo" placeholder="upload file" type="file">
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm-10 text-right">
                <button type="submit" class="btn btn-success">ส่งข้อมูล</button>
            </div>
        </div>
    </form>
    <div class="col-sm-12">
        <h4>Progress</h4>
        <div class="progressLoaded"></div>
        <div class="progressTotal"></div>
        <div class="progressPercent"></div>
        <div class="progressbar"></div>
    </div>
    <div class="col-sm-12" id="resultA"></div>
    <script src="../vendor/components/jquery/jquery.min.js"></script>
    <script>
        function xhrProgress(xhr) {
            if (xhr.upload) {
                // For handling the progress of the upload
                xhr.upload.addEventListener('progress', function (e) {
                    if (e.lengthComputable) {
                        $('.progressTotal').html(e.total);
                        $('.progressLoaded').html(e.loaded);

                        percentComplete = parseInt((e.loaded / e.total * 100), 10);
                        $('.progressPercent').html(percentComplete);
                        $('.progressbar').css("width", percentComplete + '%');
                    }
                }, false);
            }
            return xhr;
        }

        $(function () {

            var formA = $('#formA');

            formA.submit(function (event) {
                event.preventDefault();

                $.ajax({
                    "beforeSend": function (jqXHR, settings) {
                        /* แยกตัวแปรไว้ตรวจสอบข้อมูล */
                        var params = settings.data;

                        if (settings.data.get('socialid') == '') {

                            jqXHR.abort();
                            alert('กรุณากรอกหมายเลขบัตรประชาชน');

                            return false;
                        }

                        if (settings.data.get('photo').name == '') {

                            jqXHR.abort();
                            alert('กรุณาอัพโหลดรูปภาพ');

                            return false;
                        }

                    },
                    "contentType": false,
                    "data": new FormData(formA[0]),
                    "method": "POST",
                    "processData": false,
                    "success": function (data, textStatus, jqXHR) {
                        $('#resultA').html(data);
                    },
                    "url": "values.php",
                    "xhr": function () {
                        return xhrProgress($.ajaxSettings.xhr());
                    },

                });

            });

        });
    </script>
</body>

</html>

ไฟล์ที่รอรับข้อมูล

<?php
echo 'FILE<pre>', print_r($_FILES, true), '</pre>';
echo 'GET<pre>', print_r($_GET, true), '</pre>';
echo 'POST<pre>', print_r($_POST, true), '</pre>';

วิธีนี้ใช้ความสามารถใหม่ FormData ที่ใช้กับ browser เก่าแก่กว่า Internet Explorer 10 หรือ Edge ไม่ได้ ดูเพิ่มเติม

jQuery: ส่ง form แบบ ajax ชั้นสูง

ก่อนที่จะใช้ jQuery ส่งข้อมูลแบบ ajax ออกไป อาจจะใช้ javascript ทำงานเบื้องต้นก่อนได้เช่น การตรวจสอบข้อมูล (validation) ว่า user ได้ทำการกรอกข้อมูลตามที่จำเป็นครบรึเปล่า หรือนำข้อมูลออกไปจากแบบฟอร์ม โดยที่ไม่ต้องไปทำในฝั่ง server

ใน jQuery Ajax สามารถทำได้โดยการระบุ “beforeSend” ตาม code ตัวอย่าง

$.ajax({
    "beforeSend": function(jqXHR, settings) {

        /* ใส่ตัวแปรเข้าไปเพิ่ม */
        settings.data += '&ajax=true';

        /* แยกตัวแปรไว้ตรวจสอบข้อมูล */
        var params = new URLSearchParams(settings.data);

        /* บังคับกรอกข้อมูล */
        if (params.get('way') == 'socialid' && params.get('socialid') == '') {

            jqXHR.abort();
            alert('กรุณากรอกหมายเลขบัตรประชาชน');

            return false;
        } else if (params.get('way') == 'phone' && params.get('phone') == '') {

            jqXHR.abort();
            alert('กรุณากรอกหมายเลขโทรศัพท์');

            return false;
        }

        if (params.get('amount') == '') {

            jqXHR.abort();
            alert('กรุณากรอกจำนวนเงิน');

            return false;
        }

        /* เอาข้อมูลที่ไม่ต้องการส่งออก */
        settings.data = RemoveParameterFromUrl(settings.data, 'way');
        settings.data = RemoveParameterFromUrl(settings.data, 'way');
        if (params.get('way') == 'socialid') {
            settings.data = RemoveParameterFromUrl(settings.data, 'phone');
        } else {
            settings.data = RemoveParameterFromUrl(settings.data, 'socialid');
        }

    },
    "data": formA.serialize(),
    "method": "POST",
    "success": function(data, textStatus, jqXHR) {
        $('#resultA').html(data);
    },
    "url": "values.php",
});

เราจะดูข้อมูลที่รวบรวมมาได้ใน object settings.data และสามารถต่อ string เพิ่มเข้าไปได้เหมือนการต่อ string

settings.data += '&ajax=true';

การที่จะแยกข้อมูลออกมาสามารถใช้

var params = new URLSearchParams(settings.data);

แบ่งข้อมูลออกมาเป็น array เพื่อใช้ตรวจสอบข้อมูลหรือจะนำไปใช้ในการเปลี่ยนแปลงข้อมูล เช่นเดียวกันก็สามารถ delete ลบข้อมูลที่ไม่ได้การออกไปได้โดยใช้ function

function RemoveParameterFromUrl(url, parameter) {
    return url.replace(new RegExp('^([^#]*\?)(([^#]*)&)?' + parameter + '(\=[^&#]*)?(&|#|$)'), '$1$3$5').replace(/^([^#]*)((\?)&|\?(#|$))/, '$1$3$4');
}

ตัวอย่าง form เขียนโดยยกกรณีโอนเงินโดยใช้ พร้อมเพย์ (PromptPay) ที่ user จะเลือกได้ว่าจะโอนเงินโดยใช้ บัตรประชาชน หรือหมายเลขโทรศัพท์ของผู้รับแทนที่จะใช้หมายเลขบัญชี

<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <title>jQuery: Advance Form Send</title>
    <link href="../vendor/twbs/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" type="text/css">
</head>

<body>
    <h1>พร้อมเพย์ (PromptPay)</h1>
    <form action="values.php" class="form-horizontal" id="formA" method="post">
        <div class="form-group">
            <label class="col-sm-2 control-label">โอนเงินด้วย</label>
            <div class="col-sm-10">
                <div class="radio">
                    <label>
                        <input checked name="way" type="radio" value="socialid"> หมายเลขบัตรประชาชน
                    </label>
                </div>
                <div class="radio">
                    <label>
                        <input name="way" type="radio" value="phone"> หมายเลขโทรศัพท์
                    </label>
                </div>
            </div>
        </div>
        <div class="form-group">
            <label class="col-sm-2 control-label" for="socialidI">หมายเลขบัตรประชาชน</label>
            <div class="col-sm-10">
                <input class="form-control" id="socialidI" maxlength="13" name="socialid" placeholder="หมายเลขบัตรประชาชน" type="number">
            </div>
        </div>
        <div class="form-group">
            <label class="col-sm-2 control-label" for="phoneI">หมายเลขโทรศัพท์</label>
            <div class="col-sm-10">
                <input class="form-control" id="phoneI" maxlength="11" name="phone" placeholder="หมายเลขโทรศัพท์" type="tel">
            </div>
        </div>
        <div class="form-group">
            <label for="amount" class="col-sm-2 control-label">จำนวนเงิน</label>
            <div class="col-sm-10">
                <div class="input-group">
                    <input class="form-control" id="amount" max="5000" min="0" name="amount" placeholder="จำนวนเงิน" type="number">
                    <div class="input-group-addon">&#3647;</div>
                </div>
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm-10 text-right">
                <button type="submit" class="btn btn-success">โอนเงิน</button>
            </div>
        </div>
    </form>
    <div class="col-sm-12" id="resultA"></div>
    <script src="../vendor/components/jquery/jquery.min.js"></script>
    <script>
        function RemoveParameterFromUrl(url, parameter) {
            return url.replace(new RegExp('^([^#]*\?)(([^#]*)&)?' + parameter + '(\=[^&#]*)?(&|#|$)'), '$1$3$5').replace(/^([^#]*)((\?)&|\?(#|$))/, '$1$3$4');
        }

        $(function () {
            var formA = $('#formA');

            formA.submit(function (event) {
                event.preventDefault();

                $.ajax({
                    "beforeSend": function (jqXHR, settings) {

                        /* ใส่ตัวแปรเข้าไปเพิ่ม */
                        settings.data += '&ajax=true';

                        /* แยกตัวแปรไว้ตรวจสอบข้อมูล */
                        var params = new URLSearchParams(settings.data);

                        /* บังคับกรอกข้อมูล */
                        if (params.get('way') == 'socialid' && params.get('socialid') == '') {

                            jqXHR.abort();
                            alert('กรุณากรอกหมายเลขบัตรประชาชน');

                            return false;
                        } else if (params.get('way') == 'phone' && params.get('phone') == '') {

                            jqXHR.abort();
                            alert('กรุณากรอกหมายเลขโทรศัพท์');

                            return false;
                        }

                        if (params.get('amount') == '') {

                            jqXHR.abort();
                            alert('กรุณากรอกจำนวนเงิน');

                            return false;
                        }

                        /* เอาข้อมูลที่ไม่ต้องการส่งออก */
                        settings.data = RemoveParameterFromUrl(settings.data, 'way');
                        settings.data = RemoveParameterFromUrl(settings.data, 'way');
                        if (params.get('way') == 'socialid') {
                            settings.data = RemoveParameterFromUrl(settings.data, 'phone');
                        } else {
                            settings.data = RemoveParameterFromUrl(settings.data, 'socialid');
                        }

                    },
                    "data": formA.serialize(),
                    "method": "POST",
                    "success": function (data, textStatus, jqXHR) {
                        $('#resultA').html(data);
                    },
                    "url": "values.php",
                });
            });
        });
    </script>
</body>

</html>

และฝั่ง server side ที่ควรจะมีการ validation ตรวจสอบข้อมูลซ้ำอีกครั้งเพื่อความมั่นใจ แต่ตัวอย่างนี้จะแสดงข้อมูลที่ทางฝั่ง server ได้รับเท่านั้น เพื่อให้เห็นการ edit แก้ข้อมูลที่ส่งไป

<?php
echo 'GET<pre>', print_r($_GET, true), '</pre>';
echo 'POST<pre>', print_r($_POST, true), '</pre>';

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

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