1

I already work a few days at a first glance at the simple task - to implement a custom cell. The task is following: to make a custom cell with a div element with an id (eg "mydiv"), then to call a function for this div like $('#mydiv').raty({start: cellvaue, readonly:true}) and then, the 3rd subtask - in the edit mode (editGridRow) I have to change the parameter of raty-function to readonly:false as it should be possible to change the value.

Firstly, I have worked with formatter. In formatter I defined my div element, and with calling in afterInsertRow my function $('# mydiv').raty({start: cellvalue, readonly: true}). For the overview it worked perfectly. But, in edit modal dialog of editGridRow the form input text was always rendered, which I do not need here. I need here still only my div element. If I understand correctly, the formatter only modify the values, but still renders form input text.

Then I swithed to edittype: custom, but it has not help, because these functions are invoked for the first time only in editGridRow.

I am sure that this problem is solvable, the only question is how.

Thanks for any tips.

UPDATE Thanks to Oleg now I am very close to a functioning implementation of this task. Here I will describe my solution (based on Oleg's advices, or at least on my interpretion of his tips). The jqGrid is defined with datatype: "json". The custom cell is defined as:

name:'ranking', editable:true, edittype:'custom',formatter:ratingFormatter, 
editoptions:{custom_element:ratingFormatter, custom_value:ratingValue}

The mentioned functions are defined as follows:

function ratingFormatter(cellvalue, options, rowObject) {return '<div class="rnk"></div>';}; 

function ratingValue(elem, operation, value) {return $('#ranking-score').val();};

Then modal edit dialog:

ondblClickRow: function(id) {
jQuery('#grid').jqGrid('editGridRow',id,
{closeOnEscape:true,width:400, 
savekey:[true,13],
recreateForm:true,beforeShowForm:initRating
});

The initRating function:

function initRating() {$('#ranking').raty({path:'/img/'})};

And finally the loadComplete event

 loadComplete: function (data) {
 var ids = jQuery("#grid").getDataIDs();
 for(var i=0;i<ids.length;i++){
 var rowid = ids[i];
 var ranking = data.rows[i].cell[6];
 $('#' + rowid +'> td > div.rnk').raty({path:'/img/',start:ranking,readOnly:true});
 $('#' + rowid).contextMenu('MenuJqGrid', eventsMenu);
 }
 }

So many steps for such small thing as rating plugin. Unbelievable. The last unresolved issue is getting the current rating score into initRating function. It means if I go to the editGridRow I do not have already defined rating score. Hmmm.

Anatoliy
  • 321
  • 1
  • 4
  • 19

1 Answers1

3

About the edittype:custom option I recommend you to read this and this old answers. It is important to use recreateForm:true setting to make custom edit functions custom_element and custom_value be called not ony once.

You don't include any code in your question. Your description of the problem allows different interpretations how you implemented what you described. For example it is not clear for me how you change the parameter of raty-function to readonly:false. Do you use beforeShowForm event (see this as an example) or you use dataInit property of the editoptions. In both cases all should work in case of correct implementation.

One more thing which is uncler for me. Why you need to include id="mydiv" in the cell? Is your implementation allows inserting multiple ids with the same name? It would be a bug. If you can find the cell based on the cell contain or the row contain, that you can call .raty({start: cellvaue, readonly:true}) inside of loadComplete event handler and you don't need to insert additional id attribute to the <td> element. The usage of afterInsertRow makes grid more slow, because it force to render the grid on adding every row and not only after all rows will be inserted in the grid (see gridview:true option).

UPDATED: After spending so time for writing comments and after you posted your code I modified the code to show how the raty-plugin could be integrated. As the results is the demo which looks like enter image description here
I used inline editing instead of the form editing only because form editing not full support local editing, but I wanted to make the demo without any server components.

Like in your code I used double-click for the row editing. Here are the main code fragments:

var lastSel;
var grid = $("#list");
var initRaty = function(rowid) {
    var ranking = grid.getCell(rowid,4); // the Ranking column has the index 4
    // because we use rownumbers:true the index of the Ranking column will be 1 higher
    $('#' + rowid +'> td:nth-child(5) > div').raty({
        path:'http://www.ok-soft-gmbh.com/jquery.raty/1.0.1/img/',
        start:ranking,
        readOnly:true
    });
};
grid.jqGrid({
    // ...
    colModel: [
        // other column definition
        { name:'Ranking', editable:true, width: 100, title: false,
          formatter: function(cellvalue, options, rowObject) {
              // insert div needed for the raty plugin
              // and insert a hidden span with the rating value
              return '<div></div><span style="display: none;">'+cellvalue+'</span>';
          }, unformat: function (cellvalue, options, cellobject) {
              // get rating value from the hidden span
              return cellobject.find("span").text();
          }, edittype:'custom', editoptions: {
              custom_element: function(value, options) {
                  var val = $(value);
                  var elem = $('<div id="'+options.id+'"/>');
                  setTimeout(function(){
                      elem.raty({
                          path:'http://www.ok-soft-gmbh.com/jquery.raty/1.0.1/img/',
                          start:(val.length>1? val[1].innerText: value)
                      });
                  }, 100);
                  return elem[0];
              },
              custom_value: function(elem) {
                  return elem.find("input").val();
              }
          }
        }
    ],
    editurl: 'clientArray',
    loadComplete: function (data) {
        var ids = grid.getDataIDs();
        for(var i=0;i<ids.length;i++){
            initRaty(ids[i]);
        }
    },
    ondblClickRow: function(id, ri, ci) {
        grid.jqGrid('editRow',id,true,null,null, 'clientArray', {}, initRaty);
    },
    onSelectRow: function(id) {
        if (id && id !== lastSel) {
            if (typeof lastSel !== "undefined") {
                grid.jqGrid('restoreRow',lastSel);
                var cell = $('#' + lastSel +'> td:nth-child(5)');
                var spans = cell.find('span');
                if (spans.length > 1) {
                    // if the lastSel row was not only selected, but also
                    // was in editing mode, get the hidden text with the ranking
                    var ranking = cell.find('span')[1].innerText;
                    cell.find('div').raty({
                        path:'http://www.ok-soft-gmbh.com/jquery.raty/1.0.1/img/',
                        start:ranking,
                        readOnly:true
                    });
                }
            }
            lastSel = id;
        }
    },
    // other jqGrid parameters
});

If you will use form editing and call editGridRow function instead of editRow you will need to use recreateForm:true option and use afterComplete or afterSubmit to call initRaty with the modified values (exactly like I use aftersavefunc parameter of editRow in my code example).

Community
  • 1
  • 1
Oleg
  • 220,925
  • 34
  • 403
  • 798
  • ad. `recreateForm:true` - I already read this post, it didn't work in my case, i don't know why, but it is not the biggest question now. ad. change of readonly-parameter - i think it is not easy to change this paramater, so i will call the raty-function again with new parameter. ad id='mydiv' - you are right it would be buggy, so i can add to id the rowid. ad loadComplete: I have to pass parameter row.ranking into raty function. can i do this inside of loadComplete? – Anatoliy Jan 30 '11 at 11:23
  • @Anatoliy: Probably you use `recreateForm:true` in the wrong place. If must be a part of `prmEdit` or `prmAdd` parameter of the `navGrid` function (see example in http://stackoverflow.com/questions/3951233/jqgrid-need-a-field-editable-on-add-dialog-but-not-edit-dialog/3952654#3952654) or as a part of general default settings (see http://stackoverflow.com/questions/4644180/jqgrid-common-parameters-of-the-form-editing-dialogs-how/4652201#4652201). Without the setting `recreateForm:true` you can not use `custom_element` and `custom_value` successfully. – Oleg Jan 30 '11 at 12:25
  • @Anatoliy: I am not sure that the changing of `readOnly` are supported for the Star Rating Plugin. On the other side it seems to me that **you don't need it**. If you want edit the row with form editing the **new Star Rating** control will be created in the form editing dialog. If you will use `.raty({readOnly:false})` inside of `dataInit` of the `editoptions` or inside of `beforeShowForm` event you can initialize Star Rating control of the editing form with `readOnly:false` having `readOnly:true` in the grid. – Oleg Jan 30 '11 at 12:31
  • @Oleg, thanks for precise explanation. If I want to have a cell with the Star Rating Module, means without form input elements then edittype:`custom` is only the way to manage it? ad. loadComplete this function is fired once after request is processed. I have to call my function with different parameter. Do I need to go through all data array and then call my function (.raty()) for every row? Is it better than using afterInsertRow? – Anatoliy Jan 30 '11 at 13:11
  • @Anatoliy: `edittype:custom` from one side and loop in `loadComplete` or `afterInsertRow` on the other side do **absolutely different things**. The first (`edittype:custom`) get you possible to make custom control **during editing** and the second allows you insert control in the grid cells. Using constructs like `$("#list > tbody > tr > td:nth-child(3)")` will allows you for select all cells which you need. The usage of additional `filter` function (see http://stackoverflow.com/questions/3781900/jqgrid-search-a-value-on-the-grid/3783086#3783086) allows you to select more specific cells only. – Oleg Jan 30 '11 at 13:29
  • @Oleg: What is best way to iterate through parameter data of loadComplete-function? As I have to go through all `rows` and call `raty()` with row-specific parameter. – Anatoliy Jan 30 '11 at 16:14
  • @Anatoliy: The best way depend on many factors. I don't know in what situation you use the jqGrid: do use use server-based data or you use `datatype:"local"` instead, do you use local data paging or not and so on. The universal way which works in all situation is the usage of `getDataIDs` and iterate over ids (see http://stackoverflow.com/questions/4028019/set-class-or-identifier-on-jqgrid-row-based-on-a-key-value-pair-placed-in-row-li/4028235#4028235 or http://stackoverflow.com/questions/4390999/jqgrid-showlink/4391786#4391786 as an example). – Oleg Jan 30 '11 at 16:34
  • How can I iterate through DOM elements as I have to find specific cell (I marked the cell with class rnk), and then for each of them call the raty-function with cell-specific value. The cell-specific value is contained in data object, how can I get this specific cell-value? It seems that the data object is build like data.rows[i].cell[ii]. But this way to get the cellvalue seems to be not safe enough as I can change the order of cols, and then it won't work. – Anatoliy Jan 30 '11 at 16:39
  • @Oleg, thanks for examples, I have 2 questions in this regard. 1) why you dont use the `data`-object, as we have it as paramater in loadComplete? Instead of using this object you call the function `getCell`. The `data`-object seems to be an indexed array, thats why you dont use it? 2) why you work with `for (var i = 0, l = ids.length; i < l; i++)` and not with `for (var i = 0, i < ids.length; i++)`? What is the benefit of this notion? – Anatoliy Jan 30 '11 at 16:51
  • @Oleg, the next point why I cant use cgrid.jqGrid('getCell',rowid,'ranking') because I have there my div element without any value, as Star Rating plugin requires that. So, I will try to get the value from data object. It seems to work with `data.rows[i].cell[6]` I would be more happy if it would work with `data.rows[i].cell.ranking` but it doesn't work, so I have to do it per index. – Anatoliy Jan 30 '11 at 17:18
  • @Anatoliy: We spend **so much time** in writing the comments. The programming is more easy for me as writing in English. Many things will be more clear if you just append your question with the code. For example if you use `loadonce:true` option you has many other restrictions in the case. For example the format of the `data` parameter of `loadComplete` event handle at the first load depend on the format of the server response (it is described in the `jsonReader` or `xmlReader`). If you use `loadonce:true` option then the next call of `loadComplete` will has `data` parameter in *another* form. – Oleg Jan 30 '11 at 20:12
  • @Anatoliy: I modified my answer. Now I included the working demo. – Oleg Feb 01 '11 at 00:43
  • @Oleg, thank you very much. there is a small bug in your demo, or at least it looks like bug for me. if you click on row, and then another row without changing, raty stars disappear. – Anatoliy Feb 01 '11 at 16:09
  • @Anatoliy: You are right. One should do some additional actions after the call of `restoreRow`. I made the corresponding changes. See updated demo. – Oleg Feb 01 '11 at 19:30
  • @Oleg: you can see "my" implementation there: http://babiychuk.com/demo/en/catalog/index/c_picture the small modification I made is that I retrieve current ranking from data-param of loadComplete directly. it seems sensible in this case. Thanks for your tireless support! – Anatoliy Feb 01 '11 at 19:36
  • @Anatoliy: How I wrote before you have to use `afterComplete` or `afterSubmit` to call `initRaty` with the modified values. Moreover you should either disable Next(previous buttons in the form dialog or implement refreshing of the raty control with the rating value of the next/previous row. – Oleg Feb 01 '11 at 20:08
  • @Oleg: If you look at my demo, you will see that it is not necessary to implement neither afterComplete nor afterSubmit - it just works. Rather annoying is that the ranking with already existing values can not be reseted. It works in your demo, but it doesnt in mine. I am going crazy with this plugin, never ending story. What doesnt work in your demo, is that if user go to inline editing, ranking will be reseted without any interaction of user. not ideal too. – Anatoliy Feb 01 '11 at 22:30
  • @Anatoliy: One not sees the problem which I mean with `afterComplete` or `afterSubmit` because the standard setting `reloadAfterSubmit:true`. The setting mean reloading of the whole grid after form submitting. If you change it to `reloadAfterSubmit:false` you will see what I mean. I agree that raty-plugin is not good for integration in jqGrid and that my demo has yet some implementation problems. It shows only the main idea how one can integrate raty-plugin. I don't use raty-plugin myself and so don't know it. – Oleg Feb 01 '11 at 22:51
  • @Anatoliy: One more remark. I recommend you to use `afterclickPgButtons` for the resetting correct rating in the form after the user click the "Next" or the "Previous" button in the form during row editing. – Oleg Feb 01 '11 at 23:01
  • @Oleg: Thanks for your valuable remarks. Ad. reloadAfterSubmit:false - I do not know why this option should be set to false? As usual people prefer to see "real-time-updates", it is my expreience. For sure, it would be better for the bandwidth, I mean less resource-consumpting. Otherwise, all data will be unchanged unless ranking? It also not logical? ad afterclickPgButtons - at the time you wrote it, I already implemented that with the mentioned event. Wow, I can solve the problem by myself. – Anatoliy Feb 01 '11 at 23:28
  • @Oleg: But what I could not solve by myself is the issue with reset of already existing rankings. It means if the ranking was set once I could not reset it, stupid situation. – Anatoliy Feb 01 '11 at 23:30
  • @Anatoliy: Sorry, but I don't understand what you mean with resetting of "already existing rankings". Could you explain exact scenario where you have the problem? – Oleg Feb 01 '11 at 23:37
  • @Anatoliy: if reloading of grid works quick enough, then the usage of `reloadAfterSubmit:false` has no sense. Why you should spend time to implement something if nobody will see any difference and you will show the most recent data for the user (in case of default`reloadAfterSubmit:true`)? – Oleg Feb 01 '11 at 23:41
  • @Oleg: Ad. problem scenario with reset. If you go to my demo and choose one row with already existing ranking, go into Edit-mode (dblClick) and then try to reset the rating, you will see that this is not possible. That is the problem. Ad. reloadAfterSubmit - thanks for explanation! I will monitor the performance of the grid and then decide. – Anatoliy Feb 02 '11 at 08:50
  • @Anatoliy: Sorry but what you mean under "try to reset the rating"? If I change the rating in the demo and click "Cancel" button or close the dialog the new rating will not saved. If you click "Submit" button after the changing of rating the new rating value will be saved on the server side and the grid will reloaded. So after "Submit" you are unable to go back. Probably there are a misunderstanding because you not use `closeAfterEdit:true` setting. I recommend you to use `recreateForm:true` also for Add option to be sure that you not use Edit dialog as Add dialog. You can use `gridview:true`. – Oleg Feb 02 '11 at 12:06
  • @Oleg: The ranking has 0, 1,..,5Stars. Try to set 0 Stars to the already rated record, that is what I mean with reset the ranking - to set the value of ranking to "0" or better null. It would mean that this record is not rated yet. Thank you very much! – Anatoliy Feb 02 '11 at 12:31
  • @Anatoliy: Now I understand what you mean! It is just pure Raty-Plugin problem. As a workaround you can use `showCancel:true` option for example (see http://www.ok-soft-gmbh.com/jqGrid/Ranking1.htm). – Oleg Feb 02 '11 at 12:48
  • @Oleg: In your demo it works perfectly without showCancel:true. So, I think there is a problem in my implementation. – Anatoliy Feb 02 '11 at 17:13
  • @Anatoliy: For me it seems more as Raty-Plugin problem. In my first demo http://www.ok-soft-gmbh.com/jqGrid/Ranking.htm there are the same problem as in yourth. In the demo it seems also impossible to set 0 as the ranking. – Oleg Feb 02 '11 at 17:28
  • @Oleg, you are right, it's raty setting, but now i am not sure whether is good or bad as it is also not ideal if the user accidentally go through rating and the value goes lost. Hmmm, might be that the raty-setting is good. Otherwise it could be changed in raty directly- from `g("#"+m).mouseleave(function(){ a(k,l.val(),j); });` to `g("#"+m).mouseleave(function(){ a(k,0,j); });` – Anatoliy Feb 02 '11 at 18:53