Link to home
Start Free TrialLog in
Avatar of Jason C. Levine
Jason C. LevineFlag for United States of America

asked on

Adding select value to this Spry (javascript) function

Hi folks,

I've been staring at this for too long and it's driven me crazy.  I have a page that is filtering a Spry dataset based on checkboxes as demonstrated here .

What I would like to do is throw a select box up there instead of the checkboxes as I have too many options that can comfortably fit in checkboxes (all US States and Canadian Provinces).  

A simplified version of the HTML would be:

<select name="filterstate" onChange="ToggleFilter(this.value, ffState)">
   <option value="NY">New York</option>
   <option value="WI">Wisconsin</option>
</select>

Open in new window


and the javascript should look a little like this:

function ffState(ds, row, index){ return (row.state == ???) ? row : null };

function ToggleFilter(enable, f)
{
	if (enable)
		dsPrograms.addFilter(f, true);
	else
		dsPrograms.removeFilter(f, true);
}

Open in new window


What I can't figure out for the life of me is how to pass the selected value from filterstate into the above function where the ??? are.  Help!
SOLUTION
Avatar of spiritastir
spiritastir
Flag of Ukraine image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Jason C. Levine

ASKER

Yeah, that's technically correct but not exactly what I was looking for.

I'm hoping to avoid declaring 60+ instances of switch (f) by condensing it to a single line.
The reference to the option value is this.options[this.selectedIndex].value
The select itself does not have value attribute.

Serves you right for using spry
Cd&
The reference to the option value is this.options[this.selectedIndex].value

So my line would be:

function ffState(ds, row, index){ return (row.state == this.options[this.selectedIndex].value) ? row : null };

?

The select itself does not have value attribute.

One more time in small words for the javascript-challenged (e.g. me)?

Serves you right for using spry

Sirrah, you wound me.  I did not create the initial page, just trying to reduce my workload by not re-inventing the wheel and doing the whole darn thing over in Jquery
If you want to return the abbreviation use value for the whole name use text.


Cd&
Gotcha.  Hang on whilst I test.
Testing gives me:

--
[12:41:29.384] this.selectedIndex is undefined

for

function ffState(ds, row, index){ return (row.state == this.options[this.selectedIndex].value) ? row : null };

<select name="state" onChange="ToggleFilter(this.value, ffState)">
<option value="NY">New York</option>
<option value="WI">Wisconsin</option>
</select>

So do I need an extra step to get the variable in there?  Something like this:

function ffState(ds, row, index){ var selected_option_value = state.options[index].value; return (row.state == selected_option_value) ? row : null };

(fyi, the above yields "state is not defined" in the console)
Here one line:

	dsDonuts.addFilter(window[f], true);

Open in new window


Code should be

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:spry="http://ns.adobe.com/spry">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Multiple Filters Mode Sample</title>
<link href="http://labs.adobe.com/technologies/spry/css/samples.css" rel="stylesheet" type="text/css" />
<script language="JavaScript" type="text/javascript" src="http://labs.adobe.com/technologies/spry/includes/xpath.js"></script>
<script language="JavaScript" type="text/javascript" src="http://labs.adobe.com/technologies/spry/includes/SpryData.js"></script>
<script language="JavaScript" type="text/javascript" src="http://labs.adobe.com/technologies/spry/includes/SpryDataExtensions.js"></script>
<script type="text/javascript">
<!--
var dsDonuts = new Spry.Data.XMLDataSet("donuts.xml", "/items/item", { subPaths: "topping" });

function ffNone(ds, row, index){ return (row.topping == "None") ? row : null; };
function ffGlazed(ds, row, index){ return (row.topping == "Glazed") ? row : null; };
function ffSugar(ds, row, index){ return (row.topping == "Sugar") ? row : null; };
function ffPowdered(ds, row, index){ return (row.topping == "Powdered Sugar") ? row : null; };
function ffSprinkles(ds, row, index){ return (row.topping == "Chocolate with Sprinkles") ? row : null; };
function ffChocolate(ds, row, index){ return (row.topping == "Chocolate") ? row : null; };
function ffMaple(ds, row, index){ return (row.topping == "Maple") ? row : null; };

