How to send the data to the server after inline ed

2019-08-28 19:58发布

问题:

I am using jQgrid Free (release 4.15.2) and I need to add the ability to edit rows inline which is not a problem at all because it's pretty easy to setup. Here is the code I am using:

$.jgrid = $.jgrid || {};
$.jgrid.no_legacy_api = true;
$.jgrid.useJSON = true;

$(function () {
    "use strict";

    var $grid = $("#list"),
        pagerSelector = "#pager",
        customAddButton = function (options) {
            $grid.jqGrid('navButtonAdd', pagerSelector, options);
            $grid.jqGrid('navButtonAdd', '#' + $grid[0].id + "_toppager", options);
        };

    $.fn.fmatter.customActionsFormatter = function (cellValue, options, rowData) {
        return '<a href="#" title="Delete selected row"><span class="fa fa-fw fa-trash-o delete_row" data-id="' + rowData.Id + '"></span></a>';
    };

    $grid.jqGrid({
        url: '/ajax/plans_to_forms/get_all',
        datatype: "json",
        colNames: ["", "Id", "Form #", "Form", "Plan", "Class", "Drug"],
        colModel: [
            {name: "act", formatter: "customActionsFormatter", width: 20, search: false},
            {name: "Id", jsonmap: "Id", key: true, hidden: true},
            {name: "FormId", align: 'center', fixed: true, frozen: true, resizable: false, width: 100},
            {name: "FormName", width: 300},
            {name: "PlanName", width: 300},
            {name: "DrugGroupName", width: 300},
            {name: "DrugName", width: 300}
        ],
        cmTemplate: {autoResizable: true, editable: true},
        iconSet: "fontAwesome",
        rowNum: 25,
        guiStyle: "bootstrap",
        autoResizing: {compact: true},
        rowList: [25, 50, 100, "10000:All"],
        viewrecords: true,
        autoencode: true,
        sortable: true,
        pager: pagerSelector,
        toppager: true,
        cloneToTop: true,
        hoverrows: true,
        multiselect: true,
        multiPageSelection: true,
        rownumbers: true,
        sortname: "Id",
        sortorder: "desc",
        loadonce: true,
        autowidth: true,
        autoresizeOnLoad: true,
        forceClientSorting: true,
        shrinkToFit: true,
        navOptions: {
            edit: false,
            add: false,
            del: false,
            search: false
        },
        inlineEditing: {keys: true, defaultFocusField: "DrugGroupName", focusField: "DrugGroupName"},
        onSelectRow: function (rowid, status, e) {
            var $self = $(this), savedRow = $self.jqGrid("getGridParam", "savedRow");

            if (savedRow.length > 0 && savedRow[0].id !== rowid) {
                $self.jqGrid("restoreRow", savedRow[0].id);
            }

            $self.jqGrid("editRow", rowid, {focusField: e.target});
        }
    }).jqGrid('navGrid', pagerSelector, {
        search: false,
        edit: false,
        add: false,
        del: false,
        refresh: true,
        cloneToTop: true
    }).jqGrid("filterToolbar", {
        stringResult: true, searchOnEnter: false, defaultSearch: 'cn'
    }).jqGrid("gridResize").jqGrid('setFrozenColumns');

    customAddButton({
        caption: 'Delete selected',
        buttonicon: 'fa-trash-o',
        title: "Delete all selected rows",
        onClickButton: function () {
            var rowIds = $("#list").jqGrid('getGridParam', 'selarrrow');

            if (rowIds.length > 0) {
                delete_all_link_modal.modal();
                delete_all_link_modal.attr('data-link-ids', rowIds);
            } else {
                alert('You must select at least one item.');
            }
        }
    });
});

The following line enables the inline editing:

inlineEditing: {keys: true, defaultFocusField: "DrugGroupName", focusField: "DrugGroupName"}

Where is my problem? I need to edit only the column DrugGroupName and the line above make the entire row editable which leads me to the following questions:

  • It's possible to edit only a given set of columns instead of all of them? - I was checking docs here but I could not find anything helpful

  • It's possible to send the data to the server as soon as I click in any other place or by hitting the ENTER key? - I want to avoid the extra click on the save icon.

UPDATE: I have found the answer for my first question already. I just need to make the column not editable while defining the colModel. Ex:

colModel: [
    {name: "act", formatter: "customActionsFormatter", width: 20, search: false},
    {name: "Id", jsonmap: "Id", key: true, hidden: true},
    {name: "FormId", align: 'center', fixed: true, frozen: true, resizable: false, width: 100, editable: false},
    {name: "FormName", width: 300, editable: false},
    {name: "PlanName", width: 300, editable: false},
    {
        name: "DrugGroupName",
        width: 300,
        edittype: "select",
        editoptions: {
            generateValue: true,
            selectFilled: function (options) {
                setTimeout(function () {
                    $(options.elem).select2({
                        width: "100%"
                    });
                }, 0);
            }
        },
        stype: "select", searchoptions: {
            sopt: ["eq", "ne"],
            generateValue: true,
            noFilterText: "Any",
            selectFilled: function (options) {
                $(options.elem).select2({
                    width: "100%"
                });
            }
        }
    },
    {name: "DrugName", width: 300, editable: false}
]

