Search code examples
javascriptc#asp.netrepeaterhidden

Figure out which HTML table rows in ASP Repeater are Hidden


I have a bootstrap modal that displays a list of titles with a delete button for each title. I created a little sketch to make it clear what I am talking about.

enter image description here

The table is created with a Repeater:

<asp:Repeater ID="rpRelated" runat="server" OnItemDataBound="rpRelated_ItemDataBound">
    <HeaderTemplate>
        <table class="table table-striped table-bordered table-hover">
            <thead>
                <tr>
                    <th style="width: 20%">Title</th>
                    <th style="width: 10%">Delete</i></th>
                </tr>
            </thead>
    </HeaderTemplate>
    <ItemTemplate>
        <tr runat="server" id="related">
            <td>
                <asp:HiddenField runat="server" ID="titleRelatedID" Value='<%# DataBinder.Eval(Container.DataItem , "ID")%>' />
                <asp:TextBox runat="server" ID="relatedTitle" Text='<%# DataBinder.Eval(Container.DataItem , "Title")%>' CssClass="form-control" />
            </td>
            <td>
                <a class="my-red-button" href='javascript:hideRelated(<%# DataBinder.Eval(Container.DataItem , "ID")%>);'>Delete</i></a>
            </td>
        </tr>
    </ItemTemplate>
    <FooterTemplate>
        </table>
    </FooterTemplate>
</asp:Repeater>

I set the related <tr> ID in the Repeater's ItemDataBound method in order to make it dynamic so that I can tell which row to hide (Source of the Idea):

protected void rpRelated_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
    {
        Title title = (Title)e.Item.DataItem;

        var row = e.Item.FindControl("related");
        row.ClientIDMode = ClientIDMode.Static;
        row.ID = "related" + title.ID;
    }
}

When one of the delete buttons (links) is pressed, the following JS function is called:

function hideRelated(num) {
    document.getElementById('related' + num).style.display = 'none';
}

This function hides the row temporarily, yet doesn't actually delete the title.

I don't want any of the titles to actually get deleted until the SAVE button on the bottom is pressed. When the SAVE button is clicked, the following Click Event is called in the CodeBehind, and this is where I am getting stuck:

protected void btnSaveRelated_Click(object sender, EventArgs e)
{
    foreach (RepeaterItem item in rpRelated.Items)
    {
        int ID = Convert.ToInt32(((HiddenField)item.FindControl("titleRelatedID")).Value);

        var a = item.FindControl("related" + ID); //returns null
        if (???title row is hidden???)
        {
            //code to actually delete the Title goes here
        }
    }
}


As I iterate through the Repeater Items, how can I figure out if the corresponding <tr> has the display:none attribute?

I tried accessing the item's row by doing item.FindControl("related" + ID);, yet this doesn't seem to work since it is always returning null (possibly because the ClientIDMode is Static). I though of using a HiddenField like I did with titleRelatedID, yet I couldn't figure out how to update the correct value in the JavaScript code when one of the buttons is clicked. I also though of possibly using Visible=false instead of display:none yet I couldn't either figure out how to work this method in JavaScript.

I would love to get my current method to work, yet I am open to completely different approaches to accomplish my task which is:

  1. Hide a row when the row's delete button or link is clicked
  2. When the save button is pressed, figure out which rows or IDs are missing so I know which Titles need to be deleted

Solution

  • One approach is to use another HiddenField control that tracks the IDs of deleted titles:

    <!-- before/after (not inside) the Repeater -->
    <asp:HiddenField runat="server" ID="deletedRelatedIDs" />
    

    In the hideRelated JavaScript function, append the argument to the hidden field:

    document.getElementById('deletedRelatedIDs').value += num + ' ';
    

    In the btnSaveRelated_Click method, read the value of the hidden field:

    foreach (string id in this.deletedRelatedIDs.Value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
    {
        // Delete the title with this ID.
    }
    

    Security Note: The user can easily tamper with the contents of the hidden field. If necessary, the btnSaveRelated_Click method should validate that each ID is valid and that the user has permission to delete the title.


    Q&A

    As I iterate through the Repeater Items, how can I figure out if the corresponding <tr> has the display:none attribute?

    You can't. When the browser submits the form to the server, the server doesn't receive the values of CSS attributes.

    Why does item.FindControl("related" + ID) always return null?

    Unlike most ASP.NET server control properties, the ID property is not backed by view state, so changes to the ID property aren't preserved across postbacks. Instead, the ID reverts to the value specified in the markup ("related").