function ToggleFilter(enable, f)
{
	if (enable)
		dsDonuts.addFilter(f, true);
	else
		dsDonuts.removeFilter(f, true);
}

function ToggleFilterSelect(f)
{	
	RemoveAllFilters();
	dsDonuts.addFilter(window[f], true);
}


function RemoveAllFilters()
{
	document.forms[0]["noneCB"].checked = false;
	document.forms[0]["glazedCB"].checked = false;
	document.forms[0]["sugarCB"].checked = false;
	document.forms[0]["powderedCB"].checked = false;
	document.forms[0]["sprinklesCB"].checked = false;
	document.forms[0]["chocolateCB"].checked = false;
	document.forms[0]["mapleCB"].checked = false;

	dsDonuts.removeAllFilters(true);
}
-->
</script>
<style type="text/css">
<!--
.badge {
	float: left; 
	margin: 4px;
	padding: 4px;
	text-align: center;
	background-color: #FFCC99;
	border-top: solid 1px #999999;
	border-left: solid 1px #999999;
	border-bottom: solid 1px #CCCCCC;
	border-right: solid 1px #CCCCCC;
}

label {
	font-weight: bold;
}
-->
</style>
</head>
<body>
<h3>Multiple Filters Mode Sample</h3>
<p>The multiple filter support within Spry can be switched between &quot;and&quot; and &quot;or&quot; mode. In &quot;and&quot; mode, the default, a row will only be added to the data set if all active filters return the row. If one filter returns a null, then the row will be filtered out. In &quot;or&quot; mode, if one of the active filters returns a row, that row will be added to the data set. If none of the filters return a row, then the row is filtered out.</p>
<p>In both modes, if there are no active filters, all rows will be added to the data set.</p>
<p>Here is a working example that allows you to switch between filter modes:</p>
<div class="liveSample" style="float: left; margin-bottom: 4px;">
	<form action="">
	<p>Show only donuts with the following toppings:</p>
	<label><input name="noneCB" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffNone);" /> None</label>
	<label><input name="glazedCB" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffGlazed);" /> Glazed</label>
	<label><input name="sugarCB" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffSugar);" /> Sugar</label>
	<label><input name="powderedCB" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffPowdered);" />Powdered Sugar</label>
	<label><input name="sprinklesCB" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffSprinkles);" /> Chocolate with Sprinkles</label>
	<label><input name="chocolateCB" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffChocolate);" /> Chocolate</label>
	<label><input name="mapleCB" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffMaple);" /> Maple</label>
	
	<p>
		<label>Filter by : 
		<select onchange="ToggleFilterSelect(this.value);">
			<option value="ffNone"> None</option>
			<option value="ffGlazed"> Glazed</option>
			<option value="ffSugar"> Sugar</option>
			<option value="ffPowdered">Powdered Sugar</option>
			<option value="ffSprinkles"> Chocolate with Sprinkles</option>
			<option value="ffChocolate"> Chocolate</option>
			<option value="ffMaple"> Maple</option>
		</select>
		</label>
	</p>

	<p><label>Filter Mode: <select onchange="dsDonuts.setFilterMode(this.value, true);"><option value="and" selected="selected">-- AND --</option><option value="or">-- OR --</option></select></label>
	<input type="button" value="Remove All Filters" onclick="RemoveAllFilters();" /></p>
	</form>
	<div spry:region="dsDonuts">
		<table>
			<tr><th>Type</th><th>Batter</th><th>Topping</th></tr>
			<tr spry:repeat="dsDonuts"><td>{name}</td><td>{batters/batter}</td><td>{topping}</td></tr>
		</table>
	</div>
