5

I'm wondering if someone has a creative solution to my problem. I have a repeater that is populated from my database and it is as follows:

<asp:Repeater ID="ResultsTableRepeater" runat="server" OnPreRender="ResultsTableRepeater_PreRender">

        <HeaderTemplate>
            <table class="td-table-bordered" style="font-size: small; width: 90%">
                <tr>
                    <th>Change #</th>
                    <th>Change Title</th>
                    <th>Change Description</th>
                    <th>Clarity Id</th>
                    <th>Package Description</th>
                    <th>Package Name</th>
                    <th>Package Status</th>
                    <th>Assigned To</th>
                    <th>New Package</th>
                </tr>
        </HeaderTemplate>
        <ItemTemplate>
            <asp:Literal runat="server" Text='<%# Eval("ChangeId") %>' ID="IdTag" Visible="false"></asp:Label>
            <tr id="tableRow" class="" data-changeId='<%# Eval("ChangeId") %>' runat="server" style='<%#(Eval("AssignedTo").ToString() == "7" || Eval("AssignedTo").ToString() == "8")? "": "font-weight:bold; background-color:cornsilk" %>'>
                <td><%# Eval("ChangeId") %></td>
                <td><%# Eval("ChangeTitle") %></td>
                <td><%# Eval("ChangeDescription") %></td>
                <td><%# Eval("ClarityId") %></td>
                <td><%# (Eval("PackageId").ToString() == string.Empty) ?  "" 
                    : "<a href=http://dev.rlaninfrastructure.tdbank.ca/RCIViewForm?ChangeId=" + Eval("ChangeId") + "&PackageId=" + Eval("PackageId") + " runat='server' id='RCILink'>" %>
                    <asp:Label ID="ExistingPackageLabel" runat="server" Text='<%# (Eval("PackageId").ToString() == string.Empty) ? "No packages" : Eval("PackageDescription").ToString() %>'>
                    </asp:Label><%# (Eval("PackageId").ToString() == string.Empty) ? "" : "</a>" %>
                </td>
                <td><%# Eval("PackageName") %></td>
                <td>
                    <asp:Label ID="LabRequestedLabel" runat="server" Text='<%# (Eval("PackageStatus").ToString() == "1") ? "Requested" 
                                                                             : (Eval("PackageStatus").ToString() == "2") ? "Built" 
                                                                             : (Eval("PackageStatus").ToString() == "3") ? "NFT"
                                                                             : (Eval("PackageStatus").ToString() == "4") ? "Pilot"
                                                                             : (Eval("PackageStatus").ToString() == "5") ? "Production" 
                                                                             : (Eval("PackageStatus").ToString() == "6") ? "Completed" 
                                                                             : (Eval("PackageStatus").ToString() == "7") ? "Cancelled"
                                                                             : (Eval("PackageStatus").ToString() == "8") ? "Pending" 
                                                                             : "" %>'></asp:Label>
                </td>
                <td><%# (Eval("EmployeeName").ToString() == string.Empty) ? "" : Eval("EmployeeName")%></td>
                <td><%# "<a href=http://dev.rlaninfrastructure.tdbank.ca/RCIViewForm?ChangeId=" + Eval("ChangeId") + " runat='server' id='RCILink'>"%>
                    <asp:Label ID="NewPackageLabel" runat="server" Text="Create New">
                    </asp:Label><%#"</a>" %></td>
            </tr>
        </ItemTemplate>
        <FooterTemplate>
            </table>
        </FooterTemplate>
    </asp:Repeater>

and it produces a table that looks like this

enter image description here

What I've circled are rows with repeating Change Id's that I'd like to default to collapsed but can be clicked to be expanded (as well as clicked again to be collapsed again).

I've begun to implement Jon P's solution below, and I'm almost done! Just the Jquery onclick event.

Below is my codebehind PreRender method.

