Dynamically loading combo boxes with XML

Ev Conrad

Member
Joined
Jan 13, 2003
Messages
17
I would like to dynamically load linked combo boxes where the data for the combo boxes is stored in XML. For instance, if I select a value from the State dropdown, a second dropdown gets a specific list of cities in that state. I can do it with server side script, javascript arrays, etc - but I think the solution I need is an XSL-based solution and Im not sure how it would be done. I can get one combo to poulate ok - its the dynamic loading of the second combo that gives me a proble. Any information on doing this without static client-scripted arrays or server side code is greatly appreciated.

Everett
 
Where do you plan on doing the filtering, client side or server side? If client side, I assume youre using IE with DataIslands, or something else?

If you plan on doing client side filtering and you have Data islands (<xml id=xml1>...</xml>) then you can use javascript. Trap the first combos changing and call a function. You can use selectNodes with something like
"/City[State= + " selectedValue + "]"
(the exact syntax depends on your XML)
You can then loop through XML nodes and create OPTION objects and add them to the Citys combo.

Or, if you dont want to loop but use binding, youll need to store the XSL in an island so that you can transform the State/City XML and shove it back into the xml island that the city is bound to. Its a little nastier and requires more work, but will probably run a little faster.

For server side you could do either option (create the <option> elements manually or transform the XML), but the transformation would be a little simpler and probably the better way to go, assuming you know XSL.

I cant help with server-side code as I dont much about ASP.NET and where youd put the transformation code (in Page_Load or something? I have no idea :))

-ner
 
Thanks for your help. Im new to XSL so Im kind of flailing my way through it. Im using client-side filtering and data islands like you described. I have separate .XML files to store the data that belongs in each combo box. I load the first combo when the page loads. The salient code looks like this:

C#:
<SPAN id="xslGroup">
<XML id="groupsource" src="../Lists/citygroup.xml"></XML>
<XML id="groupstyle" src="../Lists/citygroup.xsl"></XML>
 </SPAN>

<!-- Later on -->

<SCRIPT FOR="window" EVENT="onload">
	xslGroup.innerHTML = groupsource.transformNode(groupstyle.XMLDocument);
</SCRIPT>

One of the two client side options you described is most appealing. I want to keep the data in separate XML files for easy modification by a non-technical user. Im not sure of the best way to go about this - any direction is appreciated. Thanks again!

Everett
 
I think that I have it narrowed down to determining if a specific ID node in the child XML is equal to the same ID in the parent XML. I think I need to use:

C#:
<xsl:when test="@//Parent/Node/ID">

I know thats wrong, but I think that the concept is correct.

Everett
 
If you can show a snippet of each XML island (states and city/states), I could post some XSL that would work. Or, I could show the looping code to build the options manually. Building manually is easier to read in code, but slightly slower. Probably not an issue since either method will run in less than 1/10 second. Youre likely to wait longer for the browser to refresh than to do the actual filtering.

-Nerseus
 
Ive attached some of the code I am attempting to make work. The city and state data can be in either one or two files, whichever makes the project more workable. Here is the code that Im attempting to make work (Im currently using separate files for cities and states):

C#:
<!-- Separate files for cities and states -->
<?xml version="1.0" standalone="yes" ?> 
<Cities>
   <City>
      <CityID>1</CityID>
      <CityName>San Diego</CityName>
      <StateID>1</StateID>
   <City>
   <City>
      <CityID>2</CityID>
      <CityName>Los Angeles</CityName>
      <StateID>1</StateID>
   <City>
   <City>
      <CityID>3</CityID>
      <CityName>Las Vegas</CityName>
      <StateID>2</StateID>
   <City>
</Cities>

<States>
   <State>
      <StateID>1</StateID>
      <StateName>California</StateName>
   </State>
   <State>
      <StateID>2</StateID>
      <StateName>Nevada</StateName>
   </State>
</States>

OR

C#:
<States>
   <State>
      <StateID>1</StateID>
      <StateName>California</StateName>
      <Cities>
         <City>
            <CityID>1</CityID>
            <CityName>San Diego</CityName>
         <City>
         <City>
            <CityID>2</CityID>
            <CityName>Los Angeles</CityName>
         <City>
      </Cities>
   </State>
   <State>
      <StateID>2</StateID>
      <StateName>Nevada</StateName>
      <Cities>
         <City>
            <CityID>3</CityID>
            <CityName>Las Vegas</CityName>
         <City>
      </Cities>
   </State>
</States>

Here is my city .xsl file...

C#:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/">

<select size="1" id="Cities" name="Cities" Class="InputCity">
<xsl:for-each select="//Cities/City">

   <xsl:choose>
      <xsl:when test="@selected">
         <option selected="true"> <xsl:value-of select="Description"/> </option>
     </xsl:when>
      <xsl:otherwise>
         <option> <xsl:value-of select="Description"/> </option>
      </xsl:otherwise>
   </xsl:choose>

