Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

filter a combo box from a value in another

Status
Not open for further replies.

gwilym40

Programmer
May 2, 2002
31
GB
Hi

I have a query in access with 3 columns ID, service and area
The first combo box in the asp takes the ID and distinct service from the query
I'd like the 2nd combo box to filter the area values based on the service chosen from the first.

thanks
tony
 
When it comes to chaining select boxes there are generally two methods,
1) Set up the onChange event in the first select to submit the form back to the same page, then in the page do a check like this:
If Request.Form(&quot;selFirstSelect&quot;) <> &quot;&quot; Then
'select statement based on value in first
Else
'select statement based on value of second
end if
Then you can execute it into a Recordset or open a recordset with your sql statement and populate your boxes exactly like before (making sure that if the first one was set you make the value from the previous page selected, etc):
Code:
'pretend we have a recordset named rs
Response.Write &quot;<select name=&quot;&quot;selFirstSelect&quot;&quot; onChange=&quot;&quot;myFormName.action='nameOfThisPage.asp';myFormName.submit();&quot;&quot;>&quot;
rs.moveFirst
Do Until rs.EOF
   Response.Write &quot;<option value='&quot; & rs(&quot;whatever&quot;) & &quot;'&quot;
   If Request.Form(&quot;selFirstSelect&quot;) = rs(&quot;whatever&quot;) Then Response.Write &quot; selected&quot;
   Response.Write &quot;> &quot; & rs(&quot;whateverElse&quot;) & &quot;</option>&quot;
   rs.MoveNext
Loop
Response.Write &quot;</select>&quot;
The first time the page loads it will come up with the first option in the select box (because there is nothing in the Request.Form named selFirstSelect). When they choose an option it will change the action of the form to the page &quot;nameOfThisPage.asp&quot; and submit it back to itself. On the second load it will go into the if statement, filter the recordset used for the second select statement, generate the first select from the first recordset (as it did before) selecting the option that matches the one from the previous page.
With three options that filter themselves dependant on the value of the previous one you would want to set up the queries like this:
Code:
If Request.Form(&quot;selSecondSelect&quot;) <> &quot;&quot; Then
   sql_query3 = &quot;Select...Where service = '&quot; & Request.Form(&quot;selSecondSelect&quot;) & &quot;'&quot; 'assuming service was a string
   sql_query2 = &quot;Select...Where id = &quot; & Request.Form(&quot;selFirstSelect&quot;) 'assuming id was a number
ElseIf Request.Form(&quot;selSelectFirst&quot;) <> &quot;&quot; Then
   sql_query3 = &quot;Select...&quot; 'select based on id
   sql_query2 = &quot;Select...Where id = &quot; & Request.Form(&quot;selFirstSelect&quot;) 'assuming id was a number
Else
   sql_query3 = &quot;Select *...&quot;
   sql_query2 = &quot;select *...&quot;
End If

sql_query1 = &quot;select *...&quot; 'always select everything for the first one

Ok, so that should be enough to get started if you choose the resubmission option.

The other method...wait, let me put up a big 'ol two:
2) Ok, the other method is to use javascript arrays to hold the values. Then one a value is selected from the first select it calls a function that empties the remaining select boxes then repopulates them from arrays based on the first select box. The difficulties with this method are, of course, browser compatibility and all the javascript necessary. If you want to see an example of a lot of javascript, their is a messy copy (a co-developers first try with my later changes to reflect changes to the db) here: go to advanced search (nav in top right), the taxonomy search is three chained select boxes, if you view source you will see a lot of javascript, most of it should be arrays to maintain those drop downs. These arrays were written dynamically from the ASP script. The other fuctions of interest are populateSubject(), populateTopic, and clearCombo functions. If your still interested after seeing that, let me know, I have a much cleaner example (ok, cleaner not clean ;) ) stored locally that could lead you through the process.

Hope this helps,
-Tarwn

________________________________________________________________________________
Want to get great answers to your Tek-Tips questions? Have a look at faq333-2924
 
Thanks Tarwn

I'd be interested in the javascript to hold the arrays.
If you had an example that would be great - I'd need to build the arrays dynamically i.e. from the 2 recordsets containing service and area - I'm abit of a novice at all this so an example would be great

