Hướng dẫn array in form html - mảng ở dạng html

Bài viết sau hướng dẫn tạo HTML Form truyền dữ liệu có cấu trúc (object và array) lên server.

Hướng dẫn array in form html - mảng ở dạng html
Hướng dẫn submit object/array với HTML Form

Cách tạo form gửi object/array và xử lý bằng PHP

Nếu đã từng sử dụng PHP hẳn bạn biết có thể dùng $_POST để nhận dữ liệu có cấu trúc từ client bằng cách dùng dấu ngoặc vuông trong thuộc tính

// Result of $_POST (PHP): // array( // 'user' => array( // 'username' => 'User Name' // 'password' => 'Password' // ) // )
0 của
// Result of $_POST (PHP): // array( // 'user' => array( // 'username' => 'User Name' // 'password' => 'Password' // ) // )
1 để thể hiện được cấu trúc đó.

Ví dụ theo cách thông thường khi muốn truyền tham số lên server ta làm như sau:

// Result of $_POST (PHP): // array( // 'username' => 'User Name', // 'password' => 'Password' // )

Dùng ngoặc vuông trong thuộc tính

// Result of $_POST (PHP): // array( // 'user' => array( // 'username' => 'User Name' // 'password' => 'Password' // ) // )
0, ta có thể gửi một Object lên server như sau:

// Result of $_POST (PHP): // array( // 'user' => array( // 'username' => 'User Name' // 'password' => 'Password' // ) // )

Nếu không chỉ định index trong dấu ngoặc vuông, PHP ngầm hiểu đó là một Array và sẽ tự tăng index (tính từ 0) nếu gặp phần tử trùng lặp.

// Result of $_POST (PHP): // array( // 'user' => array( // 0 => 'User Name', // 1 => 'Password' // ) // )

Và tất nhiên ta có thể chỉ định index của mảng nếu muốn.

// Result of $_POST (PHP): // array( // 'user' => array( // 0 => 'User Name', // 1 => 'Password' // ) // )

Tuyệt vời hơn, cách này cũng có thể áp dụng với cấu trúc dữ liệu lồng nhau (nested).

// Result of $_POST (PHP): // array( // 'user' => array( // 'auth' => array( // 'username' => 'User Name', // 'password' => 'Password' // ), // 'info' => array( // 0 => array( // 'address' => 'Address 1', // 'city' => 'City 1', // ), // 1 => array( // 'address' => 'Address 2', // 'city' => 'City 2', // ) // ) // ) // )

Xử lý form có object/array với JavaScript

Đôi khi phía Server lại là của bên thứ 3 và dữ liệu quy ước nhận được lại có Content Type là JSON chẳng hạn thì cách làm như trên hoàn toàn vô hiệu.

Cách làm của tôi là thay vì submit trực tiếp, tôi tách dữ liệu của Form thành một JSON object, sau đó sử dụng jQuery AJAX để gửi lên server.

Ở đây tôi sử dụng một jQuery plugin là

// Result of $_POST (PHP): // array( // 'user' => array( // 'username' => 'User Name' // 'password' => 'Password' // ) // )
3 (không phải mặc định của jQuery đâu nhé).

Nhiệm vụ của plugin này là chuyển đổi dữ liệu trong các input của một form thành một JSON object giống như những gì một server PHP nhận được như đã nói ở phần trước. Với ví dụ trên, data trả về có cấu trúc như sau:

{
    'user': {
        'auth': {
            'username': 'User Name',
            'password': 'Password'
        },
        'info' : {
            [
                {
                    'address': 'Address 1',
                    'city': 'City 1'
                },
                {
                    'address': 'Address 1',
                    'city': 'City 1'
                }
            ]
        }
    }
}

Mã nguồn của plugin

// Result of $_POST (PHP): // array( // 'user' => array( // 'username' => 'User Name' // 'password' => 'Password' // ) // )
3 như sau:

jquery.icreativ.js

