In doing internet searches trying to learn how to incorporated ajax into web pages generated by the yii framework I noticed that there seems to be a lot of confusion. I worry that too-clever hacks will be broken in upgrades of the framework.
I am documenting the way I do it (if this is way off base then someone please give me a 'heads up'). Note I'm hard-coding the jQuery because my site is heavily javascript-based anyway.
The basic procedure is to (1) write the code that responds to (processes) the ajax call as an action in a controller and (2) call the action with the (yii-framework) appropriate url in the jQuery method. I'll call the controller AppController.php and the view will be under protected/views/app/index.php. Both are automagically created by gii.
The view (protected/views/app/index.php) is the part of the web app your users are interacting with on the page. As such, it needs to import the javascript files. This is done by 'registering' the scripts at the 'top' of the file. In our case, with scripts in a directory called 'js' just under the document root for the app, we have
<?php
/* @var $this AppController */
$this->breadcrumbs=array(
'App',
);
// Include the client scripts
$baseUrl = Yii::app()->baseUrl;
$cs = Yii::app()->getClientScript();
$cs->registerScriptFile($baseUrl.'/js/ajaxScript.js');
// more scripts would be added here by registering their filenames if needed
?>
(After this in the view file is the html business.)
The controller is /protected/controllers/AppController.php
In AppController.php we have an action method
public function actionAjaxProcessor(){
$a = $_POST['ajaxData'];
// process $a and get output $b
// output some JSON instead of the usual text/html
header('Content-Type: application/json; charset="UTF-8"');
echo CJSON::encode(array('output'=>$b));
}
Finally, ajaxScript.js has this for the ajax call:
[javascript]
ajaxCall = function(a){
jQuery.ajax({
// The url must be appropriate for your configuration;
// this works with the default config of 1.1.11
url: 'index.php?r=app/ajaxProcessor',
type: "POST",
data: {ajaxData: a},
error: function(xhr,tStatus,e){
if(!xhr){
alert(" We have an error ");
alert(tStatus+" "+e.message);
}else{
alert("else: "+e.message); // the great unknown
}
},
success: function(resp){
nextThingToDo(resp); // deal with data returned
}
});
};
The script will bind this function to a button click or other event, usually. See the jQuery website documentation (it is excellent) for more about this.
Note how we get to the controller action by using our usual controller/action url scheme in the ajax call.
This is all you need to do to modify the normal "php + ajax way" to the yii framework. I hope it saves some other newbies some time.
Why this won't (always) work ¶
The URL of the AJAX request is relative: "index.php?r=app/ajaxProcessor". It makes 2 assumptions:
- The page that initiates the call has an URL ending with "index.php" or its directory.
- Yii reads the route (controller and action) through the GET parameter "r".
With a recent version of Yii, both assumptions can be wrong depending on the configuration of the application.
The solutions ¶
You can't guess the webservice URL in pure Javascript! This URL will depend on the configuration of the application. So there are at least 2 solutions that mix in PHP and JS:
- Make the JS file a PHP template
- Provide JS variables from the main PHP view.
Updated
I updated the article. The minor change was adding a HTTP header for JSON instead of serving it as an HTML page. Then I appended an explanation of why this method won't work: it tries to guess the URL of the AJAX service from pure JS, while this URL depends on a PHP configuration. I gave hints on how to fix this.
Added comment to code to clarify
Hi François,
Thank you for your changes and comments. I added a comment to the code concerning the URL issue you brought up. You are correct that it shouldn't be taken as obvious that the URL used may vary with the configuration.
If you are saying, however, that with recent versions of yii this URL could change depending on how the page is loaded (or due to some other dynamic effect) then that is an entirely different story. In fact, that may make the framework a bad choice in general going forward because developers are starting to rely more on client-side processing.
So, for the record, and in order to make the wiki as clear and precise as possible, which version's default does the url I provided not work for? I started (perhaps a month ago) with version 1.1.11, and it works fine for that. If you could add to my comment where it breaks down that would be very helpful. Thank you.
urlFormat
See The Definitive Guide to Yii, URL Management for an explanation. The page almost begins with a warning:
But I was wrong about the default config: the config file that Yii generates by default does contain the lines
'components' => array( 'urlManager' => array( 'urlFormat' => 'path',
but they are commented by default.
URL might not be the issue but DATA is !
Lets say I have configured my URLmanager as
'urlManager'=>array( 'urlFormat'=>'path', 'showScriptName'=>false, ),
So that I can make a call to
http://myapp/app/ajaxProcessor
But if I use the below code
jaxCall = function(a){ jQuery.ajax({ // The url must be appropriate for your configuration; // this works with the default config of 1.1.11 url: 'http://myapp/app/ajaxProcessor', type: "POST", data: {ajaxData: a},
When the JS makes the AJAX POST request(or GET request) it will always make the request with a URL like :
http://myapp/app/ajaxProcessor?ajaxData=a
So if I have activated 'urlFormat'=>'path' then I will always get an AJAX error since I am not using
http://myapp/app/ajaxProcessor/ajaxData/a
To make that request.
How to make JS use the path urlFormat or what can be a solution?
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.