</div>
<p><br style="clear: both;" />
</p>
<p>When running the example above in &quot;and&quot; mode, notice how any time you click more than one checkbox, no data shows up. That is because each row of the data set only contains a single type of topping, which means that at least one of the filters installed will return a null object instead of a row. If you switch the example into &quot;or&quot; mode, notice how it shows only entries that have toppings that are checked. This is because in &quot;or&quot; mode only it is only necessary for one of the active filters to return a row for it to be added to the data set.</p>
<p>The filter functions used in the example above look like this:</p>
<pre>
function ffNone(ds, row, index){ return (row.topping == &quot;None&quot;) ? row : null; };
function ffGlazed(ds, row, index){ return (row.topping == &quot;Glazed&quot;) ? row : null; };
function ffSugar(ds, row, index){ return (row.topping == &quot;Sugar&quot;) ? row : null; };
function ffPowdered(ds, row, index){ return (row.topping == &quot;Powdered Sugar&quot;) ? row : null; };
function ffSprinkles(ds, row, index){ return (row.topping == &quot;Chocolate with Sprinkles&quot;) ? row : null; };
function ffChocolate(ds, row, index){ return (row.topping == &quot;Chocolate&quot;) ? row : null; };
function ffMaple(ds, row, index){ return (row.topping == &quot;Maple&quot;) ? row : null; };
</pre>
<p>Checking a checkbox in the example above causes one of the functions listed above to be passed into a call to dsDonuts.addFilter(). For more info on adding/removing filters, check out the <a href="MultipleFiltersSample.html">Multiple Filters Sample</a>.</p>
<p>To switch filter modes, simply call the setFilterMode() function with a value of &quot;and&quot; or &quot;or&quot;:</p>
<pre>
&lt;script type=&quot;text/javascript&quot;&gt;
&lt;!--

var dsDonuts = new Spry.Data.XMLDataSet(&quot;../../data/donuts.xml&quot;, &quot;/items/item&quot;, { subPaths: &quot;topping&quot; });

...

// Switch into &quot;or&quot; filtering mode.

dsDonuts.setFilterMode("or");

...

// Switch back into &quot;and&quot; filtering mode.

dsDonuts.setFilterMode("and");

...
--&gt;
&lt;/script&gt;</pre>
<p>The call to setFilterMode() in the code </p>
<pre>
&lt;script type=&quot;text/javascript&quot;&gt;
&lt;!--

var dsDonuts = new Spry.Data.XMLDataSet(&quot;../../data/donuts.xml&quot;, &quot;/items/item&quot;, { subPaths: &quot;topping&quot; });

...

// Switch into &quot;or&quot; filtering mode and force the data set
// to re-apply all active filters.

dsDonuts.setFilterMode(&quot;or&quot;, true);

...

// Switch back into &quot;and&quot; filtering mode and force the
// data set to re-apply all active filters.

dsDonuts.setFilterMode(&quot;and&quot;, true);

...
--&gt;
&lt;/script&gt;</pre>
<p> above, places the multi-filter support within dsDonuts in &quot;or&quot; mode. The next time a filter is added to the data set, the &quot;or&quot; mode will be used while running each filter to figure whether or not the row should be added to the data set, or filtered out.</p>
<p>If you would like to change the filter mode and immediately re-filter the data, simply pass a true value as the 2nd argument:</p>
<p>&nbsp;</p>
</body>
</html>

Open in new window


Try it and let me know.
corrected:
<select name="state" onChange="ToggleFilter(this.options[this.selectedIndex].value,this.options[this.selectedIndex].text)">
<option value="NY">New York</option>
<option value="WI">Wisconsin</option>
</select>

If New York has been selected (index 0) then that onchnge is sending:

ToggleFilter('NY', 'NewYork')

so the receiving function should be

function ToggleFilter(abbr,state)
{
    your code
}