(function ( $ ) {
    $.fn.serializeToObject = function() {
        let data = $(this).serializeArray(),
            ret = {};

        data.forEach( obj => {
            var matches = obj.name.match(/^[a-zA-Z0-9_]+(\[[a-zA-Z0-9_]*])+$/);    
            if (matches) { // array type input
                let lastKey = obj.name.match(/^[a-zA-Z0-9_]+/)[0],
                    dimensions = obj.name.match(/\[[a-zA-Z0-9_]*\]/g),
                    ref = ret;

                dimensions.forEach( function(key, depth) {
                    key = key.replace(/[\[\]]/g, ''); // remove square brackets

                    if (key === '') { // dynamic array
                        if (lastKey == '')  {
                            let idx = ref.length - 1;
                            if (idx == -1 || !ref[idx]) {
                                ref[idx + 1] = [];
                            }
                        } else {
                            if (!ref[lastKey]) {
                                ref[lastKey] = [];
                            }
                        }
                    } else if (isInteger(key)) { // indexed array
                        if (lastKey == '')  {
                            let idx = ref.length - 1;
                            if (idx == -1 || ref[idx][key] !== undefined) {
                                ref[idx + 1] = [];
                            }
                        } else {
                            if (!ref[lastKey]) {
                                ref[lastKey] = [];
                            }
                        }
                    } else { // object
                        if (lastKey == '')  {
                            let idx = ref.length - 1;
                            if (idx < 0 || ref[idx][key] !== undefined) {
                                ref[idx + 1] = {};
                            }
                        } else {
                            if (!ref[lastKey]) {
                                ref[lastKey] = {};
                            }
                        }
                    }
                    if (lastKey === '') {
                        let idx = ref.length - 1;
                        ref = ref[idx];
                    } else {
                        ref = ref[lastKey];
                    }
                    if (depth === dimensions.length - 1) {
                        if (key === '') {
                            let idx = ref.length;
                            ref[idx] = obj.value;
                        } else {
                            ref[key] = obj.value;
                        }
                    }
                    lastKey = key;
                });
            } else {
                if (!ret[obj.name]) {                    
                    ret[obj.name] = obj.value;
                } else {
                    if (ret[obj.name].constructor === Array) {
                        ret[obj.name].push(obj.value);
                    } else {
                        let temp = ret[obj.name];
                        ret[obj.name] = [temp, obj.value];
                    }
                }
            }
        } );
        return ret;
    }
} )( jQuery );

Nếu hứng thú với Vanilla JS thì tôi cũng chia sẻ mã nguồn như sau:

function serializeArray(form) {
    var result = [];

    Array.prototype.slice.call(form.elements).forEach(function (field) {
        if (!field.name || field.disabled
        || ['file', 'reset', 'submit', 'button'].indexOf(field.type) > -1)
            return;

        if (field.type === 'select-multiple') {
            Array.prototype.slice.call(field.options).forEach(function (option) {
                if (!option.selected)
                    return;
                result.push({
                    name: field.name,
                    value: option.value
                });
            });
           return;
        }
        if (['checkbox', 'radio'].indexOf(field.type) > -1 && !field.checked)
            return;
        result.push({
            name: field.name,
            value: field.value
        });
    });
    return result;
}

function serializeObject(form) {
    let data = serializeArray(form);
    let ret = {};

    data.forEach( obj => {
        var matches = obj.name.match(/^[a-zA-Z0-9_]+(\[[a-zA-Z0-9_]*\])+$/);
        if (matches) { // array type input
            let lastKey = obj.name.match(/^[a-zA-Z0-9_]+/)[0],
                dimensions = obj.name.match(/\[[a-zA-Z0-9_]*\]/g),
                ref = ret;

            dimensions.forEach( function(key, depth) {
                key = key.replace(/[\[\]]/g, ''); // remove square brackets

                if (key === '') { // dynamic array
                    if (lastKey == '')  {
                        let idx = ref.length - 1;
                        if (idx == -1 || !ref[idx]) {
                            ref[idx + 1] = [];
                        }
                    } else {
                        if (!ref[lastKey]) {
                            ref[lastKey] = [];
                        }
                    }
                } else if (isInteger(key)) { // indexed array
                    if (lastKey == '')  {
                        let idx = ref.length - 1;
                        if (idx == -1 || ref[idx][key] !== undefined) {
                            ref[idx + 1] = [];
                        }
                    } else {
                        if (!ref[lastKey]) {
                            ref[lastKey] = [];
                        }
                    }
                } else { // object
                    if (lastKey == '')  {
                        let idx = ref.length - 1;
                        if (idx < 0 || ref[idx][key] !== undefined) {
                            ref[idx + 1] = {};
                        }
                    } else {
                        if (!ref[lastKey]) {
                            ref[lastKey] = {};
                        }
                    }
                }
                if (lastKey === '') {
                    let idx = ref.length - 1;
                    ref = ref[idx];
                } else {
                    ref = ref[lastKey];
                }
                if (depth === dimensions.length - 1) {
                    if (key === '') {
                        let idx = ref.length;
                        ref[idx] = obj.value;
                    } else {
                        if (!ref[key]) {
                            ref[key] = obj.value;
                        } else {
                            if (ref[key].constructor === Array)
                                ref[key].push(obj.value);
                            else {
                                let temp = ref[key];
                                ref[key] = [temp, obj.value];
                            }
                        }
                    }
                }
                lastKey = key;
            });
        } else {
            if (!ret[obj.name]) {
                ret[obj.name] = obj.value;
            } else {
                if (ret[obj.name].constructor === Array) {
                    ret[obj.name].push(obj.value);
                } else {
                    let temp = ret[obj.name];
                    ret[obj.name] = [temp, obj.value];
                }
            }
        }
    } );
    return ret;
};