-
Notifications
You must be signed in to change notification settings - Fork 4
jQuery Ajax Helper
Category:Contributions::Helpers::Ajax
jQuery is one of, if not the most popular JavaScript library available. I was surprised to not find any helpers for it for CodeIgniter. So, I decided to write my own and contribute back to the CI community.
Setup:
-
You can download the files here: File:jquery-helper-0.3.zip
-
Place jquery_helper.php in your application/helpers/ folder.
-
You'll also need these asset files (or your own local copies):
http://jqueryui.com/latest/themes/base/ui.all.css http://code.jquery.com/jquery-latest.js http://jqueryui.com/latest/ui/ui.core.js http://jqueryui.com/latest/ui/ui.draggable.js http://jqueryui.com/latest/ui/ui.droppable.js http://jqueryui.com/latest/ui/ui.resizable.js http://jqueryui.com/latest/ui/ui.dialog.js
Note: I developed this helper using jQuery 1.3.1
(Optional) If you want to use autocomplete input with Ajax you need to:
- Place autocomplete.php in your application/hooks/ folder.
- Edit your application/config/hooks.php and add:
$hook['pre_system'][] = array(
'class' => 'Autocomplete',
'function' => 'override_get',
'filename' => 'autocomplete.php',
'filepath' => 'hooks',
'params' => array()
);
- Download jQuery Autocomplete plugin: http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/
- Setup these asset files on your server: /path/to/your/jquery.autocomplete.css /path/to/your/local/jquery.autocomplete.min.js
Note: You can still use autocomplete inputs by statically passing an array of data by just doing steps 6 & 7 if you don't want to use the hook.
How to use the jquery helper:
A) Either add 'jquery' to autoload or in your controller class call $this->load->helper('jquery');
B) In your views use the jquery helper functions. Most are wrappers on top of form helper functions.
For example, you may have a view with a form for searching stories on Digg. You want the user to enter in a
search term, click submit on the form and have the results appear in a submodal dialog box:
search_form.php view:
<p>Search for Digg stories:</p>
<div class="search-form">
<?=ajax_form_open('diggsearch/search_digg', array('id'=>'formDiggSearch'))?>
<div>
<?=form_input('searchbox',$searchbox,'maxlength="40" size="40"'))?>
<input id="searchBtn" type="submit" name="search" value="Search" /><br/>
(Enter in terms to find story title matches)
</div>
<?=ajax_form_close()?>
<?=simple_callback_dialog('formDiggSearch', array('modal'=>true,'height'=>400,'width'=>580))?>
</div>
Then you just need to process the POST in your controller (ex. Diggsearch->search_digg()) as you would for a normal POST
and load the results view. For instance:
function search_digg() {
ini_set('user_agent', 'SampleCIjQueryHelperApp/1.0');
$data['searchbox']=$this->input->post('searchbox', true);
$diggApiResponse = file_get_contents("http://services.digg.com/search/stories?query=". urlencode($data['searchbox'])."&appkey=http://example.com&type=json");
$stories = json_decode($diggApiResponse);
$this->load->view('diggsearch/search_results',$stories);
}
// update the app key to your domain. example.com won't work.
Similarly, you may have an anchor that when the user clicks it, you want to slide down the search form:
index.php view snippet:
<div style="margin-left:20%;width:800px;">
<h1>Search Digg Sample CodeIgniter WebApp using jQuery Helper</h1>
<div style="float:right;">
<?=ajax_get_anchor('diggsearch/search_form','Search Digg', array('id'=>'search_anchor'))?>
</div>
<div class="cb"></div>
<?=simple_callback_div('search_anchor', 'slideDown(1000)', array('style'=>'float:right;'))?>
<p>
This is a sample Digg Search application that uses my jquery ajax helper for CodeIgniter.
<br/><br/>
Click the "Search Digg" link to the right to view the search form.
</p>
</div>
C) Ajax autocomplete input:
You need to write a controller method that will output the return data one per line. I find the best way is to
get a single dimension array variable with your data and then:
echo implode("\n",$singleDimensionalArray);
The autocomplete pre_system hook is required because the autocomplete plugin passes 3 query parameters in its
GET request: "q", "limit", and "timestamp". The hook will detect these and clear the query string and put them in
the POST. So in your controller method you can access those 3 params like form data to do your processing (to query
yor db or whatever) to populate your single dimensional array. The autocomplete plugin can handle more complex
scenarios but this helper function is only for the base use case.
Back to the example of the Digg search form. Say you want to suggest stories as the user is typing in the searchbox input.
You'll need to make this change to your search_form.php view:
<p>Search for Digg stories:</p>
<div class="search-form">
<?=ajax_form_open('diggsearch/search_digg', array('id'=>'formDiggSearch'))?>
<div>
<?=ac_form_input('searchbox',$searchbox,'maxlength="40" size="40"', 'diggsearch/suggest', array('max'=>5,'min'=>3))?>
<input id="searchBtn" type="submit" name="search" value="Search" /><br/>
(Enter in terms to find story title matches)
</div>
<?=ajax_form_close()?>
<?=simple_callback_dialog('formDiggSearch', array('modal'=>true,'height'=>400,'width'=>580))?>
</div>
Then you will need to create a Diggsearch->suggest() method that returns the matches one per line:
function suggest() {
ini_set('user_agent', 'SampleCIjQueryHelperApp/1.0');
$diggApiResponse = file_get_contents("http://services.digg.com/search/stories?query=". urlencode($this->input->post('q', true))."&appkey=http://example.com&type=json");
$stories = json_decode($diggApiResponse);
$suggestions = array();
for($i=0;$i<$this->input->post('limit', true); $i++) {
$suggestions[] = $stories->stories[$i]->title;
}
echo implode("\n", $suggestions);
}
Note: You need to change the app key to your domain. example.com will not work. Also don't abuse the Digg api. You can get your IP banned.
This is just for a learning example. Also, some web hosts don't allow URL fetching with file_get_contents.
Here is the contents of jquery_helper.php:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/*
By Brodie Hodges, Oct 22, 2009.
*/
/*
ajax_form_open will create a form using standard CI form_open helper function but add jQuery onsubmit js code to
"ajaxify" the post. Only one additional optional parameter for return type to callback function over standard
form_open(). Defaults html return type. If no id attribute is passed in, "ajax_form" is used for the form's id.
The id is used for the callback function name. My convention assumes that all callback function names as the form's
id + "_callback". You can either create your own JavaScript callback function with that name in your view, or use
one of the "simple_" helper functions below in your view to automatically create the callback function and containing
element. The $.post() is appended to the end of the passed in onsubmit attribute value, so it is possible to do some
pre-POST processing with JavaScript, if required.
*/
if ( ! function_exists('ajax_form_open')) {
function ajax_form_open($action = '', $attributes = array(), $hidden = array(), $returnType = 'html') {
$obj =& get_instance();
$obj->load->helper('form');
if ( ! array_key_exists('onsubmit', $attributes)) {
$attributes['onsubmit'] = "";
} else {
$onsubmit = trim($attributes['onsubmit']);
if ( ! substr($onsubmit,-1) == ";") {
$attributes['onsubmit'] = $onsubmit . ";";
}
}
if ( ! array_key_exists('id', $attributes)) {
$attributes['id'] = "ajax_form";
}
$attributes['onsubmit'] .= "$.post(this.action,$('#'+this.id).serialize(),function(data,textStatus) {".$attributes['id']."_callback(data);},'".$returnType."');return false;";
return form_open($action, $attributes, $hidden);
}
}
/*
This function is included for aesthetics so that visually matches to the ajax_form_open in your view. You can use
form_close() or "</form>" if you want, instead.
*/
if ( ! function_exists('ajax_form_close')) {
function ajax_form_close() {
return "</form>";
}
}
/*
function ajax_get_anchor is very similar to ajax_form_open except it creates a jQuery ajax get that uses the anchors
href attribute (generated from the $uri param). A callback is called which is the id attribute + "_callback". You
can either create a function called that in your view or use one of the "simple_" functions below to do that for you.
The default id is set to "ajax_get_anchor" if no id attribute is passed in to the helper, thus the default callback
JavaScript function is "ajax_get_anchor_callback()". The $.get() is appended to the end of the passed in onclick
attribute value, so it is possible to do some pre-GET processing with JavaScript, if required. Defaults html return
type.
*/
if ( ! function_exists('ajax_get_anchor')) {
function ajax_get_anchor($uri = '', $title = '', $attributes = array(), $returnType = 'html') {
$obj =& get_instance();
$obj->load->helper('url');
if ( ! array_key_exists('onclick', $attributes)) {
$attributes['onclick'] = "";
} else {
$onclick = trim($attributes['onclick']);
if ( ! substr($onclick,-1) == ";") {
$attributes['onclick'] = $onclick . ";";
}
}
if ( ! array_key_exists('id', $attributes)) {
$attributes['id'] = "ajax_get_anchor";
}
$attributes['onclick'] .= "$.get(this.href,'',function(data,textStatus) {".$attributes['id']."_callback(data);},'".$returnType."');return false;";
return anchor($uri,$title,$attributes);
}
}
/*
ajax_load_anchor creates an anchor tag that will jquery ajax load the contents into the specified selector's element(s).
$returnSelector is the where the content returned from the GET is put and can be any appropriate jQuery selector.
Per jQuery ajax load function documentation, the $optionalLoadSelector is appended to the load url and will filter
the incoming HTML document, only injecting the elements that match the $optionalLoadSelector.
$useJsCallback is a boolean that defaults to false. If set to true the anchor id attribute + "_callback" or
default "ajax_load_anchor_callback()" is called on return.
*/
if ( ! function_exists('ajax_load_anchor')) {
function ajax_load_anchor($returnSelector, $optionalLoadSelector = '', $useJsCallback = false, $uri = '', $title = '', $attributes = array()) {
$obj =& get_instance();
$obj->load->helper('url');
if ( ! array_key_exists('onclick', $attributes)) {
$attributes['onclick'] = "";
} else {
$onclick = trim($attributes['onclick']);
if ( ! substr($onclick,-1) == ";") {
$attributes['onclick'] = $onclick . ";";
}
}
if ($useJsCallback) {
if (array_key_exists('id',$attributes) && ! $attributes['id'] === "") {
$js_callback = $attributes['id'] . "_callback";
} else {
$js_callback = "ajax_load_anchor_callback";
}
}
if ($optionalLoadSelector) {
$attributes['onclick'] .= "$('".$returnSelector."').load(this.href+' ".$optionalLoadSelector . "'";
} else {
$attributes['onclick'] .= "$('".$returnSelector."').load(this.href";
}
if ($useJsCallback) {
$attributes['onclick'] .= ",'',function(data) {".$js_callback."(data);}";
}
$attributes['onclick'] .= ");return false;";
return anchor($uri,$title,$attributes);
}
}
/*
Include this function in your view for your html return content to show in a div. The div will
exist on initial page render and thus will be structurally in your view right where you called
this function. The ajax form's or ajax get anchor's id attribute value must be passed in with
the $callingElementId param.
*/
if ( ! function_exists('simple_callback_div')) {
function simple_callback_div($callingElementId, $displayEffect = 'show("slow")', $attributes = '') {
$obj =& get_instance();
$obj->load->helper('form');
$formattedAttributes = _parse_form_attributes($attributes, array());
$simpleHtmlCallbackDiv = <<<CONTENT
<div id="${callingElementId}_results" style="display:none;"${formattedAttributes}>
loading ...
</div>
[removed]
function ${callingElementId}_callback(htmlContent) {
$("#${callingElementId}_results").html(htmlContent).${displayEffect};
}
[removed]
CONTENT;
return $simpleHtmlCallbackDiv;
}
}
/*
Include this function in your view for your html return content to show in a jquery dialog div.
The dialog div is injected by jQuery and appended to the body tag. This will allow the dialog
to be opened and closed more than once. By default, closing the dialog destroys the dialog div.
The ajax form's or ajax get anchor's id attribute value must be passed in with the
$callingElementId param.
*/
if ( ! function_exists('simple_callback_dialog')) {
function simple_callback_dialog($callingElementId, $dialogOptions = array(), $attributes = '') {
$obj =& get_instance();
$obj->load->helper('form');
$formattedAttributes = _parse_form_attributes($attributes, array());
$formattedDialogOptions = _format_jquery_options($dialogOptions);
$simpleHtmlCallbackDialog = <<<CONTENT
[removed]
function ${callingElementId}_callback(htmlContent) {
theDialog = $('<div id="${callingElementId}_results"${formattedAttributes}>loading ...</div>').appendTo('body');
theDialog.html(htmlContent);
theDialog.dialog( { ${formattedDialogOptions} } );
}
[removed]
CONTENT;
return $simpleHtmlCallbackDialog;
}
}
/*
This function creates a standard text input with autocomplete functionality.
Requires that http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/ plugin is setup on your
server. Documentation: http://docs.jquery.com/Plugins/Autocomplete
List of autocomplete options is here:
http://docs.jquery.com/Plugins/Autocomplete/autocomplete#url_or_dataoptions
pass in array form with $acOptions param.
$uriOrData param can be a uri string (e.g. "/user/view/") or a single dimensional array with choices
Ajax autocomplete requires a pre_system hook to function correctly. Add to your
application/config/hooks.php if not already there:
$hook['pre_system'][] = array(
'class' => 'Autocomplete',
'function' => 'override_get',
'filename' => 'autocomplete.php',
'filepath' => 'hooks',
'params' => array()
);
Make sure autocomplete.php is in your application/hooks/ folder.
*/
if ( ! function_exists('ac_form_input')) {
function ac_form_input($data = '', $value = '', $extra = '', $uriOrData = '', $acOptions = array() ) {
$obj =& get_instance();
$obj->load->helper(array('form', 'url'));
$formattedAcOptions = _format_jquery_options($acOptions);
if (is_string($data)) {
$name = $data;
unset($data);
$data['name'] = $name;
$data['value'] = $value;
}
if ( is_array($data) && (! array_key_exists('id',$data) || $data['id'] === "") ) {
$data['id'] = 'ac_input';
}
$acJs = '';
if ( is_string($uriOrData) ) {
$url = $uriOrData . "/";
$acJs = <<<JS
[removed]
$(document).ready(function() {
$('#${data['id']}').autocomplete('${url}',{ ${formattedAcOptions} });
});
[removed]
JS;
} elseif ( is_array($uriOrData) ) {
$arrData = implode(' ',$uriOrData);
$acJs = <<<JS
[removed]
$(document).ready(function() {
var staticData = "${arrData}".split(" ");
$('#${data['id']}').autocomplete(staticData, ${formattedAcOptions});
});
[removed]
JS;
}
$acInput = form_input($data, $value, $extra);
return ($acJs . $acInput);
}
}
/*
This function creates a standard textarea with autocomplete functionality.
Requires that http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/ plugin is setup on your
server. Documentation: http://docs.jquery.com/Plugins/Autocomplete
List of autocomplete options is here:
http://docs.jquery.com/Plugins/Autocomplete/autocomplete#url_or_dataoptions
pass in array form with $acOptions param.
$uriOrData param can be a uri string (e.g. "/user/view/") or a single dimensional array with choices
Ajax autocomplete requires a pre_system hook to function correctly. Add to your
application/config/hooks.php if not already there:
$hook['pre_system'][] = array(
'class' => 'Autocomplete',
'function' => 'override_get',
'filename' => 'autocomplete.php',
'filepath' => 'hooks',
'params' => array()
);
Make sure autocomplete.php is in your application/hooks/ folder.
*/
if ( ! function_exists('ac_form_textarea')) {
function ac_form_textarea($data = '', $value = '', $extra = '', $uriOrData = '', $acOptions = array() ) {
$obj =& get_instance();
$obj->load->helper(array('form', 'url'));
$formattedAcOptions = _format_jquery_options($acOptions);
if (is_string($data)) {
$name = $data;
unset($data);
$data['name'] = $name;
$data['value'] = $value;
}
if ( is_array($data) && (! array_key_exists('id',$data) || $data['id'] === "") ) {
$data['id'] = 'ac_textarea';
}
$acJs = '';
if ( is_string($uriOrData) ) {
$url = $uriOrData . "/";
$acJs = <<<JS
[removed]
$(document).ready(function() {
$('#${data['id']}').autocomplete('${url}',{ ${formattedAcOptions} });
});
[removed]
JS;
} elseif ( is_array($uriOrData) ) {
$arrData = implode(' ',$uriOrData);
$acJs = <<<JS
[removed]
$(document).ready(function() {
var staticData = "${arrData}".split(" ");
$('#${data['id']}').autocomplete(staticData,{ ${formattedAcOptions} });
});
[removed]
JS;
}
$acTextarea = form_textarea($data, $value, $extra);
return ($acJs . $acTextarea);
}
}
/*
This function takes a PHP array and makes it into JS array of key : value pairs.
If passing in a javascript function as a value, make sure to enclose the function
in quotes.
*/
if ( ! function_exists('_format_jquery_options')) {
function _format_jquery_options($options = array()) {
$formattedOptions = "";
if (is_array($options) && count($options) > 0) {
foreach($options as $option=>$optval) {
if (is_bool($optval)) {
$formattedOptions .= ' '. $option .':'.($optval ? 'true':'false').',';
} elseif (is_int($optval)) {
$formattedOptions .= ' '. $option .':'.$optval.',';
} elseif (is_string($optval)) {
if (strstr($optval, "function")) {
$formattedOptions .= ' '.$option.':'.$optval.',';
} else {
$formattedOptions .= ' '.$option.':"'.$optval.'",';
}
}
}
$formattedOptions = substr($formattedOptions,0,-1);
}
return $formattedOptions;
}
}
?>
I welcome your comments.
Thanks, -Brodie