Adding Expand/Collapse Logic to your Data View Web Parts (DVWPs) –The Easy Way
Live and learn. Maybe that’s my mantra. One thing I can tell you is that the first time I come up with a way to do something, it’s rarely the best. If I look at some old code of mine and think it’s perfect, I worry. We all are learning all the time, and it’s usually the case that we can see a better way to do things the next time we look.
Adding expand/collapse logic to a Data View Web Part (DVWP) is a great example. I’ve done this many, many times, and suddenly last week, I looked at a DVWP and saw it all again for the first time.
Part of the reason I saw it differently was that I was looking at the document Object Model (DOM) in the context of doing some jQuery spiffiness. Because of that, I was looking at how SharePoint itself does the expand/collapse stuff. You know what I mean:
When I set the view above to group by Navigation Area, I get different sections in it for each value of Navigation Area, in this case [Meats, Vegetables].
So how does it work? Well, simply, SharePoint lists the data in table rows (TRs) beneath the Group By values with the style set to display:none; (Assuming you have the view set to collapse by default, which is probably the most common setting.) That’s right, SharePoint just shows each group and item in a table row, no fancy wrapping DIVs logic or anything (which is always what I’d tried to do in the past, successfully, but it was usually pretty messy).
What SharePoint does (yes, SharePoint is anthropomorphic to me and I’m sticking to that) is give the table rows which are group headers IDs like this:
<tr id="group0{generate-id()}">
The generate-id() bit is what SharePoint Designer adds for you to make the group IDs unique; the generated IDs look something like:
Each of the group rows contain markup that looks something like this:
<TD class=ms-gb colSpan=100 noWrap> <IMG alt="" src="/_layouts/images/blank.gif" width=0 height=1> <A onclick="javascript:ExpCollGroup('1-2_','img_1-2_');return false;" href="javascript:"> <IMG id=img_1-2_ border=0 alt=Expand/Collapse src="/_layouts/images/minus.gif"> </A> <A onclick="javascript:ExpCollGroup('1-2_','img_1-2_');return false;" href="javascript:">Navigation Area</A> : <A href="/Intranet/JQueryLib/Lists/Regions/DispForm.aspx?ID=7&RootFolder=*">Meats</A> <SPAN style="FONT-WEIGHT: lighter">(1)</SPAN> </TD>
If you emit markup similar to this in your DVWP, you can take advantage of the JavaScript functions that SharePoint makes available in core.js. (If you dig into the DOM for different examples of the grouping behavior, you’ll see some variations in all of this. SharePoint seems to make some determinations about how to piece it all together which are different based on some rules I haven’t quite figured out.)
In a DVWP I built the other day, I emitted markup like this in my DVWP’s rowview template:
<xsl:template name="Targets.rowview"> <xsl:variable name="NewSortValue" select="ddwrt:NameChanged(string(@Target), 0)"/> <xsl:if test="string-length($NewSortValue) > 0"> <tr id="group0{generate-id()}"> <td class="ms-gb" colspan="99"> <a onclick="javascript:ExpGroupBy(this);return false;" href="javascript:"> <img border="0" name="expand" alt="expand" src="/_layouts/images/plus.gif"/> </a> Target: <xsl:value-of select="@Target" disable-output-escaping="yes"/> </td> </tr> </xsl:if> <tr class="" style="display:none;"> <td class="ms-vb"> <xsl:value-of select="@Title"/> </td> </tr> </xsl:template>
What this does is:
- In line 2, I use the ddwrt:NameChanged function to determine if the value of the @Target column has changed. ddwrt:NameChanged returns the new value if there has been a change and nothing if there hasn’t been.
- In line 3, I test whether there has been a change, and it there has been, I emit a group row.
- Whether there has been a change or not, I emit each item starting in line 13, in this case showing only the value of the @Title column (just to keep the example simple).
The really cool part of this is that I can use the ExpGroupBy JavaScript function just like SharePoint does. I did look into exactly how it works, but I’ll keep it simple here. Basically, it does the expand/collapse behavior you want on all of the rows (items) under the row which has the ExpGroupBy function in the link. It even swaps the plus and minus images appropriately for you. No fuss, little muss.
Marc, Is there a way to not show the plus (+) sign if the joined subview is empty? How do you reference the joined subview from the parent dataview?
Mark,
After doing some research on a question i posted on another one of your posts i found this post, just wondering if you’d know a way to call a another function after this “ExpCollGroup” core function is called when expanding and collapsing groups, since the rows don’t exist in a group and only become preset in the DOM when the is loaded after the onclick: “javascript:ExpCollGroup()”. i need to wait until they are loaded in order for me to listen for a checkbox:checked or tr click function. anything to point me in the right direction will be greatly appreciated.
@Cesar:
You can define your own ExpCollGroup() function that calls the existing one end then does your own logic. It would be something like this:
M.
Mark:
i tried the example provided but i keep getting the following error “Uncaught TypeError: origExpCollGroup is not a function” i placed my logic under the origExpCollGroup(); line but no luck, any thoughts or ideas as what i might be doing wrong ?