14

is there an example of using jqgrid's getChangedCells method to determine if data has changed?

I grepped getChangedCells in the downloadable demos for jqgrid, and could only find the function definition, not example usages of getChangedCells.

What I want to do is save the edits that a user's made if the user clicks on another row. But, I only want to submit the save if the row is dirty.

Thanks in advance, --Nate

Diosney
  • 10,520
  • 15
  • 66
  • 111
Nathan Neff
  • 553
  • 1
  • 6
  • 9

5 Answers5

5

There are no safe dirty flag on the row. You can use the fact that at the beginning of row editing (at the start of the inline editing mode) the method editRow add editable="1" attribute to the grid row (<tr> element). Later the methods saveRow and restoreRow changes the attribute value to editable="0". So the rows of the current page which was at least once in the inline editing mode will have the editable attribute. If the id of the table element is "list" you can find the edited rows with

$("#list tr[editable]")

The ids of the elements of the set are the rowids of the rows.

If you use paging in the grid you should be careful and save the ids of the edited rows on the current page before the changing of the page. The onPaging event would help you here.

In my opinion the best and the most safe way to do what you need is to use aftersavefunc parameter of the editRow or saveRow methods (probably you use directly only editRow). Inside of your aftersavefunc function you can save the id of the modified row in an array/map. This will solve your problem and will safe work.