tony
 
this may also give you some direction
thread333-451739 _______________________________________________
[sub]{ str = &quot;sleep is good for you. sleep gives you the energy you need to function&quot;;
ptr = /sleep/gi;Nstr = str.replace(ptr,&quot;coffee&quot;);alert(Nstr); }[/sub]
_______________________________________________
for the best results to your questions: FAQ333-2924
has you're questio
 
Ok, I have three examples here at work that I can post. The first is a static example to give you a feel for how the page will be output to the browser. This example only has two select boxes so what I will do is go through the static and dynamic versions of two select boxes then try to edit the static one for three select boxes and hopefully that should be enough to guide you on how to do it dynamically.

Here is the static example:
Code:
<html>
<head>
<title> Shift Selection </title>
<script language=&quot;JavaScript&quot;>
<!--
var shifts;
	function init(){
		//to initialize them all to the same times
		shifts={dayOfWeek: [{day: &quot;monday&quot;,theShifts: [&quot;1&quot;,&quot;2&quot;,&quot;3&quot;]},{day:&quot;tuesday&quot;,theShifts: [&quot;4&quot;,&quot;5&quot;,&quot;6&quot;]}]};
	}

	//change day function
	function changeDay(elem){
		var j, day;
		var shiftText;

		if(elem.selectedIndex == 0)	//if they selected our pretty [select a day] stmt
			return false;			//do nothing and leave quietly
		
		//Clear the second drop down of all but top [select a shift]
		for (i = frmFormName.workday.options.length - 1; i >= 1; i--) frmFormName.workday.options[i] = null;
		frmFormName.workday.selectedIndex = 0;

		//grab day from select box
		day = elem.selectedIndex-1;

		for(j=0;j<shifts.dayOfWeek[day].theShifts.length;j++){
			document.frmFormName.workday.options[j] = new Option(shifts.dayOfWeek[day].theShifts[j],&quot;&quot;);
		}
	}
//-->
</script>
</head>
<body onLoad=&quot;init();&quot;>
<form method=POST action=&quot;wherever.html&quot; name=&quot;frmFormName&quot;>

<select name=&quot;weekday&quot; onChange=&quot;changeDay(this);&quot;>
	<option>[Select a day]</option>
	<option>Monday</option>
	<option>Tuesday</option>
</select>
<select name=&quot;workday&quot;>
	<option>[Select a Shift]</option>
</select>
</form>
</body>
</html>