</xsl:for-each>

</select>

</xsl:template>
</xsl:stylesheet>

...and my state .xsl file:

C#:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/">

<select size="1" id="States" name="States" Class="InputState" onchange="return States_onchange()">
<xsl:for-each select="//States/State">

   <xsl:choose>
      <xsl:when test="@selected">
         <option selected="true"> <xsl:value-of select="Description"/> </option>
     </xsl:when>
      <xsl:otherwise>
         <option> <xsl:value-of select="Description"/> </option>
      </xsl:otherwise>
   </xsl:choose>

</xsl:for-each>

</select>


</xsl:template>
</xsl:stylesheet>

Here is my test HTML page:

C#:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
	<HEAD>
		<META NAME="GENERATOR" Content="Microsoft Visual Studio 7.0">
		<TITLE>Interdependent Dropdown Lists</TITLE>
	</HEAD>
	<BODY>
	<h4>Interdependent Dropdown Lists</h4>
	<hr>
	Here are combo boxes populated straight from XML files:

		<br>&nbsp;<br>
		First choose a state, then choose a city.
		<br>&nbsp;<br>
		<table>
		<tr>
		<td height="30" class="InputState">State:</td><td>&nbsp</td>
		<td><SPAN id="xslState">
       	 		<XML id="statesource" src="State.xml"></XML>
       	 		<XML id="statestyle" src="State.xsl"></XML>
    		    </SPAN>
		</td>
		</tr>
		<tr>
		<td height="30" class="InputCity">City:</td><td>&nbsp</td>
		<td><SPAN id="xslCity">
       	 		<XML id="citysource" src="city.xml"></XML>
       	 		<XML id="citystyle" src="city.xsl"></XML>
    		    </SPAN>
		    <SPAN id="xslPlaceholder">
    		    </SPAN>
		</td>
		</tr>
		</table>
	</BODY>
  	<SCRIPT FOR="window" EVENT="onload">
		xslState.innerHTML = statesource.transformNode(statestyle.XMLDocument);
		xslPlaceholder.innerHTML = "<select><option>Select a State</select>";
  	</SCRIPT>
  	<SCRIPT FOR="States" EVENT="onchange">
		xslPlaceholder.innerHTML = "";
		xslCity.innerHTML = citysource.transformNode(citystyle.XMLDocument);
  	</SCRIPT>
</HTML>

The placeholder span is so that the select box shows up when the page is loaded. I had problems with it otherwise.

This is a lot of stuff - thanks again for taking a look and showing me my mistake - I would think that you will probably only have to change the city.xsl file.

Everett
 
Ive attached a version the populates the city combo "by hand". I think the codes a little easier to read, plus I cant remember off-hand how to pass params to XSL using javascript :) (youll need a param to filter the cities per state).

Sorry about the reformatting of the HTML file. I prefer the script at the top and I dont like using SCRIPT FOR... - just my preference and it was easier for me as I was debugging: your City XML was invalid, no closing tags on the <City> nodes.

When filling the State combo, its interesting to note that you can create your own attributes and pull out values as if they were part of the DOM.
For example, change:
<xsl:attribute name="value"><xsl:value-of select="StateID"/></xsl:attribute>
to
<xsl:attribute name="StateID"><xsl:value-of select="StateID"/></xsl:attribute>

Youll create an option tag that looks like:
<option StateID="1">Nevada</option>

Now you can reference the StateID with code like:
var stateID = States.options[States.selectedIndex].StateID;
Notice that the last piece of this line, StateID, is an attribute of the option element. Pretty cool, as you arent limited to storing one value per option tag. Only works in IE of course, but then again so do the XML islands :)

If you decide to go with using a transform to fill the City list, youll have to define a param in the XSL and fill it from the javascript. Or, you can cheat - which is how Ive done it in the past - and use the XSL island as XML (which it is) and perform a selectSingleNode to find your template match and change it dynamically based on the current stateID.

Have fun!

-Nerseus
 

Attachments

Forgot to mention I updated your state.xsl as well. It was using the Description for the state name. I also converted the Option element to be built using XSL so that I could set the Value attribute (or the StateID as mentioned above) to values from the XML.

-Nerseus
 
That worked like a charm!!

I just noticed the <CITY> tags didnt have the closing brackets. I did that pretty late last night, and was working on recreating something to post.

I noticed a problem when attempting to wrap a <form> around where the combo boxes would be. Let me know if you know of a caveat here; otherwise I fixed it with a little script to add the form elements upon page loading.

Can you recommend a good resource for getting up to speed with XSL? Im pretty new to it (coming from a C++ / VB background) and the documentation I cam across yesterday online didnt really get the point across, IMHO.

In any event, thanks for your help. Im pretty new to this forum, but there are some pretty knowledgeable developers here.

Everett
 
Last edited by a moderator:
Back
Top