Results 1 to 3 of 3

Thread: [jQuery] GridView - Find next and previous rows

Threaded View

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    [jQuery] GridView - Find next and previous rows

    Hi,

    I am trying to build myself a grid that displays some very simple data (a name and a price of food items on a menu) which the user can very easily edit. The idea is that the grid is always editable, and editing, deleting, adding a new row and hopefully swapping two rows can be done without posting back to the server (at all, not even in an update panel or something).


    So far I managed to get deleting to work, but it requires a bit of an explanation. I have a pretty good idea of how to implement adding new rows so I will try that myself. The problem at the moment is swapping rows.


    Be warned, this post is very long, but the questions at the end might be simple to answer even without any background information. I'm still going to provide the background information though, you will most probably need it. If you don't have time, can't be bothered to read it, or whatever, look for a bold line "TOO LONG TO READ" at the bottom and start there; the two questions are there.



    First of all, the data I am displaying in the grid are instances of the MenuItem class, which has properties:
    • Id (integer, unique identifier)
    • Name (string)
    • Price (float)



    I'll explain this step by step:


    Saving updates

    The grid is a GridView with 6 rows:
    • TemplateField with 'move up button'
    • TemplateField with 'move down button'
    • TemplateField with HiddenField that contains the unique Id of the menu item
    • TemplateField with a TextBox that contains the Name of the menu item
    • TemplateField with a TextBox that contains the Price of the menu item
    • TemplateField with a 'delete' button

    So while there's 6 rows, the third row is invisible because it only contains a HiddenField. The HiddenField is there so I can find the Id of the menu items in javascript.
    asp Code:
    1. <asp:HiddenField runat="server" ID="hfIds" />
    2.  
    3.             <asp:GridView runat="server" ID="grid" AutoGenerateColumns="False"
    4.                 onrowcommand="grid_RowCommand" onrowdeleting="grid_RowDeleting">
    5.                 <Columns>
    6.  
    7.                     <!-- Move Up button -->
    8.                     <asp:TemplateField>
    9.                         <ItemTemplate>
    10.                             <asp:LinkButton runat="server" OnClientClick="return false;">
    11.                                 <asp:Image ImageUrl="~/Images/moveUp.png" AlternateText="moveUp" runat="server" CssClass="moveUp" ID="moveUp" />
    12.                             </asp:LinkButton>
    13.                         </ItemTemplate>
    14.                     </asp:TemplateField>
    15.                    
    16.                     <!-- Move Down button -->
    17.                     <asp:TemplateField>
    18.                         <ItemTemplate>
    19.                             <asp:LinkButton runat="server" OnClientClick="return false;">
    20.                                 <asp:Image ImageUrl="~/Images/moveDown.png" AlternateText="moveDown" runat="server" CssClass="moveDown" ID="moveDown" />
    21.                             </asp:LinkButton>
    22.                         </ItemTemplate>
    23.                     </asp:TemplateField>
    24.                    
    25.                     <!-- ID Hidden Field -->
    26.                     <asp:TemplateField>
    27.                         <ItemTemplate>
    28.                                 <asp:HiddenField runat="server" Value='<%# DataBinder.Eval(Container.DataItem, "Id") %>' />
    29.                         </ItemTemplate>
    30.                     </asp:TemplateField>
    31.  
    32.                     <!-- Name textbox field -->
    33.                     <asp:TemplateField HeaderText="Naam">
    34.                         <ItemTemplate>
    35.                                 <asp:TextBox runat="server" Width="200px" Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>' />
    36.                         </ItemTemplate>
    37.                     </asp:TemplateField>
    38.  
    39.                     <!-- Price textbox field -->
    40.                     <asp:TemplateField HeaderText="Prijs">
    41.                         <ItemTemplate>
    42.                                 <asp:TextBox runat="server" Width="50px" Text='<%# DataBinder.Eval(Container.DataItem, "Price") %>' />
    43.                         </ItemTemplate>
    44.                     </asp:TemplateField>
    45.  
    46.                     <!-- Delete button -->
    47.                     <asp:TemplateField>
    48.                         <ItemTemplate>
    49.                             <asp:LinkButton runat="server" OnClientClick="return false;">
    50.                                 <asp:Image ImageUrl="~/Images/delete.png" AlternateText="delete" runat="server" CssClass="delete" ID="delete" />
    51.                             </asp:LinkButton>
    52.                         </ItemTemplate>
    53.                     </asp:TemplateField>
    54.                 </Columns>
    55.             </asp:GridView>

    As you can see, there's no regular BoundFields or labels, everything editable (name and price) is in a TextBox all the time so the user can edit it whenever he wants.
    Obviously when he makes an edit, nothing happens on the server.

    When the user now clicks the Save button (a regular button on the bottom of the page), I run a Save method which does this:
    • Loop through every row in the grid
    • Find the Id, Name and Price via the controls on that row
    • Retrieve the menu item from the database with the specified Id (if none is found, I create a new MenuItem)
    • Save it to the database

    Or, in code:
    csharp Code:
    1. int sortId = 0;
    2.             foreach (GridViewRow row in grid.Rows)
    3.             {
    4.                 // Find the controls that contain the Id, Name and Price:
    5.                 var idHf = (HiddenField) row.Cells[IDFIELD_INDEX].Controls[1];
    6.                 var nameTb = (TextBox)row.Cells[IDFIELD_INDEX + 1].Controls[1];
    7.                 var priceTb = (TextBox)row.Cells[IDFIELD_INDEX + 2].Controls[1];
    8.  
    9.                 // Find the values
    10.                 int id = int.Parse(idHf.Value);
    11.                 string name = nameTb.Text.Trim();
    12.                 float price = float.Parse(priceTb.Text); // Should use TryParse, irrelevant at the moment
    13.            
    14.                 // Retrieve the MenuItem by Id, or create a new one if it doesn't exist
    15.                 var menuItem = menuItems.FromId(id);
    16.                 if (menuItem == null) menuItem = new MenuItem();
    17.                
    18.                 // Set new values
    19.                 menuItem.Name = name;
    20.                 menuItem.Price = price;
    21.                 menuItem.SortId = sortId;
    22.  
    23.                 // Increment sort id (grid will be sorted by this sort id)
    24.                 sortId++;
    25.  
    26.                 // Save to database
    27.                 MenuItemManager.Instance.Save(menuItem);                            
    28.             }
    This happens only when the Save button is clicked, not every time an edit is made.

    NOTE: Obviously this will fail horribly when there's other stuff on the page that might trigger a postback (the grid will be reloaded and the edited values lost), but there's not, so don't worry about that.

    Anyway, this works fine, on to deleting.


    Deleting rows
    To delete a row, I had the following train of thought:
    • When the Delete button is clicked, find the row that belongs to that button
    • Find the hidden field that stores the Id (in the third column)
    • Append this Id to another HiddenField (this one on the page itself) so I can see later which Ids are to be removed from the database
    • Remove the row from the table (grid)


    After some fiddling around, a slightly modified version seems to work very well. The delete button is actually an empty LinkButton with an Image inside, and I found some jQuery code that responded to a click on an image in a grid and found the row that belonged to that image. I'm sure I could have done the same with the LinkButton directly, but the image works too.
    I can use the jQuery code to find the first input field in this row (which in this case is the Id hidden field), so that's what I did. Then I delete the row (again via jQuery) and append the Id to the other hidden field:
    Code:
    $(document).ready(function () {
    
                $('table td img.delete').click(function () {
    
                    if (confirm('Are you sure you want to delete this row?')) {
    
                        // Get the hidden field that stores the IDs to delete later
                        var hf = document.getElementById('<%= hfIds.ClientID %>');
    
                        // Get the hidden field in the grid row that contains the ID of the clicked item
                        var itemHf = $(this).parent().parent().parent().find(':input');
    
                        // Append the ID to the list of IDs (separated by | character) in the hidden field
                        hf.value = hf.value + "|" + itemHf.val();
    
                        // Remove grid row
                        $(this).parent().parent().parent().remove();
                    }
    
                    return false;
                });
        });
    This also works. The grid row is deleted visually on the page and the Id is appended to the HiddenField 'hfIds'.
    I had to modify the Save method because the 'deleted' row is actually still in the grid.Rows collection. Luckily, the value in the Id hidden field (in the third column) is cleared, so that I can see from this that this is a deleted row.
    Then, after saving the remaining rows, I parse the 'hfIds' hidden field (split it by |) and remove each id from the database:
    csharp Code:
    1. int sortId = 0;
    2.             foreach (GridViewRow row in grid.Rows)
    3.             {
    4.                 // Find the controls that contain the Id, Name and Price:
    5.                 var idHf = (HiddenField) row.Cells[IDFIELD_INDEX].Controls[1];
    6.                 var nameTb = (TextBox)row.Cells[IDFIELD_INDEX + 1].Controls[1];
    7.                 var priceTb = (TextBox)row.Cells[IDFIELD_INDEX + 2].Controls[1];
    8.  
    9.                 if (idHf.Value == "")
    10.                 {
    11.                     // This is a deleted row! Skip it, we don't need to save it
    12.                     continue;
    13.                 }
    14.                
    15.                 // Find the values
    16.                 int id = int.Parse(idHf.Value);
    17.                 string name = nameTb.Text.Trim();
    18.                 float price = float.Parse(priceTb.Text); // Should use TryParse, irrelevant at the moment
    19.            
    20.                 // Retrieve the MenuItem by Id, or create a new one if it doesn't exist
    21.                 var menuItem = menuItems.FromId(id);
    22.                 if (menuItem == null) menuItem = new MenuItem();
    23.                
    24.                 // Set new values
    25.                 menuItem.Name = name;
    26.                 menuItem.Price = price;
    27.                 menuItem.SortId = sortId;
    28.  
    29.                 // Increment sort id (grid will be sorted by this sort id)
    30.                 sortId++;
    31.  
    32.                 // Save to database
    33.                 MenuItemManager.Instance.Save(menuItem);                            
    34.             }
    35.            
    36.             // When we are done saving, we retrieve the Ids
    37.             // of the menu items that need to be deleted from the database:
    38.            
    39.             // hfIds.Value contains something like '|3|4|16|93'
    40.             // this indicates rows 3, 4, 16 and 93 need to be deleted
    41.            
    42.             var removeIds = hfIds.Value.Split(new[] {'|'}, StringSplitOptions.RemoveEmptyEntries);
    43.             foreach (var strId in removeIds)
    44.             {
    45.                 int id = int.Parse(strId);
    46.                 MenuItemManager.Instance.DeleteById(id);
    47.             }

    It works!


    Moving rows:

    To move rows up or down I came up with the following idea (assuming moving a row up, but down works the same way):
    • Find the Id, Name and Price of the row where the 'move up' button was clicked (same as before with a twist)
    • Find the previous row
    • Find the Id, Name and Price of the previous row
    • Simply swap the values (swap id of current row with id of previous row, same for name and price)

    That should be it if I'm not mistaken. The Save method will work the same way.



    TOO LONG TO READ, CONTINUE HERE:

    I have two problems with this approach though (finally, my question...)



    1. I have a table with some rows which contain LinkButtons. I am handling a click event of an image in such a LinkButton.
    I can get the row of the button that was clicked using this jQuery:
    Code:
    var row = $(this).parent().parent().parent();
    the first parent is the LinkButton (remember, I'm handling the click event of an image in the LinkButton), the second is the cell and the third is the row.

    But how do I get the previous or next row in the table?



    2. I can get the hidden field in the row that contains the Id using this:
    Code:
    var hf = $(this).parent().parent().parent().find(':input');
    I don't really understand, but I interpret the 'find' as returning the first element that is an input element, in this case that is the hidden field. There's two images as well but those are not inputs so they aren't found. However, there's a name and price input after the hidden field, but I am assuming it returns the first element.

    How do I find the second and third input elements? I will also need the name and price inputs if I want to swap them with the previous row, but as far as I can see this only gives me the first input, I need them all!



    Thanks for any help!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width