0

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.

ReynierPM
  • 17,594
  • 53
  • 193
  • 363
  • 1
    Free jqGrid allows to use `editable` not only as Boolean, but as `"hidden"`, `"disabled"`, `"readonly"` or as callback function. You can use `editable: "hidden"` in for example `"FormId"` column to send the data from the column, but not edit the data from `"FormId"` column. Additionally you can remove unneeded `{name: "Id", jsonmap: "Id", key: true, hidden: true}` column and to add `prmNames: { id: "Id" }`. It fix filling the grid and editing: inline editing will send `Id` property instead of `id` during editing. – Oleg Jan 04 '18 at 15:16
  • @Oleg I know your time is gold but could you post an answer with the explanation above as an example? Also regarding the "send data to the server" I have found [this](https://stackoverflow.com/questions/9777903/in-jqgrid-do-i-have-to-manually-call-saverow-to-trigger-an-ajax-save-request) and [this](http://jsfiddle.net/OlegKi/HJema/209/) but still not clear to me how to achieve this, could you add this too to the answer? – ReynierPM Jan 04 '18 at 15:19
  • I can do that later. Could you prepare jsfiddle demo with some simple test data, which you your code? You can use Echo service of jsfiddle to simulate loading the data from the server. Which data exactly you want to send to the server? From some other column of the editing row or some other dynamic value? I don't see any `editurl` parameter in your grid, which is strange. You want that jqGrid send editing data to the server, but you have to specify the URL (`editurl`) to which the data should be send. You can use Echo service (`editurl: "/echo/json/"`) in the demo for testing in DevTools. – Oleg Jan 04 '18 at 16:16
  • @Oleg I am not so good with JSFiddle but I prepare a "demo" for you [here](https://jsfiddle.net/reynierpm/rmo2370r/14/). I didn't add the remote support cause I don't know how to do it and for some reason Bootstrap modal are messing up with the UX not sure why. I just find out that `editurl` so I have added it on the demo, let me know if you need anything else from me. – ReynierPM Jan 04 '18 at 16:53
  • Which version of Bootstrap, jQuery and jQuery UI you use in your real project? Do you use jQuery UI at all or you use only Bootstrap? I don't understand additionally the goal of `customActionsFormatter`. Could you explain what you try to implement with it? Why you don't use the standard `formatter: "actions"`? – Oleg Jan 04 '18 at 16:56
  • @Oleg jQuery UI - v1.10.3, Bootstrap v2.3.1-j6, jQuery v1.9.1 some of them are really old but I don't have the time to update them to the latest version and doing right away will break the application – ReynierPM Jan 04 '18 at 16:59

1 Answers1

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.

Oleg
  • 220,925
  • 34
  • 403
  • 798
  • I want to show a message after the edit action is successful which means I am returning `1` as JSON from the backend, what callback should I use for this purpose? – ReynierPM Jan 04 '18 at 20:28
  • @ReynierPM: jqGrid interpret editing as successful it HTTP status code of the corresponding request is successful (<300 or 304). You can use `aftersavefunc` callback (`inlineEditing.aftersavefunc`) for a final message. If the editing failed you can use `errorfunc` callback (`inlineEditing.errorfunc`) to "decode" the server response and to display an error message. If you can't return error HTTP status in case of error you can use `successfunc` callback (`inlineEditing.aftersavefunc`) which get server response as parameter and should return `[true]` or `[false, "error text"]` on error. – Oleg Jan 04 '18 at 20:41
  • One thing more, for some reason I can't drag the delete popup and it's showing up at left truncate by the browser window, is there any property where I can center it? Something like `align`? BTW where are the docs for this `formDeleting`? I couldn't find it on the jQgrid Wiki – ReynierPM Jan 04 '18 at 20:50
  • 1
    @ReynierPM: One can define, for example, static `top: 200` and `left: 300` values inside of `formDeleting` or to use `beforeShowForm` or `afterShowForm` callback (`formDeleting.afterShowForm`) to change the position of delete dialog dynamically. See [the old answer](https://stackoverflow.com/a/5720456/315935) or [another one](https://stackoverflow.com/a/22012959/315935) as an example of possible code of `afterShowForm`. – Oleg Jan 04 '18 at 21:09
  • 1
    @ReynierPM: You can specify any option or callback of [delGridRow](http://www.trirand.com/jqgridwiki/doku.php?id=wiki:form_editing#delgridrow) inside of `formDeleting`. It simplify the usage because `delGridRow` can be called **indirectly**. I described that in [the wiki article](https://github.com/free-jqgrid/jqGrid/wiki/New-style-of-usage-options-of-internal-methods). More exact the option is described [here](https://github.com/free-jqgrid/jqGrid/blob/v4.15.2/ts/free-jqgrid.d.ts#L1360) and [here](https://github.com/free-jqgrid/jqGrid/blob/v4.15.2/ts/free-jqgrid.d.ts#L860-L895) in TypeScript. – Oleg Jan 04 '18 at 21:38