That way I am forcing DrugGroupName to be the only one editable.

回答1:

I think that your code have many small problems. I prepared the demo https://jsfiddle.net/OlegKi/rmo2370r/19/, which should fix the most problems and demonstrates the usage of select2 and some features of free jqGrid.

The first small problem is the usage of correct rowid. You use currently hidden column

{name: "Id", jsonmap: "Id", key: true, hidden: true}

It's typical misunderstanding of users, who use jqGrid. Rowid will be saved as id attribute of rows (<tr> elements). See the picture. One don't need to place the case information as hidden <td> element inside of the grid. Instead of that one can just use the following jqGrid options

prmNames: { id: "Id" },
jsonReader: { id: "Id" },

instead. The option jsonReader.id informs jqGrid where to get rowid during filling the grid and prmNames.id provides the name of id during editing the grid.

To fill jqGrid inside of JSFiddle one can use Echo service:

url: '/echo/json/',
datatype: 'json',
mtype: 'POST', // required for '/echo/json/'
postData: {
    json: JSON.stringify(mydata)
},

The request to the URL /echo/json/ will mydata as the response. One can use Network tab of Developer Tools of Chrome/IE/Firefox to examine the HTTP traffic in details.

In the same way one can use

editurl: '/echo/json/',
formDeleting: {
    url: '/echo/json/',
    ...
}

for inline editing and form deleting.

The next changes. I added resetWidthOrg: true property in autoResizing:

autoResizing: {
    compact: true,
    resetWidthOrg: true
}

which changed the results of working autowidth: true in combination with autoresizeOnLoad: true. You can see that the width of all columns are based on the content of the columns much better as before. See the issues for more details.

I didn't understood the goal of customActionsFormatter. I replaced it to the standard formatter actions

{ name: "act", template: "actions" }

Free jqGrid allows very easy to customize the action buttons if required. See the answer and the wiki article for more details.

Your old code used

cmTemplate: {
  autoResizable: true,
  editable: true
}

and set editable: false in the most columns. Instead of that you need just remove editable: true from cmTemplate, add editable: true only in one column, which you need to edit, and to include in cmTemplate other setting mostly common used in colModel:

cmTemplate: {
    width: 300,
    autoResizable: true
}

A lot of other code could be simplified too. See the modified code of onSelectRow for example.

To customize delete dialog one can use the following settings:

formDeleting: {
    url: '/echo/json/', // '/ajax/plans_to_forms/delete/' in final solution
    width: 320,
    caption: 'Delete Plan to Form Link',
    msg: 'Are you sure you want to delete this link?',
    beforeShowForm: function ($form) {
        var rowids = $form.find("#DelData>td").data("rowids");
            console.log(rowids);
            if (rowids.length > 1) {
            $form.find("td.delmsg")
                    .html('Are you sure you want to delete all the selected form links?');
            }
    }
}

Delete send the data Id=20622,20626 and oper=del to the server (formDeleting.url). One can use serializeDelData to convert the data to JSON if it's required.

To send more data from columns to the server during editing one can add editable: "hidden" in some column. I added the property in FormId column of the demo and the data sending to the server during editing looked like

{"FormId":"3393","DrugGroupName":"Some other value","oper":"edit","Id":"20620"}

To fill the data of <select> with respect of additional Ajax request to the server one need to use editoptions.dataUrl. I added in the demo editoptions.postData to simulate only the real request to the server:

editoptions: {
    dataUrl: "/echo/json/",
    postData: {
        json: JSON.stringify([
                "Non-Specialty Medications",
                "General Pharmacy Authorization",
                "Some other value"
            ])
        },
        buildSelect: function (data) {
            var select = "<select>", i;

            for (i = 0; i < data.length; i++) {
                select += "<option value='" + String(data[i]).replace(/\'/g, "&#39;") +
                            "'>" + $.jgrid.htmlEncode(data[i]) + "</option>"
            }
            return select + "</select>";
        },
        selectFilled: function(options) {
            var $self = $(this);

            setTimeout(function() {
                $(options.elem).select2({
                    width: "100%"
                }).on('select2:select', function (e) { 
                    // save the data on selection
                    $self.jqGrid("saveRow", options.rowid);
                });
            }, 0);
        }
    },
    stype: "select",
    searchoptions: {
        sopt: ["eq", "ne"],
        generateValue: true,
        noFilterText: "Any",
        selectFilled: function(options) {
            $(options.elem).select2({
                width: "100%"
            });
        }
    }
}

The above request to dataUrl returns JSON string [ "Non-Specialty Medications", "General Pharmacy Authorization", "Some other value" ] and buildSelect converts the data to HTML fragment with <select> contains all the <options>. The resulting <select> will be converted to select2 contril inside of selectFilled callback. Finally the code use

ajaxSelectOptions: {
    type: "POST",
    dataType: "json"
}

option to change the parameters of Ajax request to dataUrl. The demo https://jsfiddle.net/OlegKi/rmo2370r/19/ contains some other minor changes, like removing unneeded empty pager div and the usage of pager: true in the same way like you use already toppager: true. It's one more feature, which I implemented in free jqGrid fork to simplify the usage of jqGrid.