Oleg
  • 220,925
  • 34
  • 403
  • 798
  • I analized your method because I need what he asks too, but your method give the information of what row has been selected for inline editing at some point, rather than tell wether the data in the row has been __changed__ or not. – Diosney Aug 21 '11 at 21:11
  • 2
    @diosney: In the first sentence of my answer I wrote: "There are no safe dirty flag on the row". In case if you has nothing you can use `$("#list tr[editable]")` to reduce the number of rows which you need send to the server. You can see rows which are definitively not changed. Like I wrote later you can use `aftersavefunc` to trace the changes. It will work safe. – Oleg Aug 22 '11 at 00:29
  • I am trying to do almost the same thing, warning the user when unsaved changes. the above mentioned approach is not applicable, when user changes the value and later reverts it back, it will still consider it as changed. I am using inline edit. Since its been 5 years, do you have any update on this now? Please assist. Thanks – Rustin Cohle Jul 13 '16 at 06:13
  • 1
    @RustinCohle: It's better that you open new question, where you describe exactly what you do and which problem you have. The standard way is `editurl` and to save the data directly on the server. It prevents many problems (processing errors on the server side like concurrency control for example) and simplify the code. Moreover one should don't edit more as one row at the same time. There are more alternatives, but it's vary important to know the exact usage of jqGrid and the version and fork of jqGrid which you use. – Oleg Jul 13 '16 at 09:44
  • @Oleg I am banned from asking a question. Sorry.I am not trying to save. I prompt the user as Do you want to leave? not save. I want the way to track the changes, Saving isnt a problem, because I am not saving. Thanks. I am using Tony's fork. 4.6.0. – Rustin Cohle Jul 13 '16 at 10:57
  • @RustinCohle: I really need to understand what scenario you use to be able to help you. I still don't understand your problem. Which `datatype` you use? Do you use `loadonce: true` or not? In general inline editing allows to specify `aftersavefunc` callback. You can extend the local data with additional property (for example boolean `saved` property). You can grep all items, which have `saved: true` property. Alternatively you can hold the map with ids of saved/modified items and to have the same results. One need just to use `aftersavefunc` callback. – Oleg Jul 13 '16 at 11:09
  • @RustinCohle: The demo https://jsfiddle.net/OlegKi/byygepy3/11/ created for [the answer](http://stackoverflow.com/a/34976517/315935) shows how to set `dirty` property for an local item after modification. It uses jqGrid `afterSetRow` callback which exist on free jqGrid only, but you can use the same idea with old jqGrid 4.6.0 and to use `aftersavefunc` callback of inline editing. – Oleg Jul 13 '16 at 11:15
  • @Oleg I am not trying to save dirty data, I am only trying to warn the user whether to discard or stay. I just want to track if the user has edited the row value or not like if user edits a row some flag needs to be set to true. I am using `datatype: json, loadonce: default` – Rustin Cohle Jul 13 '16 at 11:58
  • @Oleg Thanks and als I want the dirty flag to be set only if the user has changed any value in the row(textbox,select,option). Again, my scenario: Click Editrow -> change some value -> click refresh button(or any server calling function) -> Warn user as you have unsaved data, proceed?. Thanks. – Rustin Cohle Jul 13 '16 at 12:03
  • @RustinCohle: If you use remote datatype without (`datatype: "json"` without `loadonce: true`) then there are exist **no local data** which could be changed by user. Which `editurl` you use? If the user modifies the data with respect of inline editing then the data will be saved on the server and reloading of data will return new data. I can guess that the user is **editing** the data. I mean that some row is in editing mode. You can detect the case by usage `savedRow` parameter of jqGrid. If the length of the array `savedRow` is not 0 then there are exist at least one row in editing mode. – Oleg Jul 13 '16 at 12:15
  • @RustinCohle: Look at the large number of comments about the subject, but I still not sure what problem you really have. I suggested you before to open new question where you post some code, pictures and describe the problem more detailed. It would be really helpful. I'm working now on a solution for one of my customer and it has no relation with jqGrid or JavaScript. Thus posting many comments just take time and bring us not so quickly to the solution of the problem. – Oleg Jul 13 '16 at 12:30
  • Thanks @Oleg. I wish I could post the code. I used your idea of editable attr. However, in inline edit, is there a way to get the defaultValue(original value before editing) of textbox or any other html element of a particular row? Please help – Rustin Cohle Jul 14 '16 at 10:25
  • @RustinCohle: You still don't understand me. Till now you wrote just about "inline editing". I don't know whether you call `editRow` directly (inside of `onSelectRow` for example) or you use `inlineNav` or you use `formatter: "actions"`. All what you explain I can interpret in many ways. Indirectly, from your questions, I guess that you have the problem because you use inline editing in the wrong way (but I don't know how you do it). I think that you try to solve the problem, which should not exist and thus you should fix the code where you use inline editing. Do you tried `aftersavefunc`? – Oleg Jul 14 '16 at 10:47
  • @RustinCohle: I wrote you additionally that jqGrid saves the values from the editing row (original values before editing) in `savedRow` option of jqGrid. Thus `grid.jqGrid("getGridParam", "savedRow")[0]` contains the information which you need. – Oleg Jul 14 '16 at 10:56
  • Thanks @oleg, I am using formatter:actions, to keep a buttong in each row, on clicking i call editRow manually. at the same time as of now i serialize the row data and keeping it and then if the user clicks anywhere without saving, i compare the serialized data and current row data for unsaved changes. this is my current way. – Rustin Cohle Jul 17 '16 at 11:45
  • @RustinCohle: Sorry, but I really have to have the demo or the JavaScript code which shows **exactly** what you do and I need the exact description of the problem. I do my main job, implement solutions for my main customers, post answers on [issues](https://github.com/free-jqgrid/jqGrid/issues) to GitHub and try to answer on questions on the stackoverflow. Every time after I read your last comment *I have to reread all previous one* to remember what problem you have. It spend too many my time. I can't help you till you post either new question on stackoverflow or new issue to free jqGrid. – Oleg Jul 17 '16 at 14:39
4

Finally, I managed to bring a piece of code to detect what we want ;)

Hopefully any jqgrid gurus there (like Oleg), have enough time to review this code and improve it.

The example code will work for detect data changed in a grid with an editable field named "name". If you want to check for changed data in more columns, you have to add the variables after_edit and before_edit asociated with that columns.

To get the previous cell data inside the onSelectRow function, I don't used the getCell method because in the documentation says in red:

Do not use this method when you editing the row or cell. This will return the cell content and not the actuall value of the input element

By disgrace I could check that the documentation was right :(. However the getCell function works properly with the current cell data.

And here is the code:

 // Declare variables used for inline edit functionality.
 var last_selected;
 var before_edit_value;
 var after_edit_value;
 $('#grid-id').jqGrid({
...

onSelectRow: function(row_id){
    if(row_id && row_id !== last_selected) {
        /*
         * Determine if the value was changed, if not there is no need to save to server.
         */
         if (typeof(last_selected) != 'undefined') {
            after_edit_value = $('#grid-id tr#' + last_selected + ' .name_column input').val();
         }

        if (before_edit_value != after_edit_value) {
            /*
             * Save row.
             */
            $('#grid-id').jqGrid(
                'saveRow', 
                last_selected, 
                function(response){
                    /* SuccessFunction: Do something with the server response */

                    return true;    
                }, 
                'http://url.to.server-side.script.com/server-side-script.php', 
                {
                    additional_data: 'example: additional string',
                });
            }
            else {
                /*
                 * Restore the row.
                 */
                $('#grid-id').jqGrid('restoreRow', last_selected);
            }

        before_edit_value   = $('#grid-id').jqGrid('getCell', row_id, 'name');
    }   

    last_selected       = row_id;

    /*
     * Edit row.
     */
    $('#grid-id').jqGrid(
        'editRow', 
        row_id, 
        true, 
        function() {/* OnEditFunction */}, 
        function(response) {
        /* SuccessFunction: Do something with the server response */

        return true;

    }, 
    'http://url.to.server-side.script.com/server-side-script.php', 
    {
        additional_data: 'example: additional string',
    }); 
   },
...
});
Community
  • 1
  • 1
Diosney
  • 10,520
  • 15
  • 66
  • 111
  • 1
    excellent solution. Unfortunately this works only with single column. How to check all editable columns for any column has changed or if some edit has made ? – Andrus Sep 08 '11 at 09:56
2

In one of my projects I did the following: before editing the row I remember row data in global variable and after editing is done just check if row data was changed. Something like this (edit mode activated by double click):

var beforeEditData;

function onGridDblClickRow(id) {
  if (isRowEditable(id)) {
    beforeEditData = grid.getRowData(id);
    grid.editRow(id, true, null, null, 'clientArray', null, onRowAfterEdit);
    ...
  }
}
function onRowAfterEdit(row) {
  var data = grid.getRowData(row);
  if (!isDataChanged(beforeEditData, data)) {        
    return; // No changes
  }
  ... // Save data here
}
function isDataChanged(before, after){
  ... // Allows tricky logic for dirty data, e.g. one may trim spaces etc.
}

Denis The Menace
  • 505
  • 4
  • 10
1

Using MVC4 and JQuery this is what I did

In the View

<script type="text/javascript">

var $grid = $("#Grid");
var lastSelection;
var datachanged = false;

function gridInitialised() {
    var headers = $('th>div>:input');
    for (var h = 0; h < headers.length; headers[h++].onclick = (function () { if (datachanged) { $grid.saveRow(lastSelection); datachanged = false; } }));
}

function editRow(id) {
    if (id && id !== lastSelection) {
        if (datachanged) { $grid.saveRow(lastSelection); datachanged = false; }
        $grid.restoreRow(lastSelection);
        $grid.editRow(id, true);
        var inputs = $('#'+id+'>td>:input[class="editable"]');
        for (var i = 0; i < inputs.length; inputs[i++].onchange = (function () { datachanged = true; }));
        lastSelection = id;
    }
}
</script>

@Html.Trirand().JQGrid(Model.Grid, "Grid")

in the Model

            Grid.ClientSideEvents.RowSelect = "editRow";
            Grid.ClientSideEvents.GridInitialized = "gridInitialised";

The gridInitialised code is to handle changes to the search filter.

Dave

dave
  • 11
  • 1
0

As Oleg mentioned 5 (wow) years ago - I used the saveRow function and passed the flag as extraparam.

something like this, assuming your "model" or a hidden column IsDirty in my case:

onSelectRow: function(id) {
                if (id && id !== lastgridsel) {
                    $("#myGrid").saveRow(lastgridsel, false, "clientArray", { IsDirty: "True" });
                    $("#myGrid").editRow(id, true, null, null, "clientArray");
                    lastgridsel = id;
                }
            },

and then loop through the rows on Save click (external button in my case), something along the lines of:

$("#gridSaveBtn").on("click", function() {
            var batch = new Array();
            var dataIds = $("#myGrid").jqGrid("getDataIDs");
            for (var i = 0; i < dataIds.length; i++) {
                try {
                    $("#myGrid").jqGrid("saveRow", dataIds[i], false, "clientArray");
                    //get row data
                    var data = $("#myGrid").jqGrid("getRowData", dataIds[i]);
                    if (data["IsDirty"] === "True") {
                        batch.push(data);
                    }
                } catch (ex) {
                    alert(ex.Message);
                    $("#myGrid").jqGrid("restoreRow", dataIds[i]);
                }
            }
        });
LazyZebra
  • 1,097
  • 16
  • 24