Jump to content
Xtreme .Net Talk

Recommended Posts

Posted

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 I'm not sure how it would be done. I can get one combo to poulate ok - it's 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

  • *Experts*
Posted

Where do you plan on doing the filtering, client side or server side? If client side, I assume you're 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 combo's changing and call a function. You can use selectNodes with something like

"/City[state= + " selectedValue + &quot]"

(the exact syntax depends on your XML)

You can then loop through XML nodes and create OPTION objects and add them to the City's combo.

 

Or, if you don't want to loop but use binding, you'll 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. It's 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 can't help with server-side code as I don't much about ASP.NET and where you'd put the transformation code (in Page_Load or something? I have no idea :))

 

-ner

"I want to stand as close to the edge as I can without going over. Out on the edge you see all the kinds of things you can't see from the center." - Kurt Vonnegut
Posted

Thanks for your help. I'm new to XSL so I'm kind of flailing my way through it. I'm 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:

 

<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. I'm not sure of the best way to go about this - any direction is appreciated. Thanks again!

 

Everett

Posted

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:

 

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

 

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

 

Everett

  • *Experts*
Posted

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. You're likely to wait longer for the browser to refresh than to do the actual filtering.

 

-Nerseus

"I want to stand as close to the edge as I can without going over. Out on the edge you see all the kinds of things you can't see from the center." - Kurt Vonnegut
Posted

I've 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 I'm attempting to make work (I'm currently using separate files for cities and states):

 


<!-- 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

 

<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...

 

<?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:

 

<?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:

 

<!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> <br>
	First choose a state, then choose a city.
	<br> <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

  • *Experts*
Posted

I've attached a version the populates the city combo "by hand". I think the code's a little easier to read, plus I can't remember off-hand how to pass params to XSL using javascript :) (you'll 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 don't 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, it's 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>

 

You'll 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 aren't 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, you'll have to define a param in the XSL and fill it from the javascript. Or, you can cheat - which is how I've 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

city.zip

"I want to stand as close to the edge as I can without going over. Out on the edge you see all the kinds of things you can't see from the center." - Kurt Vonnegut
  • *Experts*
Posted

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

"I want to stand as close to the edge as I can without going over. Out on the edge you see all the kinds of things you can't see from the center." - Kurt Vonnegut
Posted (edited)

That worked like a charm!!

 

I just noticed the <CITY> tags didn't 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? I'm pretty new to it (coming from a C++ / VB background) and the documentation I cam across yesterday online didn't really get the point across, IMHO.

 

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

 

Everett

Edited by Ev Conrad

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...