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!

Chained Selectors Class

Status
Not open for further replies.

FRrogoy

Programmer
Jul 3, 2002
34
0
0
US
Here is an enhanced implementation of the Zend chained selectors class. Selection from the first list populates the 2nd list. Selection from the 2nd list returns the value. The example also has a button which can be used if you don't want the immediate return from the 2nd selection. Some of the variable names may look funny because this was test code for an actual implementation. You will have to add your dbconnect code and your list source(s) (db tables).

Class module named chainedSelectors.php:
<?php
/*
** Class: chainedSelectors
** Description: This class allows you to create two selectors. Selections
** made in the first selector cause the second selector to be updated.
** PHP is used to dynamically create the necessary JavaScript.
*/

//These constants make the code a bit more readable. They should be
//used in in the creation of the input data arrays, too.
define("CS_FORM", 0);
define("CS_FIRST_SELECTOR", 1);
define("CS_SECOND_SELECTOR", 2);
define("CS_CLASS", 3); // To pass in CSS formatting
define("CS_ONCHANGE", 4); // To pass in name of other VB function for 2nd Selector

define("CS_SOURCE_ID", 0);
define("CS_SOURCE_LABEL", 1);
define("CS_TARGET_ID", 2);
define("CS_TARGET_LABEL", 3);

class chainedSelectors
{
/*
** Properties
*/

//Array of names for the form and the two selectors.
//Should take the form of array("myForm", "Selector1", "Selector2")
var $names;

//Array of data used to fill the two selectors
var $data;

//Unique set of choices for the first selector, generated on init
var $uniqueChoices;

//Calculated counts
var $maxTargetChoices;
var $longestTargetChoice;


/*
** Methods
*/

//constructor
function chainedSelectors($names, $data)
{
/*
**copy parameters into properties
*/
$this->names = $names;
$this->data = $data;

/*
** traverse data, create uniqueChoices, get limits
*/
foreach($data as $row)
{
//create list of unique choices for first selector
$this->uniqueChoices[($row[CS_SOURCE_ID])] = $row[CS_SOURCE_LABEL];

//find the maximum choices for target selector
$maxPerChoice[($row[CS_SOURCE_ID])]++;

//find longest value for target selector
if(strlen($row[CS_TARGET_LABEL]) > $this->longestTargetChoice)
{
$this->longestTargetChoice=strlen($row[CS_TARGET_LABEL]);
}
}

$this->maxTargetChoices = max($maxPerChoice);
}

//prints the JavaScript function to update second selector
function printUpdateFunction()
{
/*
** Create some variables to make the code
** more readable.
*/
$sourceSelector = "document." . $this->names[CS_FORM] . "." .
$this->names[CS_FIRST_SELECTOR];
$targetSelector = "document." . $this->names[CS_FORM] . "." .
$this->names[CS_SECOND_SELECTOR];

/*
** Start the function
*/
print("function update" .$this->names[CS_SECOND_SELECTOR] . "()\n");

print("{\n");

/*
** Add code to clear out next selector
*/
print("\t//clear " . $this->names[CS_SECOND_SELECTOR] . "\n");
print("\tfor(index=0; index < $this->maxTargetChoices; index++)\n");
print("\t{\n");
print("\t\t" . $targetSelector . ".options[index].text = '';\n");
print("\t\t" . $targetSelector . ".options[index].value = '';\n");
print("\t}\n\n");
print("\t" . $targetSelector . ".options[0].selected = true;\n\n");

/*
** Add code to find which was selected
*/
print("whichSelected = " . $sourceSelector . ".selectedIndex;\n");

/*
** Add giant "if" tree that puts values into target selector
** based on which selection was made in source selector
*/

//loop over each value of this selector
foreach($this->uniqueChoices as $sourceValue=>$sourceLabel)
{
print("\tif(" . $sourceSelector .
".options[whichSelected].value == " .
"'$sourceValue')\n");
print("\t{\n");

$count=0;
foreach($this->data as $row)
{
if($row[0] == $sourceValue)
{
$optionValue = $row[CS_TARGET_ID];
$optionLabel = $row[CS_TARGET_LABEL];

print("\t\t" . $targetSelector .
".options[$count].value = '$optionValue';\n");
print("\t\t" . $targetSelector .
".options[$count].text = '$optionLabel';\n\n");

$count++;
}
}

print("\t}\n\n");
}

print("\treturn true;\n");
print("}\n\n");

}

//print the two selectors
function printSelectors()
{
/*
**create prefilled first selector
*/
$selected=TRUE;
print("<select name=\"" . $this->names[CS_FIRST_SELECTOR] . "\" ".
"onChange=\"update".$this->names[CS_SECOND_SELECTOR]."();\"");
print((!isset($this->names[CS_CLASS]))?'>\n':" class=\"". $this->names[CS_CLASS] . "\">\n");
foreach($this->uniqueChoices as $key=>$value)
{
print("\t<option value=\"$key\"");
if($selected)
{
print(" selected=\"selected\"");
$selected=FALSE;
}
print(">$value</option>\n");
}
print("</select>\n");

/*
**create empty target selector
*/
$dummyData = str_repeat("X", $this->longestTargetChoice);

print("<select name=\"".$this->names[CS_SECOND_SELECTOR]."\" ");
print((!isset($this->names[CS_CLASS]))?'':"class=\"". $this->names[CS_CLASS] . "\" ");
print((!isset($this->names[CS_ONCHANGE]))?'>\n':"onChange=\"". $this->names[CS_ONCHANGE] .";\">\n");
for($i=0; $i < $this->maxTargetChoices; $i++)
{
print("\t<option value=\"\">$dummyData</option>\n");
}
print("</select>\n");

}

//prints a call to the update function
function initialize()
{
print("update" .$this->names[CS_SECOND_SELECTOR] . "();\n");
}
}
?>

