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>

97 Comments

  1. Thank You for this new version, but I’m still having problems.
    I’m not sure what I’m doing wrong either.

    I must be missing something, but can’t find it.

    Here is what I’ve done so far.

    1. Main_Categories list. 1 column named ‘Category’
    2. Category_Pairs list. 2 columns first named Title (Sub Categories) and Category (lookup to Category in Main_Categories)
    3. Created a document library, added 2 new columns. Category (lookup to Category column in Main_Categories). Sub_Category (Lookup to ‘Title’ in Category Pairs)
    4. In Edit.aspx inserted a DVWP from the Category_Pairs list. Included ‘Category’, ‘Title’, and ‘ID’. I named the table ‘Column1Table’
    5. in the JS I changed ‘Region’ to ‘Category’ and changed ‘State’ to ‘Sub_Category’.
    6. I linked to the JS into my page.

    it should work now right?
    The sub category dropdown is not filtering for me, and the DVWP doesn’t change colors. I’m not sure what I’m doing wrong.

    Reply
  2. I’m looking at the source of the demo, and I noticed that your dropdown are named Region, State. Like this title=State

    Now with the default edit.aspx, how do we name our dropdowns fields? I don’t see a way to name them within SPD.

    Reply
    • Chris:

      It sounds like you’re doing everything right, but perhaps you’ve missed a step somewhere along the way. In your form, the selects will automagically have the Title=”thecolumnname”. You don’t need to do anything to name them otherwise. Are you not seeing those titles in the page code? Have you started with the default EditForm.aspx?

      M.

      Reply
  3. Ya I was just going to report back that if I view the page in the browser I see the select titles, they don’t show in SPD. so I thought they weren’t there.

    So now I’m more stumped (I was hoping the select titles was it), and yes I’m using the default editform.aspx.
    I even started all over hoping that maybe I missed something, deleted all my old lists and form library.

    But still not working.

    I just noticed now tho, once I add the JS to the page. I get an error that says
    Line 10
    Char3
    Error: Object Expected
    Code: 0

    Is there anything else in the js, that needs to be changed besides those references?

    Reply
  4. :(
    Darn I was hoping that was going to be it.

    I added the Utilities.js to my site, then in my edit.aspx I linked to it.

    but now I get this error
    1.innerHTML’ is null or not an object

    I’m not sure what the means? Is that something in the js? I don’t see that it there.

    Reply
  5. I see it in the js now
    if(Column1TableRowsDetails[1].innerHTML == Column1.options[Column1.selectedIndex].text) {

    but why would it be null or not set? The only thing I changed in the js were the names of the selects. Everything else is the same..

    Reply
    • Chris:

      If Column1TableRowsDetails[1].innerHTML is throwing a null error, then I’m guessing that the DVWP table doesn’t have an id which matches the id in the JavaScript. Check that next.

      If you’re going to be doing much JavaScript debugging, consider upgrading to IE8 and using the built-in debugger there.

      M.

      Reply
  6. Hi Marc,

    I am not a hard code developer. I have dowloaded your utilities.js file and upload it to the document library and i have added cascading.js (script) to the content editor webpart(source code editor) but I am not able to make the Javascript work.. do you think I should download and install some dll’s to make it work? I have recreated the same states list, regions list, look ups and everything else.. I have even added the DVWP to the page… can just help me iwth dll’s? or something that I am doing wrong.

    Thanks a ton
    V

    Reply
    • V:

      There’s nothing else required other than what I’ve got posted: no DLLs, nothing at all on the server. That’s one of the benefits of this approach.

      M.

      Reply
  7. That is great help Marc… I am almost there.. hope to learn new tricks from you using the SPD. :)

    But I still face the same issue as Chris mentioned “1.innerHTML is null or not an object” also I am working on my client machine which does have IE 6 and not have access to download IE 8.

    I also checked the name I gave to my DVWP table as and the same id is used instead of “Column1Table”

    let me know your thoughts..

    Thanks again.
    Akshay

    Reply
  8. Marc,

    I found out the way !!! thanks for directions
    I removed the additional XSL tags/properties and gave a similar plain vanilla template and it worked.

    @Chris – Try editing the XSL and modify the templates and make it as simple as possible as shown in the post. Hope this works..

    Cheers,
    Akshay

    Reply
  9. And Marc,
    thank you for writing this tutorial!
    Cascading dropdowns was something I really needed and the lack of anything built into Sharepoint was a big disappointment.

    This is a great help

    Reply

Have a thought or opinion?