Showing posts with label dynamic. Show all posts
Showing posts with label dynamic. Show all posts

Thursday, February 10, 2011

jqgrid - dynamically load different drop down values for different rows depending on another column value

Goal:

As we all know the jqGrid examples in the demo and the Wiki always refer to static values for drop down boxes. This of course is a personal preference but in dynamic design these values should be populated from the database/xml file, etc, ideally JSON formatted.
Can you do this in jqGrid, yes, but with some custom coding which we will briefly show below (refer to some of my other blog entries for a more detailed discussion on this topic).
What you CANNOT do in jqGrid, referring here up and to version 3.8.x, is to load different drop down values for different rows in the jqGrid. Well, not without some trickery, which is what this discussion is about.

Issue:

Of course the issue is that jqGrid has been designed for high performance and thus I have no issue with them loading a  reference to a single drop down values list for every column. This way if you have 500 rows or one, each row only refers to a single list for that particular column. Nice!
SO how easy would it be to simply traverse the grid once loaded on gridComplete or loadComplete and simply load the select tag's options from scratch, via Ajax, from memory variable, hard coded etc? Impossible! Since their is no embedded SELECT tag within each cell containing the drop down values (remember it only has a reference to that list in memory), all you will see when you inspect the cell prior to clicking on it, or even before and on beforeEditCell, is an empty .
When trying to load that list via a click event on that cell will temporarily load the list but jqGrid's last internal callback event will remove it and replace it with the old one, and you are back to square one.

Solution:

Yes, after spending a few hours on this found a solution to the problem that does not require any updates to jqGrid source code, thank GOD!
Before we get into the coding details, the solution here can of course be customized to suite your specific needs, this one loads the entire drop down list that would be needed across all rows once into global variable. I then parse this object that contains all the properties I need to filter the rows depending on which ones I want the user to see based off of another cell value in that row. This only happens when clicking the cell, so no performance penalty. You may of course to load it via Ajax when the user clicks the cell, but I found it more efficient to load the entire list as part of jqGrid's normal editoptions: { multiple: false, value: listingStatus } colModel options which again keeps only a reference to the single list, no duplication.
Lets get into the meat and potatoes of it.
        var acctId = $('#Id').val();

        var data = $.Ajax({ url: $('#ajaxGetAllMaterialsTrackingLookupDataUrl').val(), data: { accountId: acctId }, dataType: 'json', async: false, success: function(data, result) { if (!result) alert('Failure to retrieve the Alert related lookup data.'); } }).responseText;
        var lookupData = eval('(' + data + ')');

        var listingCategory = lookupData.ListingCategory;

        var catList = '{';
        $(lookupData.ListingCategory).each(function() {
            catList += this.Id + ':"' + this.Name + '",';
        });
        catList += '}';



        var lastsel;
        var ignoreAlert = true;
        $(item)
        .jqGrid({
            url: listURL,
            postData: '',
            datatype: "local",
            colNames: ['Id', 'Name', 'Commission
Rep', 'Business
Group', 'Order
Date', 'Edit', 'TBD', 'Month', 'Year', 'Week', 'Product', 'Product
Type', 'Online/
Magazine', 'Materials', 'Special
Placement', 'Logo', 'Image', 'Text', 'Contact
Info', 'Everthing
In', 'Category', 'Status'],
            colModel: [
                { name: 'Id', index: 'Id', hidden: true, hidedlg: true },
                { name: 'AccountName', index: 'AccountName', align: "left", resizable: true, search: true, width: 100 },
                { name: 'OnlineName', index: 'OnlineName', align: 'left', sortable: false, width: 80 },
                { name: 'ListingCategoryName', index: 'ListingCategoryName', width: 85, editable: true, hidden: false, edittype: "select", editoptions: { multiple: false, value: eval('(' + catList + ')') }, editrules: { required: false }, formatoptions: { disabled: false} }

            ],
            jsonReader: {
                root: "List",
                page: "CurrentPage",
                total: "TotalPages",
                records: "TotalRecords",
                userdata: "Errors",
                repeatitems: false,
                id: "0"
            },
            rowNum: $rows,
            rowList: [10, 20, 50, 200, 500, 1000, 2000],
            imgpath: jQueryImageRoot,
            pager: $(item + 'Pager'),
            shrinkToFit: true,
            width: 1455,
            recordtext: 'Traffic lines',
            sortname: 'OrderDate',
            viewrecords: true,
            sortorder: "asc",
            altRows: true,
            cellEdit: true,
            cellsubmit: "remote",
            cellurl: editURL + '?rows=' + $rows + '&page=1',
            loadComplete: function() {

            },
            gridComplete: function() {

            },
            loadError: function(xhr, st, err) {

            },
            afterEditCell: function(rowid, cellname, value, iRow, iCol) {
                var select = $(item).find('td.edit-cell select');
                $(item).find('td.edit-cell select option').each(function() {
                    var option = $(this);
                    var optionId = $(this).val();
                    $(lookupData.ListingCategory).each(function() {
                        if (this.Id == optionId) {                          
                            if (this.OnlineName != $(item).getCell(rowid, 'OnlineName')) {
                                option.remove();
                                return false;
                            }
                        }
                    });
                });
            },

            search: true,
            searchdata: {},
            caption: "List of all Traffic lines",
            editurl: editURL + '?rows=' + $rows + '&page=1',
            hiddengrid: hideGrid


Here is the JSON data returned via the Ajax call during the jqGrid function call above (NOTE it must be { async: false}:
{"ListingCategory":[{"Id":29,"Name":"Document Imaging","OnlineName":"RF Globalnet"}   
,{"Id":1,"Name":"Ancillary Department","OnlineName":"Healthcare Technology Online"} 
,{"Id":2,"Name":"Asset Tracking","OnlineName":"Healthcare Technology Online"}   
,{"Id":3,"Name":"Asset Tracking","OnlineName":"RF Globalnet"}   
,{"Id":4,"Name":"Asset Tracking","OnlineName":"ISMR"}   
,{"Id":5,"Name":"Document Imaging","OnlineName":"Healthcare Technology Online"} 
,{"Id":6,"Name":"Document Imaging","OnlineName":"RF Globalnet"}   
,{"Id":7,"Name":"EMR/EHR Software","OnlineName":"Healthcare Technology Online"}]}
I only need the Id and Name for the drop down list, but the third column in the JSON object is important, it is the only that I match up with the OnlineName in the jqGrid column, and then in the loop during afterEditCell simply remove the ones I don't want the user to see. That's it!

Monday, June 29, 2009

jqGrid Dynamically loading select tag

Scenario: You are using the jqGrid to edit rows that contain fields that are of HTML tag type "SELECT".

Problem: You do not want to hard code the values of the select tag like in the jqGrid samples. For example:

editoption: { value: "FE:FedEx; IN:InTime; TN:TNT" }
Solution: For example to load a list of countries dynamically, define the variable before the definition of the jqGrid:

//get all countries
var countries = $.ajax({url: $('#ajaxAllCountriesUrl').val(), async: false, success: function(data, result) {if (!result) alert('Failure to retrieve the Countries.');}}).responseText;


Now define your jqGrid:

$(yourgrid).jqGrid({
...
colModel: [
{ name: 'Id', index: 'Id', width: 20, sortable: true, align: "center", editable: false, editoptions: { readonly: true, size: 0 }, search: true },

{ name: 'Country', index: 'Country', width: 80, sortable: true, editable: true, edittype: "select", editrules: { required: true }, editoptions: { size: 71} }
],
...
loadComplete: function() {
$(item).setColProp('Country', { editoptions: { value: countries} });
},
...
Important: The ajax call to retrieve the countries must be set to "async: false" otherwise you have a very good chance that the grid will be defined before the data is actually returned. Using the same logic you can reload the list based on some other event and trigger the reload of the grid if need be to reload the new list.