Basically the concept here is that we have 6 shifts. Shifts 1-3 only occur on Mondays, 4-6 only occur on Tuesdays.
We set up the array to hold these values like so(pseudo code to help with transition to dynamic):
Code:
var array = {nameOfSection:[
   For each day
      output {day:&quot;name of day&quot;,theShifts:[
      for each shiftfor this day
         output &quot;shift name&quot;,
      next
   next

Now we use this concept above to make the code dynamic. Basically before we initialize the array we need to do our server-side db connection and queries, then we loop through them to build the array:
Code:
<%
Option Explicit

Dim objRS

'objRS holds a record set that contains records with shift_day, shift_number
' for example
' 1st record: Monday, 1
' 2nd record: Monday, 2
' 3rd record: Monday, 3
' 4th record: Tuesday, 1
' etc
%>
<html>
<head>
<title> Shift Selection </title>
<script language=&quot;JavaScript&quot;>
<!--
var shifts;
	function init(){
		//to initialize them all to the same times
		shifts={dayOfWeek: [
		<%
		dim tDay
		objRS.MoveFirst
		Do Until objRS.EOF
			If tDay <> objRS(&quot;shift_day&quot;) Then	'If not equal to previous, than start a new array element
				%>
				{day: &quot;<%=objRS(&quot;shift_day&quot;)%>&quot;,theShifts: [
				<%
				tDay = objRS(&quot;shift_day&quot;)			
			End If
			%>&quot;<%=objRS(&quot;shit_number&quot;)%>&quot;, <%	'add the shift number to inner array
			objRS.MoveNext
			If tDay <> objRS(&quot;shift_day&quot;) Then	'If next not equal to current, end the array element
				Response.Write &quot;]},&quot;
			End If
		Loop
		%>
	}

	//change day function
	function changeDay(elem){
		var j, day;
		var shiftText;

		if(elem.selectedIndex == 0)	//if they selected our pretty [select a day] stmt
			return false;			//do nothing and leave quietly
		
		//Clear the second drop down of all but top [select a shift]
		for (i = frmFormName.workday.options.length - 1; i >= 1; i--) frmFormName.workday.options[i] = null;
		frmFormName.workday.selectedIndex = 0;

		//grab day from select box
		day = elem.selectedIndex-1;

		for(j=0;j<shifts.dayOfWeek[day].theShifts.length-1;j++){
			document.frmFormName.workday.options[j] = new Option(shifts.dayOfWeek[day].theShifts[j],&quot;&quot;);
		}
	}
//-->
</script>
</head>
<body onLoad=&quot;init();&quot;>
<form method=POST action=&quot;wherever.html&quot; name=&quot;frmFormName&quot;>

<select name=&quot;weekday&quot; onChange=&quot;changeDay(this);&quot;>
	<option>[Select a day]</option>
	<%
	objRS.MoveFirst
	tDay = &quot;&quot;
	Do Until objRS.EOF
		If tDay <> objRS(&quot;shift_day&quot;) Then
			%>
			<option><%=objRS(&quot;shift_day&quot;)%></option>
			<%
			tDay = objRS(&quot;shift_day&quot;)
		End If
		objRS.MoveNext
	Loop
	%>
</select>
<select name=&quot;workday&quot;>
	<option>[Select a Shift]</option>
</select>
</form>
</body>
</html>

Now that shows us how it will work with 2 select boxes, but what you are looking to do has two minor differences:
1) there are 3 select boxes, and
2) you want to display all the options to begin with and then filter it down

Altering the code to do (2) will probably be more difficult than altering the code to do (1). Here is the alteration for (1):

Create your third array level similar to how the second array is nested in the first one above, probably will want iut to look like this(pseudo code again):
Code:
var array = {nameOfSection:[
   For each ID
      output {id:&quot;value of ID&quot;,{services:[
      for each service for this ID
         output {&quot;service name&quot;,{areas:[
         for each area for this service
            output &quot;area name&quot;
         next
      next
   next

Then I would just go ahead and build a second function to handle assigning values to the second select box.

The first function will need to clear both boxes, as a change to the first box (id) will need a cvhange to the second box(service) and if there were already values in the last box (area) then we need to get rid of them because service has changed.

Concerning your recordset:
You will want to pull all of the ID's, Services, and Areas out in one recordset so you can preserve the relationships as your outputting them into your javascript array. The easiest way to loop through this and build the array will be to have a tempID and tempService variable.
Then loop through like pseudocode above, here is an example with the db stuff in it, assume our recordset is named rs_all:
Code:
rs_all.MoveFirst
'escaping to javascript to write some variables, usually I would
'   response.write these but I wanted to clarify the change from
'   ASP to javascript
%>
var tempID, tempService
var myArray = {chainedData:[<% 'back to ASP to handle outputting the recordset
Do Until rs_all.EOF
   'if this id is not equal to the last one
   If rs_all(&quot;ID&quot;) <> tempID Then
      'if this isn't the first run through
      If tempID <> &quot;&quot; Then
         'end the previous id block
         Response.Write &quot;},&quot;
      End If

      'Start a new ID block
      Response.Write &quot;{id:&quot;&quot;&quot; & rs_all(&quot;ID&quot;) & &quot;&quot;&quot;,{services:[&quot;
   End If

   'Ok, now we do the same thing for service because the services are block as well instead of just comma-delimited lists in the array
   If rs_all(&quot;service&quot;) <> tempService Then
      'if this isn't the first run through
      If tempService <> &quot;&quot; Then
         'end the previous id block
         Response.Write &quot;},&quot;
      End If

      'Start a new Service block
      Response.Write &quot;{serviceName:&quot;&quot;&quot; & rs_all(&quot;service&quot;) & &quot;&quot;&quot;,{services:[&quot;
   End If

      'Now we just need to output the areas in a comma delimited fashion
      'check if this is a new service, if so we don't need a preceding comma
      If tempService = rs_all(&quot;service&quot;) Then
         Response.Write &quot;,&quot;
      End If
      'output area
      Response.Write &quot;&quot;&quot;&quot; & rs_all(&quot;area&quot;) & &quot;&quot;&quot;&quot;
 
      'set the tempID and tempService variables
      tempID = rs_all(&quot;id&quot;)
      tempService = rs_all(&quot;service&quot;)

      'incrememnt recordset
      rs_all.MoveNext
Loop


Ok, the above code may not be 100% as I am writing it on the fly, but I hope this is enough to get you started. Please feel free to ask questions if you get stuck or have trouble with the process,
-Tarwn ________________________________________________________________________________
Want to get great answers to your Tek-Tips questions? Have a look at faq333-2924
 
Dear Tarwn:
I appreciate your contribution to this site. We do learn a lot from your post.
I have difficulty in understanding the code below, could you take some time to give me some explaination.

function init(){
//to initialize them all to the same times
shifts={dayOfWeek: [{day: &quot;monday&quot;,theShifts: [&quot;1&quot;,&quot;2&quot;,&quot;3&quot;]},{day:&quot;tuesday&quot;,theShifts: [&quot;4&quot;,&quot;5&quot;,&quot;6&quot;]}]};
}

It looks like you are initiating a multidimensional array. Usually the multidimensional array are intialized in this way.
<script>

// Initialise the arrays..
var arr = new Array(1,2,3,4,5,6,7,8,9,10);
var arr1 = new Array(10,9,8,7,6,5,4,3,2,1);

// set up the array of arrays to simulate multi dim ones.

theArray = new Array(arr, arr1);

alert(theArray[1][0]);
alert(theArray[1][1]);
alert(theArray[1][2]);
alert(theArray[1][3]);
alert(theArray[1][4]);
alert(theArray[1][5]);


</script>

what is the difference? Which one is better?

Haijun
 
I'm am by no means an expert at javascript. I think that basically the two are doing the same thing, the first one is creating it all in one single assignment statment though, whereas yours splits it up by defining the interior arrays first than attaching them to the exterior array. The other addition to mine is the internal names that basically are allowing you to store a name for an internal array in the array one level higher. The only reason I did it that way is because that is what I learned in the beginning and it has worked for me this long. I am assuming you could also do it the other way, but where would you store the day of week in the shifts/days examples?
Code:
//this would work
var shifts = new Array(&quot;Monday&quot;,1,2,3)
var shifts2 = new Array(&quot;Tuesday&quot;,4,5,6)

var days = new Array(shifts, shifts2)
While that would work I find it clumsier in use because you have to constantly remember that the first element of each inner array is the day, while the others are the numbers. Think of the way I did it as this:
Code:
var theShifts = new Array(1,2,3);
var theShifts2 = new Array(1,2,3);
var day = new Array(&quot;Monday&quot;,shifts);
var day2 = new Array(&quot;Tuesday&quot;,shifts2);
var shifts = new Array(day, day2);

The name &quot;dayOfWeek&quot; in my aray is never actually used and probably is not necessary (I pulled this from an example I once used for something similar that also was tied into another control, and the extra name helped).

The last struture I just built would work, but would be a little difficult to build because you have to build the internal arrays first, then the external. This means something like:

Code:
   For Each Id
      For Each service
         for each area
            add this area to an areas array (with a counter)
         next
         add service name to services array followed by the areas array for this service
      Next
      add the correct services arrays to each id array
   Next   
   add the id arrays to the ids array
Now that doesn't look to bad as pseudo code, but now consider the fact that you will not really be using for each statements, I started writing the do loop for this but it is quite nasty and it turns out that not only do you have to do the services and ids assignment inside the loop, you need to add a set after the loop also to add the last set of areas to a service, then add the last group of services to a services array, then add the last group of services'(plural of services) to an id array, which then gets added to the last ids array.

It gets kind of hairy. One possibility to get around this would be to create the arrays in a string variable rather than printing them straight out, then you might have better control over them.


Also one comment about the code for the loop in the previous post, I left off the ending brackets for the array when I was writing the example, so as it is it wouldn't run, it will need some end brackets printed out after the loop ends.

-Tarwn ________________________________________________________________________________
Want to get great answers to your Tek-Tips questions? Have a look at faq333-2924
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top