Cascading Dropdown Columns in a SharePoint Form – Part 2

UPDATE 2009-08-26: We’ve translated this logic into our jQuery Library for SharePoint Web Services. I *strongly* suggest that you look at that as an option, as it is far more robust.  And free!

In my post last week entitled Cascading Dropdown Columns in a SharePoint Form – Part 1, I showed how to create cascading dropdowns, meaning that a choice in the first dropdown would change the options in the second dropdown.  The example I gave was from a client project which had over 20 options in the second dropdown.  Because of this, the required JavaScript was a bit complicated because of the way that SharePoint renders the control for more than 20 options.

I got some great questions about that post and I promised to post again with a simpler example with a working demo. I’ve created a new Demo site with a CascadingDropdowns demo page on our Sympraxis Consulting Web site (all WSS!).

Notes to the critics:  Sure, you could read the lists directly from the JavaScript.  But this method seems like it will be easier to follow for the average “middle tier” developer (or non-developer).  It’s also possible to simply embed the table of value relationships directly in the page (hard-wired), though I wouldn’t recommend it.  (Use lists for what they are good for!)

Hopefully the page will be self-explanatory, but since this is a new way to demo my code, I appreciate any feedback you might have about how to make it more useful.

Looking at the page at the above link should give you all of the details, but the JavaScript looks like this:

_spBodyOnLoadFunctionNames.push("SetUpCascading");

var Column1 = new Object();
var Column2 = new Object();
var savedColumn2Options = new Object();

function SetUpCascading() {
    // Find Column1 in the DOM (in this demo, the column is named "Region")
    Column1 = getTagFromIdentifierAndTitle("select","","Region");
    // Find Column2 in the DOM (in this demo, the column is named "State")
    Column2 = getTagFromIdentifierAndTitle("select","","State");
    // Attach the onchange event to Column1
    Column1.attachEvent('onchange', Column1Changed);
    // Call the onchange event to set the initial options for Column2
    Column1Changed();
}

function Column1Changed() {
    // Find the table with the Column1 / Column2 / ID information
    var Column1Table = document.getElementById("Column1Table");
    // Find all of the table rows
    var Column1TableRows = Column1Table.getElementsByTagName("TR");
    // See which Column2 options are allowed for the chosen Column1 option
    Column2Count = 0;
    // For each of the table rows...
    for (var i=0; i < Column1TableRows.length; i++) {
        // Get the table detail cells
        var Column1TableRowsDetails = Column1TableRows[i].getElementsByTagName("TD");
        // If the Region value in the table row matches the currently chosen Region
        if(Column1TableRowsDetails[1].innerHTML == Column1.options[Column1.selectedIndex].text) {
            // Add the option to the Column2 dropdown
            Column2.options[Column2Count] = new Option(Column1TableRowsDetails[0].innerHTML,
                Column1TableRowsDetails[2].innerHTML);
            // Increase the count of available options for Column2
            Column2Count++;
            // For this demo, set the background color of the Column2 cell to green
            Column1TableRowsDetails[0].style.backgroundColor = "green";
        // If the Region value in the table row doesn't match the currently chosen Region
        } else {
            // For this demo, set the background color of the Column2 cell to red
            Column1TableRowsDetails[0].style.backgroundColor = "red";
        }
    }
    // Set the length of the options array
    Column2.options.length = Column2Count;
    // If there aren't any available choices, then disable the Column2 dropdown
    if(Column2Count == 0) Column2.disabled = true;
     else Column2.disabled = false;
}

and the key templates in the DVWP look like this.  Note that the table must have a unique id, which is used by the JavaScript to find it in the page: table id="Column1Table".

<xsl:template name="dvt_1">
    <xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row"/>
   
<table id="Column1Table" border="0" width="100%" cellpadding="2" cellspacing="0">
        <xsl:for-each select="$Rows">
            <xsl:sort select="@Title" order="ascending"/>
            <xsl:call-template name="dvt_1.rowview"/>
        </xsl:for-each>
   </table>
</xsl:template>

<xsl:template name="dvt_1.rowview">
   
<tr>
       
<td class="ms-vb">
            <xsl:value-of select="@Title"/>
       </td>
       
<td class="ms-vb">
            <xsl:value-of select="@Region"/>
       </td>
       
<td class="ms-vb">
            <xsl:value-of select="@ID"/>
       </td>
   </tr>
</xsl:template>

Similar Posts

97 Comments

    1. Yes, Shawn, you’re right. I’ve got a broken page that I need to fix there. If you look at the SPServices documentation, you can see static examples of how it works.

      M.

  1. Marc
    Credit was given to you for the SP cascading drop down code above with a “fix” to remove repeating same value results (see 3 level Cascading Dropdowns in SharePoint – Remove Duplicate Records: http://tipsinsharepoint.wordpress.com/2010/06/24/3-level-cascading-dropdowns-in-sharepoint-remove-duplicate-records/).

    I use the SPServices jQuery Library and have this “duplicate records” problem. My data is as follows Projects > Phases > Milestones with Phases and Milestones having many of the “same name” values. Phases are filtered by the Project lookup so that’s no problem but it seems with many Phases sharing the same name values I get the MIlestone same name values multiple (#Phases) times. I could use a custom naming convention (e.g. 1.1.1, 1.1.2, 1.2.1, 1.2.2 etc.) but because I’m not entering the data and such a schema would likely prove fault laden for other users could you provide guidance for me and how I might address this use of same name values and the need to accurately filter at each progressing level.

    Thank you.

    1. John:

      In v0.7.1, I added a new option called matchOnId. By setting this option to true, the function will pull up the exact values by matching on ID instead of the text values. This ought to get you past your issue.

      M.

      1. Marc
        Yep the matchOnId: true did the trick for my multiple repeat name values and the resulting trouble with 3 cascading drop-downs. You are an awesome benefit to the SharePoint community!

  2. Hi Marc,

    I know this is an old blog posting, but I was wondering if you could point me in the direction I’m missing. I followed along with the older post and could understand where intellectually where to put an onchange function. With this approach, you don’t mention anything about an onchange event. I’ve put the code in my page, but it doesn’t appear to be called on a change, but when I look at the code in SP Designer, I don’t see anywhere to attach an event function.

    I didn’t think I was this much of a newbie, but this has been a humbling experience.

    Thanks,
    Bruce

    1. Bruce:

      Have toy tried the SPCascadeDropdowns function in SPServices? (See the comment at the top of this post.) I would discourage you from trying the code in the post and to use SPServices instead.

      M.

  3. Thanks for all posts, i’m trying to use top level List as relationship, Can any one help me with sytax. It didn’t work with relationshipWebURL: property also
    Quick help is highly appriciate!!!

    1. ram:

      Using a list in the root site works just fine; I do it all the time. You must not have the relationshipWebURL value right.

      M.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.