Tag Archive validation

Byphunsanit

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 ไม่ได้ ดูเพิ่มเติม

Byphunsanit

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>';

Byphunsanit

DataTable: การค้นหาชั้นสูง

เราสามารถเพิ่มเงื่อนไขให้ DataTables ใช้ในการค้นหาได้ โดยส่วนที่เปลี่ยนไปจากเดิมคือใช้ “beforeSend” ในการรวมข้อมูลจาก form (formA) และเขียน validation ง่ายๆ โดยบังคับว่าถ้า advanceSearch ถูกติ๊กอยู่จะต้องเลือก geo_id ด้วย (จริงๆคือ ถึงไม่ติ๊ก advanceSearch ก็ใช้ geo_id search ได้เหมือนกัน)

<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="author" content="Pitt Phunsanit">
    <title>DataTables: external.search</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>
    <form action="DataTables.json.php" class="form-inline" id="formA" method="post">
        <div class="form-group">
            <input name="advanceSearch" type="checkbox">
            <label for="advanceSearch">advance Search</label>
        </div>
        <div class="form-group">
            <label for="geo_id">ภูมิภาค:</label>
            <select class="form-control" name="geo_id">
               <option selected="selected" value="">--- Select ---</option>
               <option value="1">ภาคเหนือ</option>
               <option value="2">ภาคกลาง</option>
               <option value="3">ภาคตะวันออกเฉียงเหนือ</option>
               <option value="4">ภาคตะวันตก</option>
               <option value="5">ภาคตะวันออก</option>
               <option value="6">ภาคใต้</option>
            </select>
        </div>
        <button type="submit" class="btn btn-default">Search</button>
    </form>
    <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="data.json.external.search.js"></script>
</body>

</html>
$(function() {

    formA = $('#formA');
    tableA = $('#tableA');

    datatable = tableA.DataTable({
        "ajax": {
            "beforeSend": function(jqXHR, settings) {
                /* add value form from to DataTable params */
                settings.data = formA.serialize() + '&' + settings.data;

                /* validation */
                var params = new URLSearchParams(settings.data);

                /* user must selected region if enable advance search */
                if (params.get('advanceSearch') == 'on' && params.get('geo_id') == '') {
                    jqXHR.abort();
                    alert('กรุณาเลือกภูมิภาค');
                    return false;
                }

                return true;
            },
            "dataSrc": function(json) {
                alert('data back ' + json.data.length + ' items');

                return json.data;
            },
            "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) {
                    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);
    });

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

        datatable.ajax.reload();
    });

});
  • ข้อมูลจากฟอร์ม
    id="formA"

    จะถูกรวมกับ data ที่ DataTable สร้างขึ้นมาเองโดย

    "beforeSend": function(jqXHR, settings) {
    settings.data = formA.serialize() + '&' + settings.data;
    },
  • สามารถทำ validation data ก่อนส่งข้อมูลออกไป ถ้าต้องการหยุดการทำงาน ก็ให้ใช้
    "beforeSend": function(jqXHR, settings) {
    jqXHR.abort();
    },

    ไม่ให้ส่งข้อมูลกลับไป server อาจจะดัดแปลงให้ datatable ไม่ขอข้อมูลจาก server กรณีที่ไม่มีการเลือกตัว filter ก็ได้เช่นกัน

  • หลังได้ข้อมูลกลับมายังสามารถใช้
    "dataSrc": function(json) {
                    alert('data back ' + json.data.length + ' items');
    
                    return json.data;
                },

    แก้ไขข้อมูลก่อนแสดงผลได้ แต่ไม่ใช่ “success” เหมือนใน jQuery ajax ตามปกตินะครับ เพราะจะทำให้ DataTable มันทำงานผิดปกติได้เลย

  • ถ้าต้องการให้ DataTable ดึงข้อมูลใหม่ให้ใช้รูปแบบ
    datatable.ajax.reload();

<?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

Custom Rules Validation

ตัวอย่างการเขียน custom rule validation

<?php
class FormValidationCustomRules extends CI_Controller
{

    public function __construct()
    {
        parent::__construct();
        // Your own constructor code
    }

    public function uploadJPEG()
    {
        $this->form_validation->set_rules('cover', 'JPEG Upload', 'trim|xss_clean');

        if ($this->form_validation->run() == true) {
            $fileDir = 'assets/contents/';
            $fileName = date('U') . '.jpeg';

            $config = [
                'allowed_types' => 'jfi|jfif|jif|jpe|jpeg|jpg',
                'file_ext_tolower' => true,
                'file_name' => $fileName,
                'max_size' => '10000',
                'overwrite' => false,
                'upload_path' => $fileDir,
            ];

            $this->load->library('upload', $config);

            if ($this->upload->do_upload('cover', false)) {
                $this->uploadFile = $fileDir . $fileName;
                return true;
            } else {
                $this->form_validation->set_message('uploadJPEG', $data['error'] = $this->upload->display_errors());

                if ($_FILES['cover']['error'] != 4) {
                    return false;
                }
            }
        }

        return false;
    }

    public function index()
    {
        $this->load->library('form_validation');

        $formValidation = [
            [
                'field' => 'cover',
                'label' => 'JPEG Upload',
                'rules' => 'callback_uploadJPEG',
            ],
        ];
        $this->form_validation->set_rules($formValidation);

        if ($this->form_validation->run() == true) {
            echo 'upload file to ', $this->uploadFile, '<br><img alt="', $this->uploadFile, '" class="img-thumbnail" src="../', $this->uploadFile, '">';
        }

        $this->load->view('FormValidation/CustomRules');
    }
}

หลักการคือ

  • สร้าง function ขึ้นมา แล้วเรียกใช้โดยมี callback_ นำหน้า
  • ส่ง message กลับมาโดยใช้ $this->form_validation->set_message(‘function name’,’ข้อความ’)
Byphunsanit

แก้ validation error ใน YII 2

ฟอร์มของ yii จะมีการแจ้งเตือนถ้าหากพบว่าข้อมูล input ที่เรากรอกใน form ไม่ถูกต้อง โดยจะทำกรอบอินพุต ป้าย label เป็นสีแดงและมีข้อความแสดงใน help-block เพิ่มขึ้นมา บางครั้งก็ทำให้ฟอร์มที่จัดไว้แน่นๆ ไม่ใช่แบบบรรทัดละกล่องข้อความตามแบบเว็บสมัยใหม่ เวลาเจอความผิดพลาด มันก็จะถีบตัวอื่นออกไป หรือดูอัดแน่นเกินไปจนดูไม่สวย

ก็สามารถเอาออกได้โดยใช้ form template เหมือนเดิม โดยยังสามารถทำ validation ได้ตามปกติ

วิธีการคือ

  1. เปิดไฟล์ _form.php ใน view เป้าหมาย
  2. เปลี่ยน
    <?php $form = ActiveForm::begin(['id' => 'login-form']); ?>
    

    เป็น

    <?php $form = ActiveForm::begin([
        'fieldConfig' => [
            'template' => "{label}\n{input}\n{hint}"
        ],
        'id' => 'login-form'
    ]); ?>
    

อย่าลืมเปลี่ยน id นะครับ

ถ้าใช้

use yii\bootstrap\ActiveForm;

ให้เปลี่ยน template เป็น

'template' => "{label}\n{beginWrapper}\n{input}\n{hint}\n{endWrapper}"