protected void ResultsTableRepeater_PreRender(object sender, EventArgs e) {
        int previousId = 0;
        int currentId = 0;
        int nextId = 0;

        for (int item = 0; item < ResultsTableRepeater.Items.Count; item++) {
            Literal idTag = (Literal)ResultsTableRepeater.Items[item].FindControl("IdTag");
            Literal classTag = (Literal)ResultsTableRepeater.Items[item].FindControl("ClassTag");
            HtmlTableRow tableRow = (HtmlTableRow)ResultsTableRepeater.Items[item].FindControl("tableRow");
            if (item != ResultsTableRepeater.Items.Count - 1)
                int.TryParse(((Literal)ResultsTableRepeater.Items[item + 1].FindControl("IdTag")).Text.ToString(), out nextId);

            if (int.TryParse(idTag.Text, out currentId)) {
                if (currentId == previousId) {
                    tableRow.Attributes["class"] = "hidden";             
                }
                else if (currentId != previousId && currentId == nextId) {
                    tableRow.Attributes["class"] = "toggler";
                }
            }
            else
                nextId = 0;

            int.TryParse(idTag.Text, out previousId);
        }
    }

Update again: The only thing I have left to fix is the jquery, which expands and collapses the hidden/opened rows on the toggler lines. Below is the jquery and css as per Jon P's help.

$(".toggler").click(function(){
  var idClicked = $(this).data("changeid");

  //Toggle hidden on the sibling rows with the same data id
  $(this).nextAll("[data-changeId='" + idClicked +"']").toggleClass("hidden");

  //Toggle opened status on clicked row
  $(this).toggleClass("opened");
});


.hidden{display:none;}
.toggler td:first-child::after{content:" +";}
.toggler.opened td:first-child::after{content:" -";}

Can someone help me out in this final stretch? I feel like I'm close.

David
  • 573
  • 7
  • 40
  • David, please stop dirtying your question with your most recent implementation logic. First, let me say this isn't a good question. the repeater control doesn't support grouping logic, nor can it facilitate your desired collapse behavior on a grouping logic that it doesn't support. – Brett Caswell Jul 17 '17 at 10:40
  • to address the concept of what you're trying to achieve here, you need to look over those needs. How do I add grouping? That's fairly easy, you just change your data source.. instead `IEnumerable`, you make it `IEnumerable>`, meaning each Item is to be an `IEnumerable`.. this mostly likely means you need to modify your CRUD operations.. where are those in your sample? – Brett Caswell Jul 17 '17 at 10:48
  • I got it working, thank you for your feedback. – David Jul 17 '17 at 12:19

2 Answers2

2

Don't have time for a complete answer as there is a fair bit involved server side so I'll try and give you a broad picture.

You need to look at adding a class to your table row, maybe add asp:literal to your tr to hold the class. Also add an asp:literal for your id, this will give you a control you can get the value from. Perhaps use that literal for a data attribute on the table row.

Then use an OnPreRender event on your Repeater. Here iterate the items of the repeater. Compare the id you have stored in the literal I mentioned above with the id in the previous and next items. If the id is the same as the previous, add a css class you can use to hide it. If the id is differnt to the previous but the same as the next, add a css class to indicate that the row is used to hide and collapse.

Add some CSS to to hide the rows using the class added above.

You want to end up with HTML that resembles the following. I've also added the expand and collapse with jquery

$(document).ready(function() {
  $(".toggler").click(function() {
    var idClicked = $(this).data("changeid");

    //Toggle hidden on the sibling rows with the same data id
    $(this).nextAll("[data-changeId='" + idClicked + "']").toggleClass("hidden");

    //Toggle opened status on clicked row
    $(this).toggleClass("opened");
  });
});
.hidden {
  display: none;
}

.toggler td:first-child::after {
  content: " +";
}