Code example chainedSelectorsExample.php:
<?php
// This module is an example of how to use the chainedSelectors class
// Use for category/subcategory selection
//
// The class functions create the javascript to dynamically update a 2nd selector
// box on a from based on selection from the 1st selector box.
//
// The submit button routes to chainedSelectorsTest.php, which displays what was
// passed to it via _POST.
//
// For a working example see times2/index.php. The working example differs from this
// module in that it doesn't use a submit button, and it has a dummy subselection for
// each category. It uses a javascript onChange function
// to act upon the selections.
//
// To use this example outside the TSX environment, you will have to add your
// dbConnect logic and your own query to retrieve data for the select boxes. FRR

require_once('header.php');
// Get the chainedSelectors class
require("chainedSelectors.php");
$user_id = 2428; // Testing by entering the URL directly, so don't have this available.
// The example was created and used to test the first application of the
// class in the TSX environment.

// This js function loads the selected subcategory into a form. As this is just an example,
// it just looks for the next empty spot. When they're all filled it does nothing.
// It also does nothing when no selection was made.
echo '
<script><!--
function loadTask(el){
if (el.taskname.value == "") { return false; }
var fldObj = document.getElementsByName("inpute");
var fld2Obj = document.getElementsByName("inputd");
for (i=0;i<fldObj.length;i++){
if (fldObj.value == "") {
fldObj.value = el.taskname.value;
fld2Obj.focus();
return true;
}
}
return true;
}
//--></script>
';
// Create input data
//Fetch all tasks for current user
// In this example all the data is being retrieved via one join. So there are
// multiple records (task_names) having the same category (program).
//
// The data array could also be built using an outer loop to retrieve categories
// and an inner loop to retrieve subcategories. All 4 data array fields would
// be set in the inner loop. FRR

