![]() |
|
Snippets |
|
creates an InplaceSelect field. A text is as normal text shown, when you click on it, it transforms to a select field. After selecting a value the select field transforms back to normal text. The data shown in the select field is requested from the server.
Save this helper in your_project/lib/helper/AjaxInplaceSelectHelper.php:
<?php /** * AjaxInplaceSelectHelper. * * @package symfony * @subpackage helper * @author Thomas Eigner <webmaster@flyingfinger.de> */ /** * wrapper for InplaceSelect. * @param string $name The id of surrounding span * @param string $value The default text for the span * @param string $url The url to receive the update * @param string $json The url to fetch the list to show in the select field * @param int $selectedId The id of the element to select when select is shown first time * @param array $spanOptions The span tag options. (size, class, etc...) * @param array $inplaceOptions The options for the inplaceSelect (callback, etc...) * * @return string A span with the text, and InplaceSelect javascript tags */ use_helper('JavaScript'); function input_in_place_select_tag($name, $value, $url, $json, $selectedId, $spanOptions = array(), $inplaceOptions = array()) { $context = sfContext::getInstance(); $response = $context->getResponse(); $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/builder'); $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/prototype'); $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/effects'); $response->addJavascript('/js/inplaceselect'); $content = content_tag('span', $value, array('id' => $name)); $js_options = array(); if (isset($inplaceOptions ['callback'])) { $js_options['callback'] = $inplaceOptions['callback']; } else { $js_options['callback'] = "function(value, text) { return '".$name."_id='+value+'&".$name."_value='+text; }"; } if (isset($inplaceOptions ['onFailure'])) { $js_options['onFailure'] = $inplaceOptions['onFailure']; } if (isset($inplaceOptions ['onSuccess'])) { $js_options['onSuccess'] = $inplaceOptions['onSuccess']; } if (isset($inplaceOptions ['highlightcolor'])) { $js_options['highlightcolor'] = "'".addslashes($inplaceOptions['highlightcolor'])."'"; } if (isset($inplaceOptions ['highlightendcolor'])) { $js_options['highlightendcolor'] = "'".addslashes($inplaceOptions['highlightendcolor'])."'"; } if (isset($inplaceOptions ['savingText'])) { $js_options['savingText'] = "'".addslashes($inplaceOptions['savingText'])."'"; } if (isset($inplaceOptions ['cancelText'])) { $js_options['cancelText'] = "'".addslashes($inplaceOptions['cancelText'])."'"; } if (isset($inplaceOptions ['savingClassName'])) { $js_options['savingClassName'] = "'".addslashes($inplaceOptions['savingClassName'])."'"; } if (isset($inplaceOptions ['clickToEditText'])) { $js_options['clickToEditText'] = "'".addslashes($inplaceOptions['clickToEditText'])."'"; } if (isset($inplaceOptions ['cancelLink'])) { $js_options['cancelLink'] = ($inplaceOptions['cancelLink']) ? "true" : "false"; } $javascript = "new Ajax.InPlaceSelect('" . $name . "', '" . url_for($url) . "', '" . url_for($json) . "', " . $selectedId . ", " . _options_for_javascript($js_options) . " );"; return $content . javascript_tag($javascript); }
You also need an additional JS-File in your_project/lib/helper/AjaxInplaceSelectHelper.php:
/*
- inplaceselect.js -
Creates a <select> control in place of the html element with the id
specified. It functions similar to "Ajax.InPlaceEditor" but instead
of an <input> control, it creates a <select> control with a list of
<options> from which to choose. The parameters 'values' and 'labels'
are arrays (of the same length) from which the <options> are defined.
- Syntax -
new Ajax.InPlaceSelect('id', 'url', 'json', 'selected', { options });
- Example -
new Ajax.InPlaceSelect('someId', 'someURL', 'otherURL', 1,
{ callback: function(value, text) { return 'newval='+value+'&newtxt='+text; } } );
- Options('default value') -
- hightlightcolor("#FFFF99"): initial color (mouseover)
- hightlightendcolor("#FFFFFF"): final color (mouseover)
- onFailure(function(transport) {}): Called if failure occurs sending changes
- onSuccess(function(transport) {}): Called on sending changes successfully
- callback(function(value, text) { return 'newval='+value+'&newtxt='+text; }): function to
send additional parameters with the requests
- cancelText("cancel"): Text for the cancel link
- clickToEditText("Click to edit"): Tooltip for the text
- cancelLink(true): Should the cancel link be shown
Original JS-Script is from http://dev.rubyonrails.org/ticket/2667
*/
Ajax.InPlaceSelect = Class.create();
Ajax.InPlaceSelect.prototype =
{
initialize:function(element,url,json,selected,options)
{
this.element = $(element);
this.url = url;
this.json = json;
this.selected = selected;
this.values = new Array();
this.labels = new Array();
this.options = Object.extend(
{
highlightcolor: "#FFFF99",
highlightendcolor: "#FFFFFF",
onFailure: function(transport) {},
onSuccess: function(transport) {},
callback: function(value, text)
{
return 'newval='+value+'&newtxt='+text;
},
cancelText: "cancel",
clickToEditText: "Click to edit",
cancelLink: true,
},
options || {}
);
this.originalBackground = Element.getStyle(this.element, 'background-color');
if (!this.originalBackground)
{
this.originalBackground = "transparent";
}
this.element.title = this.options.clickToEditText;
this.ondblclickListener = this.enterEditMode.bindAsEventListener(this);
this.mouseoverListener = this.enterHover.bindAsEventListener(this);
this.mouseoutListener = this.leaveHoverNormal.bindAsEventListener(this);
Event.observe(this.element, 'click', this.ondblclickListener);
Event.observe(this.element, 'mouseover', this.mouseoverListener);
Event.observe(this.element, 'mouseout', this.mouseoutListener);
},
enterEditMode: function(evt)
{
if (this.saving) return;
if (this.editing) return;
this.editing = true;
new Ajax.Request(
this.json,
{
parameters: this.options.callback('', ''),
onSuccess:this.finishEnterEditMode.bind(this)
}
);
return false;
},
finishEnterEditMode: function(response)
{
var newData = eval(response.responseText);
this.values = new Array();
this.labels = new Array();
var i = 0;
var toSelect = 0;
for (var value in newData)
{
this.values.push(value);
if (value == this.selected) toSelect = i;
this.labels.push(newData[value]);
i++;
}
Element.hide(this.element);
this.createControls();
this.element.parentNode.insertBefore(this.menu, this.element);
this.menu.focus();
if (this.options.cancelLink)
{
this.element.parentNode.insertBefore(this.cancelButton, this.element);
}
this.menu.selectedIndex = toSelect;
return false;
},
createControls: function()
{
var options = new Array();
for (var i=0;i<this.values.length;i++)
options[i] = Builder.node('option', {value:this.values[i]}, this.labels[i]);
this.menu = Builder.node('select', options);
this.menu.onchange = this.onChange.bind(this);
this.menu.onblur = this.onCancel.bind(this);
for (var i=0;i<this.values.length;i++)
if (this.labels[i]==this.element.innerHTML)
{
this.menu.selectedIndex=i;
continue;
}
if (this.options.cancelLink)
{
this.cancelButton = Builder.node('a', this.options.cancelText);
this.cancelButton.onclick = this.onCancel.bind(this);
}
},
onCancel: function()
{
this.cleanUp();
this.leaveEditMode();
return false;
},
onChange: function()
{
var value = this.values[this.menu.selectedIndex];
var text = this.labels[this.menu.selectedIndex];
this.selected = value;
this.onLoading(text);
new Ajax.Request(
this.url, Object.extend(
{
parameters: this.options.callback(value, text),
onComplete: this.onComplete.bind(this),
onFailure: this.onFailure.bind(this)
},
this.options.ajaxOptions
)
);
},
onLoading: function(text)
{
this.saving = true;
this.removeControls();
this.leaveHover();
this.showSaving(text);
},
removeControls:function()
{
if(this.menu)
{
if (this.menu.parentNode) Element.remove(this.menu);
this.menu = null;
}
if (this.cancelButton)
{
if (this.cancelButton.parentNode) Element.remove(this.cancelButton);
this.cancelButton = null;
}
},
showSaving:function(text)
{
this.oldInnerHTML = this.element.innerHTML;
this.element.innerHTML = text;
this.element.style.backgroundColor = this.options.highlightcolor;
Element.show(this.element);
},
onComplete: function(transport)
{
this.options.onSuccess(transport);
this.cleanUp();
},
cleanUp: function()
{
this.leaveEditMode();
new Effect.Highlight(
this.element,
{
startcolor: this.options.highlightcolor,
endcolor: this.options.highlightendcolor,
restorecolor: this.originalBackground
}
);
},
onFailure: function(transport)
{
this.options.onFailure(transport);
if (this.oldInnerHTML)
{
this.element.innerHTML = this.oldInnerHTML;
this.oldInnerHTML = null;
}
return false;
},
enterHover: function()
{
if (this.saving) return;
this.element.style.backgroundColor = this.options.highlightcolor;
if (this.effect) { this.effect.cancel(); }
Element.addClassName(this.element, this.options.hoverClassName)
},
leaveHoverNormal: function()
{
if (this.saving) return;
this.element.style.backgroundColor = this.originalBackground;
},
leaveHover: function()
{
if (this.options.backgroundColor)
{
this.element.style.backgroundColor = this.oldBackground;
}
Element.removeClassName(this.element, this.options.hoverClassName)
if (this.saving) return;
this.effect = new Effect.Highlight(
this.element,
{
startcolor: this.options.highlightcolor,
endcolor: this.options.highlightendcolor,
restorecolor: this.originalBackground
}
);
},
leaveEditMode:function(transport)
{
this.removeControls();
this.leaveHover();
Element.show(this.element);
this.editing = false;
this.saving = false;
this.oldInnerHTML = null;
}
}
Now you need 3 actions
First one to build your complete HTML page:
...
executeShow()
{
$this->article = Article::retrieveByPk(1);
}
...
and the according template showSuccess.php:
<?php use_helper('AjaxInplaceSelect') ?> <h1><?php echo $article->getName() ?></h1> <strong>author:</strong> <?php echo input_in_place_select_tag( 'author_dom_id', $article->getAuthor()->getName(), 'module/updateChanges', 'module/jsonUpdate', $article->getAuthor()->getId(), array(), array('callback' => 'function(value, text) { return \'author_id=\'+value+\'&article='.$article; }')); ?>
Then the second action will generate the data to show in select field when clicking on the author. The data is passed to the InplaceSelect as JSON code:
...
executeJsonUpdate()
{
$authors = Author::doSelect(new Criteria());
$data = array():
foreach($authors as $author)
{
$data[$author->getId()]=$author->getName();
}
$this->renderText('('.json_encode($data).')');
}
...
And finally still the action that is receiving the change and saving it
...
executeUpdateChanges()
{
$newAuthorId = $this->getRequestParameter('author_id');
$articleId = $this->getRequestParameter('article');
$article = Article::retrieveByPk($articleId);
$article->setAuthorId($newAuthorId );
$article->save();
$this->renderText('');
}
...
That's it! Now the InplaceSelect should work fine. After clicking an option of the select field, it transforms back to normal text which show now the selected option. Instead of only pass over the text of the option, of course you can specify something else. For example if you normally show beside the author name also the number of books written by him, you do not need to show all this information also in the select field in order to still have this information for a new selected author. Therefore you can define a onSuccess function when calling the helper in the showSuccess template:
<?php use_helper('AjaxInplaceSelect') ?> <h1><?php echo $article->getName() ?></h1> <strong>author:</strong> <?php echo input_in_place_select_tag( 'author_dom_id', $article->getAuthor()->getName() . ' [' . $article->getAuthor()->getNumberOfBooks() . ' Book(s)]', 'module/updateChanges', 'module/jsonUpdate', $article->getAuthor()->getId(), array(), array( 'onSuccess' => 'onSuccess' => 'function(transporation) { $(\'author_dom_id\').innerHTML = transporation.responseText; }', 'callback' => 'function(value, text) { return \'author_id=\'+value+\'&article='.$article; }') ); ?>
and when saving the changes render the more detailed Author info:
...
executeUpdateChanges()
{
$newAuthorId = $this->getRequestParameter('author_id');
$articleId = $this->getRequestParameter('article');
$article = Article::retrieveByPk($articleId);
$article->setAuthorId($newAuthorId );
$article->save();
$this->renderText($article->getAuthor()->getName() . ' [' . $article->getAuthor()->getNumberOfBooks() . ' Book(s)]');
}
...
There are still some more option which you can use, check helper the source code.
Enjoy the InplaceSelect!
I've found it annoying to set the show and hide commands on each remote request. So I've used this to define it globally:
/* * Defines the indicator globally */ Ajax.Responders.register({ onCreate: function() { Element.show('indicator'); }, onComplete: function() { Element.hide('indicator'); } });
This helper creates a div tag tag into which links can call other actions and dynamically display its output via AJAX. When JS is not enabled, the links reload the page with GET parameters and allow this helper to load the output.
What do you think of this code? First of all, has it already been done before? And do you think it will actually be useful? How well does it conform to the MVC structure?
Actual code:
<?php /** * This file includes functions that assist in making AJAX degradable * * These small helper functions add in some of the basic features that allow for degradable * JS. The idea is to dynamically load contents when JS is available and let the page reload with * appropriate html when JS is not. * @author Yining Zhao * @package YZ_Helpers * @subpackage AJAX_Degredation * @since 1/16/2007 */ /** * This class creates a pair of <div> tags in which AJAX functions can input dynamic HTML. * * Usually what happends is that a link is pressed to activate the AJAX command via its onClick method. * The HREF attribute is suppressed by a 'return false;' at the end of the onClick method. However * when JS is not available, the onClick method is not activated and thus the HREF is called. The * link in the HREF reloads the page and uses GET to pass along the variables to load the designated * fragment. * * If the $trigger_var is 'login_box', here is what the following GET parameters will do: * login_box_display will load the fragment if it is set to 'on', otherwise div style = visibility:none and nothing is loaded * e.g. URL: www.website.com/file.php?login_box_display=on //will load fragment * * login_box_make_persistant will let set login_box_display to 'on' in the user session, so as long as * login_box_make_persistant is not set to 'no', the fragment will automatically be loaded on every page * load regardless of the GET statement * e.g. URL: www.website.com/file.php?login_box_make_persistant=on //will load fragment on every page load * * Sample usage of function: * //This function is being called from listSuccess.php template file in my Post module * div_fragment_creater('login_box','partial','loginPartial',array('referer'=>$referer),array('id'=>'login_div_id')) * * The source: {@source} * * @param string $trigger_var The variable passed via GET that determines if the partial is loaded * @param string $fragment_type This tells the function if you are using a partial or a component * @param string $fragment_filename The name of the fragment to be called * @param array $fragment_values_ary This passes in the values for the fragment * @param array $div_attr_ary This contains all the html attributes for the <div> tag * @return void Since this function is designed to echo the necessary html, return is void */ function div_fragment_creater($trigger_var,$fragment_type, $fragment_filename,$fragment_values_ary,$div_attr_ary) { $sf_user=sfContext::getInstance()->getUser()->getAttributeHolder(); $sf_params=sfContext::getInstance()->getRequest()->getParameterHolder(); //If in the GET there is a make_persistant command for the given trigger variable, then the display command for the //tigger is set for sessions if($sf_params->get($trigger_var.'_make_persistant')=='on') { $sf_user->set($trigger_var.'_display','on'); } else if($sf_params->get($trigger_var.'_make_persistant')=='off') { $sf_user->remove($trigger_var.'_display'); $sf_params->remove($trigger_var.'_display'); } //If the display is set for the trigger in either sf_params or sf_user, display the fragment. //Otherwise, don't display the fragment and make the div attribute style = display:none $output_fragment=false; if(($sf_params->get($trigger_var.'_display')=='on')||($sf_user->get($trigger_var.'_display')=='on')) { $output_fragment=true; } else { $div_attr_arry['style'] = 'display:none'; } $div_attr_keys = array_keys($div_attr_ary); $div_attr_ary_size = count($div_attr_keys); $div_attr=''; $div_attr_collection=''; //Cteates the attributes for the <div> for($i=0;$i<$div_attr_ary_size;$i++) { //This creates a string where the div attribute is set to its corresponding value; e.g. id=5 $div_attr_name = $div_attr_keys[$i]; $div_attr_collection.=$div_attr_name . ' = "' . $div_attr_ary[$div_attr_name] . '" '; } echo '<div '.$div_attr_collection.' >'; if($output_fragment==true) { switch($fragment_type) { case 'partial': echo include_partial($fragment_filename, $fragment_values_ary); break; case 'component': use_component($fragment_filename, $fragment_values_ary); break; } } echo '</div>'; } ?>
Usage Example: In myApp/myModule/list.php:
<body> Anything is possible... <?php //Creates div with id = myDiv. Usually this div is invisible and empty. But when the get or session variable of triggerVar_display is set to 'on', this div becomes visible and displays the whatsPossiblePartial partial file. div_fragment_creater('triggerVar','partial','whatsPossiblePartial',array('id'=>$id),array('id'=>'myDiv')) //Provides link to load action into myDiv. If JS is not enabled, link will reload page with triggerVar_display set to 'on' echo link_to_remote('Click To See Whats Possible', array( 'url'=>'MyModule/WhatPossible?id='.$value, 'update'=>array('success' => 'myDiv'), 'loading'=>"Element.show('indicator')", 'complete'=>visual_effect('blind_down','myDiv',array('duration'=>0.5))';', ), array('href'=>'list?triggerVar_display=on')); ?> </body>
Inside the whatPossibleSuccess.php:
<html> ... <?php //This file is basically just a wrapper for the whatsPossiblePartial. the reason I have this wrapper is so that I can access it from via link_to_remote() on other pages. echo include_partial(whatsPossiblePartial,array('id'=>$this->id)); ?> ... </html>
There are two multi-select box, the first one is parent list menu, the second one is child menu; when clicking item in first list, its submenu will appear in second list, then you can multi-select from 2nd list; and you can multi-select items in 1st list by clicking, double-click, ctrl+click, shift+click, all selected items in 1st list will display their children in 2nd list, all deselected items from 1st list will remove their children from 2nd list. This combo list can be used for select HOBBIES, TYPES, CATEGORIES etc.
*** in multiselSuccess.php:
<script type="text/javascript" src="/sf/js/prototype/prototype.js"></script> <body onload="new Ajax.Request('/news/select/level/province', {asynchronous:true, evalScripts:false, onComplete:function(request, json){updateJSON(request,json,1)}}); return false;" >
*** in multiselSuccess.php:
<?php echo select_tag("province",options_for_select(Array(''=>'-Select-')),'multiple=multiple size=10 onmousedown=GetCurrentListValues(this); onchange=javascript:loadCity(\''.sfConfig::get('app_site_url').'news/select/level/city\',this)'); echo select_tag('city[]',options_for_select(Array(''=>'-Select-')),'multiple=multiple size=10'); ?> <div id=statusTxt></div>
*** in view.yml:
selectSuccess: has_layout: off
*** javascript can be written in multiselSuccess.php, or in another javascript file multiselSuccess.js: if written in a seperate file multiselSuccess.js, put it under PROJECT_NAME\web\js\, and in ...\apps\APPLICATION_NAME\config:
javascripts: [multiselSuccess.js ]
<script language="javascript"> <!--// /** * ajax no refresh cascading 2-level menu * */ function loadCity(url,CONTROL) { //load city var strTmp=provinceChanged(CONTROL); //result format: +provinceId or -provinceId var provChg=strTmp.split(","); url=url+"/id/"+provChg[0]; //provChg[0]: item value of first menu; url: /news/select/level/city/id/ItemValueOfFirstMenu new Ajax.Request(url, {asynchronous:true, evalScripts:false, onComplete:function(request, json){updateJSON(request,json,2,provChg[1])}}); return false; } var arrOldValues; function GetCurrentListValues(CONTROL){ var strValues = ""; strValues = GetSelectValues(CONTROL); arrOldValues = strValues.split(",") } function GetSelectValues(CONTROL){ var strTemp = ""; for(var i = 0;i < CONTROL.length;i++){ if(CONTROL.options[i].selected == true){ strTemp += "1,"; } else{ strTemp += "0,"; } } return strTemp; } function provinceChanged(CONTROL){ var strTemp = GetSelectValues(CONTROL); arrNewValues = strTemp.split(","); var sum=0; for(var i=0;i<arrNewValues.length-1;i++){ sum+=eval(arrNewValues[i]); if (sum >= 2){ //more than 1 are selected break; } if (arrNewValues[i] == 1){ //when this condition is met, sum must be 1 var selectedPos=i; } } if (sum==0){ //deselect all by ^Click; remove all from 2nd menu provChg=""+",--"; } else if (sum==1){ //only one selected, click or ctrl+click if (arrOldValues[selectedPos] == 0){ //if this selcted one wasn't selected before, just add; //at this point,arrNewValues[selectedPos] must be 1 removeOptionAll("city"); provChg=CONTROL.options[selectedPos].value+",+"; } else{ //if this selcted one was selected before, remove all others but keep selected of this one removeOptionAll("city"); provChg=CONTROL.options[selectedPos].value+",+"; //???to simplify, just add, previously selected may be lost } } else{ // for(var i=0;i<arrNewValues.length-1;i++){ if (arrNewValues[i] != arrOldValues[i]){ if (arrNewValues[i]==1){ //add a new province provChg=CONTROL.options[i].value+",+"; } else{ //remove a new province provChg=CONTROL.options[i].value+",-"; } } } } return provChg; } /** * ajax no refresh 2-level cascading menu * */ function addOption(objSelectNow,txt,val) { //using W3C syntax add Options for SELECT var objOption = document.createElement("OPTION"); objOption.text= txt; objOption.value=val; objSelectNow.options.add(objOption); } function addOptionGroup(selectId,optGroup) { var objSelect = document.getElementsByTagName("SELECT"); var objSelectNow = objSelect[selectId]; if (selectId=="province"){ objSelectNow.length = 1; } /// add Options group for (i=0; i<optGroup.length; i++) { addOption(objSelectNow, optGroup[i][1], optGroup[i][0]); } } function removeOptionGroup(selectId,optGroup) //optGroup: array of 2nd menu corresponding to deselected item of 1st menu; categoryId=>categoryName { var objSelect = document.getElementsByTagName("SELECT"); var objSelectNow = objSelect[selectId]; for (i=0;i<objSelectNow.length;i++){ if (objSelectNow.options[i].value==optGroup[0][0]){ //the first position to be removed var bgn=i; break; } } /// remove Options for (i=0;i<optGroup.length;i++){ objSelectNow.options.remove(bgn); } } function removeOptionAll(selectId) { var objSelect = document.getElementsByTagName("SELECT"); var objSelectNow = objSelect[selectId]; for (i=1;i<objSelectNow.options.length;){ objSelectNow.options.remove(1); } } function updateJSON(request, json,level,operation){ var responses = json; if (!json){ //if you don't use the json tips then evaluate the renderedText instead var responses = eval('(' + request.responseText + ')'); } if (level==1) //for 1st menu addOptionGroup("province",responses); else if (level==2){ //for 2nd menu if (operation=="+"){ addOptionGroup("city",responses); } else if (operation=="-"){ removeOptionGroup("city",responses); } else if (operation=="--"){ removeOptionAll("city"); } } } --> </script>
*** in actions.classs.php:
public function executeSelect(){ /** * ajax no refresh 2-level cascading menu back-end * */ //load data: $c=new Criteria(); $c->add(CategoryPeer::PARENT_ID,NULL); //get contents for 1st menu $categories=CategoryPeer::doSelect($c); $parent=""; if ($categories){ $i=0; $parent='['; foreach ($categories as $category){ $name=$category->getName(); $id=$category->getId(); $parent=$parent.'["'.$id.'","'.$name.'"],'; //format for 1st menu: [["1","German"],["2","France"],["3","Argentina"]] //retrive records whose parent_id is.. $c=new Criteria(); $c->add(CategoryPeer::PARENT_ID,$category->getId()); $categories_sub=CategoryPeer::doSelect($c); $city[$id]='['; //index of $city[] is category id; format of $city[]:[["48","Team1.Fr"],["49","Team2.Fr"],["50","Team3.Fr"],["51","Team4.Fr"],["52","Team5.Fr"]] foreach ($categories_sub as $category_sub){ $city[$id]=$city[$id].'["'.$category_sub->getId().'","'.$category_sub->getName().'"],'; } $city[$id]=rtrim($city[$id],","); $city[$id].=']'; $i++; } } $parent=rtrim($parent,","); $parent.=']'; $level=$this->getRequestParameter('level'); if($level=="province"||$level==""){ //level=province, output contents for first menu $this->output=$parent; } else if($level=="city"){ //level=city, output contents for second menu $id=$this->getRequestParameter('id'); $this->output=$city[$id]; } else{ //false $this->output="error when getting data"; } return sfView::SUCCESS; }
*** in selectSuccess.php:
<?php echo $output; ?>
function process(feeder, wrapper, method, content) { xmlhttp = getxmlhttp(); if(method == "GET") { xmlhttp.open("GET", feeder,true); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { wrapper.innerHTML = xmlhttp.responseText; } } xmlhttp.send(null); } else { xmlhttp.open("POST", feeder, true); xmlhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=iso-8859-1"); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { wrapper.innerHTML = xmlhttp.responseText; dumpingData(); //toogleEditorMode('contenttext'); } } xmlhttp.send(content); } } /***** USAGE obj = document.getElementById('content'); process ("feeder.php?go="+obj.value, obj, "GET", ""); *****/
function getxmlhttp() { //Create a boolean variable to check for a valid IE instance. var xmlhttp = false; //Check if we are using IE. try { //If the javascript version is greater than 5. xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { //If not, then use the older active x object. try { //If we are using IE. xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } catch (E) { //Else we must be using a non-IE browser. xmlhttp = false; } } //If we are using a non-IE browser, create a JavaScript instance of the object. if (!xmlhttp && typeof XMLHttpRequest != 'undefined') { xmlhttp = new XMLHttpRequest(); } return xmlhttp; }
function getxmlhttp() { //Create a boolean variable to check for a valid IE instance. var xmlhttp = false; //Check if we are using IE. try { //If the javascript version is greater than 5. xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { //If not, then use the older active x object. try { //If we are using IE. xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } catch (E) { //Else we must be using a non-IE browser. xmlhttp = false; } } //If we are using a non-IE browser, create a JavaScript instance of the object. if (!xmlhttp && typeof XMLHttpRequest != 'undefined') { xmlhttp = new XMLHttpRequest(); } return xmlhttp; } [/code/
You can pass aditional parameters to input_auto_complete_tag helper. By default, autocompleter pass only object-referer value. For aditional parameter you need use 'with' in $completion_options array. in view layer:
< <?php echo object_input_tag($model, 'getFilter', array ('size' => 20, 'control_name' => 'filter',));?> ?php echo input_auto_complete_tag('field_to_search','','module/autocomplete', array('autocomplete'=>'off'),array('use_style'=>true,'with'=> " value+'&filter='+$('filter').value"))?>
you must put '" value' (with whitespace) because javascript fail (a little symfony bug). Aditional parameters must go after value.
in control layer:
public function executeAutocomplete(){ $search=$this->getRequestParameter('field_to_search'); $filter=$this->getRequestParameter('filter'); .... }
now you can use field_to_search and filter to construct any query in model layer ans show this in AutocompleteSuccess.php view or another view that you define. this was tested in synfony version > 0.9. Check your version, and review your javascripthelper helper to verify if your input_auto_complete_tag helper supports 'with' option
What is worth noting in the snippet below is the usage of the 'onchange'=> remote_function( ... you can use the remote_function in many places to call other functions ... like in the "update", "complete" etc...
<div id="js_updating">Stand by..</div> <?php echo form_tag('/module/action', 'method=get class=simpleForm') ?> Please select the day: <?php echo input_date_tag('day', 'now', array('rich' => true, 'readonly'=>true, 'onchange'=> remote_function( array( 'update' => 'Area To Update (DIV TAG)', 'url' => 'MODULE/ACTION', 'loading' => "Element.show('js_updating')", 'complete' => "Element.hide('js_updating')" )))); ?> </form>