.toggler.opened td:first-child::after {
  content: " -";
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table class="td-table-bordered" style="font-size: small; width:90%">
  <tr>
    <th>Change #</th>
    <th>Change Title</th>
    <th>Change Description</th>
    <th>Clarity Id</th>
    <th>Package Description</th>
    <th>Package Name</th>
    <th>Package Status</th>
    <th>Assigned To</th>
    <th>New Package</th>
  </tr>
  <tr data-changeId="1">
    <td>1</td>
    <td>Change Title</td>
    <td>Change Description</td>
    <td>Clarity Id</td>
    <td>Package Description</td>
    <td>Package Name</td>
    <td>Package Status</td>
    <td>Assigned To</td>
    <td>New Package</td>
  </tr>
  <tr class="toggler" data-changeId="2">
    <td>2</td>
    <td>Change Title</td>
    <td>Change Description</td>
    <td>Clarity Id</td>
    <td>Package Description</td>
    <td>Package Name</td>
    <td>Package Status</td>
    <td>Assigned To</td>
    <td>New Package</td>
  </tr>
  <tr class="hidden" data-changeId="2">
    <td>2</td>
    <td>Change Title</td>
    <td>Change Description</td>
    <td>Clarity Id</td>
    <td>Package Description</td>
    <td>Package Name</td>
    <td>Package Status</td>
    <td>Assigned To</td>
    <td>New Package</td>
  </tr>
  <tr class="hidden" data-changeId="2">
    <td>2</td>
    <td>Change Title</td>
    <td>Change Description</td>
    <td>Clarity Id</td>
    <td>Package Description</td>
    <td>Package Name</td>
    <td>Package Status</td>
    <td>Assigned To</td>
    <td>New Package</td>
  </tr>
  <tr data-changeId="3">
    <td>3</td>
    <td>Change Title</td>
    <td>Change Description</td>
    <td>Clarity Id</td>
    <td>Package Description</td>
    <td>Package Name</td>
    <td>Package Status</td>
    <td>Assigned To</td>
    <td>New Package</td>
  </tr>
  <tr class="toggler" data-changeId="4">
    <td>4</td>
    <td>Change Title</td>
    <td>Change Description</td>
    <td>Clarity Id</td>
    <td>Package Description</td>
    <td>Package Name</td>
    <td>Package Status</td>
    <td>Assigned To</td>
    <td>New Package</td>
  </tr>
  <tr class="hidden" data-changeId="4">
    <td>4</td>
    <td>Change Title</td>
    <td>Change Description</td>
    <td>Clarity Id</td>
    <td>Package Description</td>
    <td>Package Name</td>
    <td>Package Status</td>
    <td>Assigned To</td>
    <td>New Package</td>
  </tr>
</table>

NOTE this could all be done client side, but you would have a brief moment when the table is fully expanded while javascript works out what to hide. That can be a jarring experience for your users.

Jon P
  • 19,442
  • 8
  • 49
  • 72
  • Update again. I have everything working now except for the fact that clicking won't expand or collapse. Would you know why this might be the case? I'm terrible at javascript and jquery so I wouldn't know how to trouble shoot it. The tr class and data-changeid attributes are set up identically to your working example so I'm not sure why the jquery portion doesn't work. – David Jul 10 '17 at 17:59
  • 1
    @David, did you add a reference to `jQuery` library as @Jon P, did in his example? If yes, you might want to add a `console.log('Testing...')` inside this `click` function `$(".toggler").click(function(){ console.log('Testing...'); ...});` If you do see `Testing` printed out in your console, you have `jQuery` and your issue could be potentially either `CSS` that hides and shows the elements or the ID of row that is getting passed. You might want to print `console.log('ID Clicked', idClicked);` to make sure it's the correct row. This will help you to start troubleshooting. – smr5 Jul 10 '17 at 18:49
  • I already have jquery bundled into my application, I tried adding the reference on the page anyways just in case, but that wasn't the issue. The console wasn't logged with any testing text so it seems to be the jquery not being called right? However when I place a breakpoint in chrome on the $(".toggler").click(function(){ var idClicked = $(this).data("changeid"); and it triggered when I opened the page. Is there any reason it would trigger on page load? – David Jul 10 '17 at 19:05
  • 1
    You need to add `$(document).ready` this waits until the page has loaded before assigning the event handlers. I've added this to my code. – Jon P Jul 10 '17 at 22:43
  • It works now. Thank you so much for your tremendous help and patience. I applied the bounty. – David Jul 17 '17 at 12:19
0
Select Distinct from yourtableName

Use distinct in your query it may be occurring because of your query may be or you are using left join with some table if you are using joins then make that inner join repeater will repeats whatever the source will be provided so there is some problem in your query(Data source)

  • No I want all of the results as there are multiple packages to each change. I just want to be able to collapse all of the records together that share the same change #. – David Jun 22 '17 at 23:20
  • try distinct with your query it may be help becoz without knowing relation of your database and tables and of course data we are helpless – Shayan Hafeez Jun 22 '17 at 23:29
  • It's fairly determinable at this point that this is a 1 to Many relationship of `Change` to `IEnumerable` that is being flattened. But, It probably should either avoid that or be unflattened and projected to a strong-typed Model/Poco to facilitate this control.. as a Control View Model of sorts.. i.e. `control.DataSource = Data.GroupBy(item => new {i.ChangeId, ...}).Select(g => new ChangePackages { Change = new Change {g.ChangeId ...}, Packages = g.Select(i => new Package {i.PackageName ...}))` – Brett Caswell Jul 17 '17 at 12:06