4

I've got a jqGrid where I need to change the model after the data is loaded, but before it's parsed into the grid. In otherwords, I think I want to do it in the loadComplete handler. I see this approach: Setting JqGrid's colName and colModel from JSON data, but I'm have a bunch of grids already written that use the "load data with jqGrid" approach, rather than the "pre-load data and pass it to jqGrid" one used there, and I'm hoping to avoid re-coding, or making this one different.

(Hiding and showing hidden columns isn't practical, either.)

Is this possible?

More details:

Basically, I don't know what columns I need till I see the data. Say I'm showing traffic by state:

Date      CA     WA     NY    MN
4/20      100    90     85    72
4/21       95    85     89    70

There's only room to show four states, but there might be many more in the data (or there might be fewer), so I want them listed in order of traffic. Right now, the data is coming in like:

{
date : 4-20,
ca : 100,
wa : 90,
ny : 85,
mn : 72
hi : 56,
il : 30
},
{
date : 4-21,
ca : 95,
wa : 85, // note: for a given row, a column might be relatively lower
ny : 89, // than another. The column order is based on the overall
mn : 70
hi : 60,
il : 45
}

or it could be:

{
date : 4-20,
ny : 110,
hi : 95,
il : 90,
wa : 80
}

I've tried setting up columns like state1, state2, state3, state4 and then using jsonmap to remap them, but it didn't work.

loadonce = true, datatype = json

Community
  • 1
  • 1
sprugman
  • 19,351
  • 35
  • 110
  • 163
  • Could you describe more detailed which modifications in colModel you want to do. Some modifications are easy to do, another not so easy, but some of modification are impossible without recreating the whole grid. So to answer on your question you should post more details. It could be helpful if you post the jqGrid exaple which you use. At least one need to know the datatype and whether you use `loadonce:true`. – Oleg Mar 21 '11 at 22:13
  • @sprugman: It's a good example. Now I understand in general your problem. Are there are many column with the contain of the same type (like integer in the example)? – Oleg Mar 22 '11 at 19:24
  • yes, the columns that need to be dynamic all have the same structure. – sprugman Mar 22 '11 at 20:30
  • @sprugman: If nobody will solve your problem I will try to make some suggestion how to solve it tomorrow. I find your question very interesting so +1 from me, but today I am very busy. – Oleg Mar 22 '11 at 21:32
  • Ok, thanks Oleg. (At the moment, I'm doing two queries: one that just gets the states (not broken out by date), and then I'm using that to build a model, and doing a second query which returns more detail.) – sprugman Mar 22 '11 at 22:18

1 Answers1

7

I found one way which seems work OK.

The idea of my solution is following. You use colModel having many hidden columns with the dummy names like 'cm0', 'cm1', 'cm2', ... All the columns has the same data like you need in your case. To fill the data more easy I use column templates existing since jqGrid 3.8.2:

var mygrid=jQuery("#list"),
    cmIntTemplate = {
        width:50,
        sorttype:"int",
        formatter:"integer",
        align:"right",
        hidden:true
    },
    cm = [
        // here we define the first columns which we always has
        // the list can be empty or has some columns with
        // the properties other as the rest (without cmIntTemplate)
        {name:"date",label:"Date",key:true,width:100, fixed:true,
         formatter:'date',formatoptions:{srcformat:"m-d",newformat:"m/d"}}
    ], maxCol = 30, dummyColumnNamePrefix = "cm";

// Add dummy hidden columns. All the columns has the same template
for (i=0;i<maxCol;i++) {
    cm.push({name:dummyColumnNamePrefix+i,template:cmIntTemplate});
}

After that I create jqGrid in the standard way, but with the jsonReader which use the page as function:

jsonReader: {
    repeatitems: false,
    page: function (obj) {
        // ------------------------
        // here I add the main code
        // ------------------------
        return obj.page;
    }
}

The function from the jsonReader.page return the same value like as do default jsonReader, but I use the way with function because the function will be called directly before the reading of the main contain of JSON data. Inside of the code I get the first row of the data and use it's property names to fill jsonmap property of the corresponding column and set the column name. Additionally I make some dummy columns needed to display all the JSON data visible and the rest dummy column hidden. The last thing which should be done is correction of the grid width which was calculated previously. So the grid will look like this:

enter image description here

or like this

enter image description here

depend on the JSON input data.

The code of the page function is following:

page: function (obj) {
    var rows = obj.rows, colModel = mygrid[0].p.colModel,
        cmi, iFirstDummy, firstRow, prop,
        orgShrinkToFit, isFound,
        showColNames = [], hideColNames = [];

    if (typeof(rows) === "undefined" || !$.isArray(rows) || rows.length === 0) {
        // something wrong need return
        return obj.page;
    }

    // find the index of the first dummy column
    // in the colModel. If we use rownumbers:true,
    // multiselect:true or subGrid:true additional
    // columns will be inserted at the begining
    // of the colModel
    iFirstDummy = -1;
    for(i=0;i<colModel.length;i++) {
        cmi = colModel[i];
        if (dummyTestRegex.test(cmi.name)) {
            iFirstDummy = i;
            break;
        }
    }
    if (iFirstDummy === -1) {
        // something wrong need return
        return obj.page;
    }

    orgShrinkToFit = clearShrinkToFit();

    // we get the first row of the JSON data
    firstRow = rows[0];
    for (prop in firstRow) {
        if (firstRow.hasOwnProperty(prop)) {
            // we will use the properties name of the first row
            // as the names of the column headers of the grid

            // find column index having prop as the name
            isFound = false;
            for(i=0;i<colModel.length;i++) {
                cmi = colModel[i];
                if (cmi.name === prop) {
                    isFound = true;
                    showColNames.push(prop);
                    break;
                }
            }
            if(!isFound) {
                // labels defines the column names
                cmi = colModel[iFirstDummy];
                showColNames.push(cmi.name);
                mygrid.jqGrid('setLabel',cmi.name,prop);

                // because of bug in jqGrid with calculation of width
                // we have to reset the width
                cmi.width = cmIntTemplate.width;

                // we set jsonmap which jqGrid will use instead
                // of dummy column names to read all row data
                cmi.jsonmap = prop;
                iFirstDummy++;
            }
        }
    }

    // fill the list of unused columns
    for(i=0;i<colModel.length;i++) {
        cmi = colModel[i];
        if ($.inArray(cmi.name, showColNames) === -1 && dummyTestRegex.test(cmi.name)) {
            hideColNames.push(cmi.name);
        }
    }
    mygrid.jqGrid('showCol',showColNames);
    mygrid.jqGrid('hideCol',hideColNames);

    setGridWidthAndRestoreShrinkToFit(orgShrinkToFit);

    return obj.page;
}

Inside of the page function I use small helper functions

var clearShrinkToFit = function() {
        // save the original value of shrinkToFit
        var orgShrinkToFit = mygrid.jqGrid('getGridParam','shrinkToFit');
        // set shrinkToFit:false to prevent shrinking
        // the grid columns after its showing or hiding
        mygrid.jqGrid('setGridParam',{shrinkToFit:false});
        return orgShrinkToFit;
    },
    setGridWidthAndRestoreShrinkToFit = function(orgShrinkToFit) {
        // calculate the new grid width
        var width=0, i=0, headers=mygrid[0].grid.headers, l=headers.length;
        for (;i<l; i++) {
            var th = headers[i].el;
            if (th.style.display !== "none") {
                width += $(th).outerWidth();
            }
        }

        // restore the original value of shrinkToFit
        mygrid.jqGrid('setGridParam',{shrinkToFit:orgShrinkToFit});

        // set the grid width
        mygrid.jqGrid('setGridWidth',width);
    },
    dummyTestRegex = new RegExp(dummyColumnNamePrefix+"(\\d)+");

The working demo you can see here.

UPDATED: Another answer with the demo shows how to create the grid which has another format of input data: [[], [], ...] (array of arrays) - matrix.

Community
  • 1
  • 1
Oleg
  • 220,925
  • 34
  • 403
  • 798
  • @sprugman: You are welcome! How I said you before I found your question very interesting so I have to make the implementation. :-) – Oleg Mar 23 '11 at 17:22
  • @sprugman: I answered on another very close question and created the corresponding demo which is the modification of the demo which I created for you. I think this could be probably also interesting for you. See **UPDATED** part of my answer. – Oleg Mar 23 '11 at 17:45
  • @sprugman: I improved a little the code of my demo and replaced the code which I posted in my answer. – Oleg Mar 26 '11 at 20:46