Cd&
It may have been a mistake to link the Adobe page because you're seeing that and not really reading the requirement in the question.  So yes, that's one line but it still requires me to write 60+ separate functions that get iterated through.  It will work, but it's inelegant and if I expand, I have to continue to write new functions.

I want to have one function that reads the value of the option and passes it into the comparison:

<select name="filterstate" onChange="ToggleFilter(this.value, ffState)">
   <option value="NY">New York</option>
   <option value="WI">Wisconsin</option>
</select>

Passing either 'NY' or 'WI' to:

function ffState(ds, row, index){ return (row.state == ???) ? row : null };

Make sense?
I think in the on change you should be calling ffState()

ToggleFilter() does not even reference the parameters from the select.

Can you post the whole script?  The way it hangs together does not make sense.


Cd&
Sure. Aside from all of the calls to the various Spry libraries, this is the whole script:

<script type="text/javascript">
var dsPrograms = new Spry.Data.XMLDataSet("directoryxml.php", "programs/program", {sortOnLoad: "type", sortOrderOnLoad: "ascending", useCache: false});
var pvPrograms = new Spry.Data.PagedView(dsPrograms, { pageSize: 15 });
var pvProgramsPagedInfo = pvPrograms.getPagingInfo();
var dsDirectors = new Spry.Data.NestedXMLDataSet(pvPrograms, "directors/director");

function ffAdv(ds, row, index){ return (row.type == 'Advanced GI') ? row : null };
function ffBari(ds, row, index){ return (row.type == 'Bariatric') ? row : null };
function ffColo(ds, row, index){ return (row.type == 'Colorectal/MIS') ? row : null };
function ffFlex(ds, row, index){ return (row.type == 'Flexible Endoscopy') ? row : null };
function ffHPB(ds, row, index){ return (row.type == 'HPB') ? row : null };
function ffMIS(ds, row, index){ return (row.type == 'MIS') ? row : null };
function ffMISBari(ds, row, index){ return (row.type == 'MIS/Bariatric') ? row : null };
function ffMISBariFlex(ds, row, index){ return (row.type == 'MIS/Bariatric/Flexible Endoscopy') ? row : null };
function ffMISFlex(ds, row, index){ return (row.type == 'MIS/Flexible Endoscopy') ? row : null };
function ffMISRobot(ds, row, index){ return (row.type == 'MIS/Robotic') ? row : null };
function ffThor(ds, row, index){ return (row.type == 'Thoracic') ? row : null };
function ffNA(ds, row, index){ return (row.accept == 'Yes') ? row : null };
function ffState(ds, row, index){ return (row.state == '???') ? row : null };

function ToggleFilter(enable, f)
{
	if (enable)
		dsPrograms.addFilter(f, true);
	else
		dsPrograms.removeFilter(f, true);
}


function RemoveAllFilters()
{
	document.forms[1]["fAdv"].checked = false;
	document.forms[1]["fBari"].checked = false;
	document.forms[1]["fColo"].checked = false;
	document.forms[1]["fFlex"].checked = false;
	document.forms[1]["fHPB"].checked = false;
	document.forms[1]["fMIS"].checked = false;
	document.forms[1]["fMISBari"].checked = false;
	document.forms[1]["fMISBariFlex"].checked = false;
	document.forms[1]["fMISFlex"].checked = false;
	document.forms[1]["fMISRobot"].checked = false;
	document.forms[1]["fThor"].checked = false;
	document.forms[1]["fNA"].checked = false;

	dsPrograms.removeAllFilters(true);
}
-->
//-->
</script>

Open in new window


And here is the matching HTML:

<h3>Filter by Program Type</h3>
<form action="">
	<p><label>Advanced GI: <input name="fAdv" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffAdv);" /></label>
    <label>Bariatric: <input name="fBari" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffBari);" /></label>
    <label>Colorectal/MIS: <input name="fColo" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffColo);" /></label>
    <label>Flexible Endoscopy: <input name="fFlex" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffFlex);" /></label>
    <label>HPB: <input name="fHPB" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffHPB);" /></label>
    <label>MIS: <input name="fMIS" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffMIS);" /></label>
    <label>MIS/Bariatric: <input name="fMISBari" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffMISBari);" /></label>
    <label>MIS/Bariatric/Flexible Endoscopy: <input name="fMISBariFlex" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffMISBariFlex);" /></label>
    <label>MIS/Flexible Endoscopy: <input name="fMISFlex" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffMISFlex);" /></label>
    <label>MIS/Robotic: <input name="fMISRobot" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffMISRobot);" /></label>
    <label>Thoracic: <input name="fThor" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffThor);" /></label></p>
    
<h3>Filter by Applicant Status</h3>
<label>Only show Programs who will accept applications from International Applicants: <input name="fNA" type="checkbox" value="" onclick="ToggleFilter(this.checked, ffNA);" /></label>	
<h3>Filter by State</h3>

<select onchange="ToggleFilter(this.value, ffState);">
<option value="NY">New York</option>
<option value="WI">Wisconsin</option>
</select>

<h3>Filter Options</h3>
<label>Multiple Selections: <select onchange="dsPrograms.setFilterMode(this.value, true);"><option value="or" selected="selected">Yes</option><option value="and">No</option></select></label>
<input type="button" value="Remove All Filters" onclick="RemoveAllFilters();" />
</form>

Open in new window

FYI, code above has been edited.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Actually, that helps more than you know.  I thought I was going crazy because I couldn't see a simple, normal way to pass the variable into the Spry framework and you're kind of confirming that is the case.

So we have the object dsPrograms  which appears to be some kind of datastore generated by a php script.
Then we have pvprograms which is a view of dsPrograms (I assume a subset of the data, but it could just be a different ordering).
Then we have a variable pvProgramsPageInfo (I think is a string) which seems logically to be an extract of the pvPrograms view.
Finally we have the object dsDirectors  and I don't have a clue what that is.

dsPrograms is a XML dataset that is created dynamically from a PHP script.
pvPrograms is a Spry object that tells dsPrograms to display in a paged manner
pvProgramsPageInfo is the extract of the paged view and used to maintain state and provide 1-10, 11-20, etc. links
dsDirectors is a nested recordset of dsPrograms

None of this relevant to the issue at hand. :)

That brings us to the functions they receive 3 parameters, but they are not called from anywhere unless it is from a method in one of the objects.

Right.  I'm not sure why the params are declared either.  I'm trying to decipher this by following the Spry documentation at:

http://labs.adobe.com/technologies/spry/samples/data_region/MultipleFiltersSample.html
and
http://labs.adobe.com/technologies/spry/samples/data_region/MultipleFiltersModeSample.html

but they don't explain the parameters either.

You also have one option.  If you go to a select multiple you can select more than one but again you don't know what was unselected or even if it was the intention to unselect.

Okay.  So basically there is no way to set the value from a select box using the current script logic?  I'm either going to have to convert to JQuery or run with the 60+ filters and spiritastir's solution?

I honestly thought Javascript was more flexible than that :(
Don't blame javascript... that would be shooting the messenger. The limitation is structural from the HTML.  At this point jquery is your best option.  I'm not a big fan of jquery for general scripting, but this is the kind of object crushing it was intended for.  90% of the time javascript is called for; 5% of the time you actually need the jquery enhanced object handling, and 5% of the time you need a quart of scotch and an axe handle on the side of the designer's head.


Cd&
BTW ... jquery IS javascript.  It is just a library of objects created to overpower HTML, CSS and the DOM to do things there were never intended to do.


Cd&
No, I know that jquery is a javascript library to handle specific tasks (not every single task as it seems to be used for these days).  Ok, thanks Cd&

5% of the time you need a quart of scotch and an axe handle on the side of the designer's head.

If I knew who it was, I'd even buy you the bottle.