$sql = "
SELECT DISTINCT a.tid,b.task_name,b.program,
(CASE b.program
WHEN '' THEN 9
WHEN NULL THEN 9
ELSE 0
END
) AS ord
FROM times_task_groups a, times_tasks b
WHERE a.tid=b.tid AND b.status='A' AND b.abbr='TT' AND a.gid IN
(SELECT group_id FROM user_group WHERE user_id=$user_id)
ORDER BY ord,b.program,b.task_name";
$db->query($sql);
$last_prog = '';
while ($db->next_record()) {

$ord = (int)$db->f('ord'); // This was used to creat a grouping for subcategories
// that had no assigned category
$curr_prog = ($ord > 0) ? '*** UN-GROUPED ***' : trim($db->f('program'));

if($curr_prog != $last_prog){
$selectorData[] = array( // Build blank entry. This logic is not needed if you
CS_SOURCE_ID=>$curr_prog, // are using a submit button. If onChange is being used
CS_SOURCE_LABEL=>$curr_prog, // this is needed to force user to "change" the entry.
CS_TARGET_ID=>"", // (The loadTask function will ignore "")
CS_TARGET_LABEL=>"Select a task");
$last_prog = $curr_prog;
}
$val = $db->f('tid').':TT:'.ereg_replace("[\"']", '`', stripslashes($db->f('task_name')));
$selectorData[] = array( // Build the selector array
CS_SOURCE_ID=>$curr_prog, // 1st Selector index (array key)
CS_SOURCE_LABEL=>$curr_prog, // What gets displayed in 1st selector
CS_TARGET_ID=>$val, // What gets returned from 2nd selector
CS_TARGET_LABEL=>$db->f('task_name')); // What gets displayed in 2nd selector

};//End task while() loop
//Debugging:
// print_r($selectorData);
// exit;

//prepare names
$selectorNames = array(
CS_FORM=>"pickTaskName", // Change to name of form where select box will appear
CS_FIRST_SELECTOR=>"program", // Gets returned via submit
CS_SECOND_SELECTOR=>"taskname", // Gets returned via submit
CS_CLASS=>"small", // CSS stuff - don't set if not needed
CS_ONCHANGE=>"loadTask(this.form)"); // Pass function for selector2 - don't set if not needed

//instantiate class
$tasks = new chainedSelectors($selectorNames,$selectorData);
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "<html>
<head>
<title>Dynamic Selectors Example</title>
<script type="text/javascript" language="JavaScript">
</script>
</head>
<body>
<script type="text/javascript" language="JavaScript">
<?php
$tasks->printUpdateFunction(); // This function prints the update javascript.
?>
</script>

<form name="pickTaskName" method="POST" action="chainedSelectorsTest.php">
<?php
$tasks->printSelectors(); // This goes where you want the selectors to appear
// Give 'em space! The function adjusts the width of boxes to accomodate
// the longest text.
?>
<input type="submit" name="submit" value="Submit">

<br /><br />
<table border="1">
<tr>
<th align="center">Selection Entries</th><th align="center">Data Col</th>
</tr>
<?php
//Print some empty rows in the form. Selection will put data in the form and focus on the first entry column.
for ($i=0; $i<5; $i++){
echo '
<tr><td valign="middle" style="padding: 3px" style="display: none;"><input size="30" maxlength="30"
style="text-align: left" name="inpute"></td>
<td><input size="8" maxlength="8" style="text-align: right" name="inputd"></td>
</tr>';
}
?>
<tr><td colspan="2" align="center"><input type="reset" name="reset" value="Reset"></td>
</tr>
</form>
</table>
<script type="text/javascript" language="JavaScript">
<?php
$tasks->initialize(); // Should be at the end of your page. Selects 1st category and populates subcategory.
?>
</script>
</body>
</html>
Output page for example named chainedSelectorsTest.php
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "<html>
<head>
<title>Test Chained Selectors</title>
</head>
<body>

<h2>This is a test page to check the functionality of the chainedSelectors class.</h2>
<?php
echo "<br /><h3>The incoming data:</h3> <br />";
print_r($_POST);
?>
</body>
</html>

Frank
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top