Fully ajax website with Yii – Part 2

In previous post I described how to create simple full ajax web application without changing basically logic of the application. All changes was made to the layout, view and to the base classes.
But as you maybe already noticed not everything works well. For example if you tried to sign-in -menu won’t be updated. In this short article I will show how to fix such kind of problems.
All data which ouputs for ajax request collecting with help of flash messages (built-in Yii functionality);
So basically if you wand update something on your page you have to set flash message with name updatedata wich contains an array with key value elements

Example   protected/controllers/SiteController.php ;  actionLoginMethod

 
                ...
                // collect user input data
		if(isset($_POST['LoginForm']))
		{
			$model->attributes=$_POST['LoginForm'];
			// validate user input and redirect to the previous page if valid
                        if($model->validate() && $model->login()) {
                               Yii::app()->user->setFlash('updatedata', array(
 						'#mainmenu'=>$this->renderPartial('/includes/menu',array(),true),));                   
                               $this->redirect(Yii::app()->homeUrl);
                        }
                ...

As you can see I put all mainmenu to separate file. Here is it’s content

protected/views/includes/menu.php

	widget('zii.widgets.CMenu',array(
			'items'=>array(
				array('label'=>'Home', 'url'=>array('/site/index')),
				array('label'=>'About', 'url'=>array('/site/page', 'view'=>'about')),
				array('label'=>'Contact', 'url'=>array('/site/contact')),
				array('label'=>'Login', 'url'=>array('/site/login'), 'visible'=>Yii::app()->user->isGuest),
				array('label'=>'Logout ('.Yii::app()->user->name.')', 'url'=>array('/site/logout'), 'visible'=>!Yii::app()->user->isGuest)
			),
		)); ?>

For avoiding dublicating data I cut this code from main layout and put there renderPartial instead.

protected/views/layout/main.php

...

...

So all blocks that might be updated via ajax better to move to includes folder. Now after sign -in main menu is changing.

Now we have to add the same code to sign-out method

protected/controllers/SiteController.php

...
public function actionLogout()
	{
		Yii::app()->user->logout();
		Yii::app()->user->setFlash('updatedata', array(
 						'#mainmenu'=>$this->renderPartial('/includes/menu',array(),true),));
		
		$this->redirect(Yii::app()->homeUrl);
	}
...

In that way you can update everyrhing you want on the webpage. Good luck!
P.S. Suggestions are welcome!

Full ajax Yii webapp demo

Fully ajax website with Yii – Part 1

This article will be suitable for who made fully ajax site like  Gmail with best PHP Framework – Yii.
Main requirements are:

  1. All pages should be loaded via ajax.
  2. All forms should be submitted via ajax.
  3. Pages should be reachable by search engines.
  4. History support.
  5. Window title support
  6. Minimal additional code.

Here  is my solution.
I get for example simple web application which you can made with help of yiic utility.

cd /home/www/mywebsite
wget http://yii.googlecode.com/files/yii-1.1.7.r3135.tar.gz
gunzip yii-1.1.7.r3135.tar.gz
tar -xvf yii-1.1.7.r3135.tar
mv yii-1.1.7.r3135/framework/ ./
php framework/yiic.php webapp ./

That is it your application has been created!

Add this code to the protected/views/layout/main.php file before  </head> tag.


getClientScript();
  		$cs->registerCoreScript('jquery');
  		$cs->registerCoreScript('bbq');
?>
  	
  	registerScript('applychanges', $script, CClientScript::POS_HEAD);
?>	

  	registerScript('loading-indicator', $script, CClientScript::POS_READY);
?>



registerScript('ajaxlinks-and-forms', $script, CClientScript::POS_READY);
?>

Next code should be added after <body> tag

We’ve jus added javascript code which will handle all client side job
Then we extend Base controller class “Controller”.

protected/componens/Controller.php; Add new methods below:

public function ajaxRender($file, $data=array()) {
		
		$data['title'] = CHtml::encode($this->pageTitle);			
		header('Content-type: text/x-json');
		echo CJSON::encode($data);
		Yii::app()->end();

	}
		
	public function render($file, $params = array(), $data=array()) {
		
		if(Yii::app()->request->isAjaxRequest){

			if(Yii::app()->user->hasFlash('updatedata')) {
				$flashdata = Yii::app()->user->getFlash('updatedata');		
				$data = $data + $flashdata;	
			}
			
			
			$data['#content'] = parent::renderPartial($file, $params, true);
			$data['title'] = CHtml::encode($this->pageTitle);
			
			header('Content-type: text/x-json');
			echo CJSON::encode($data);
			Yii::app()->end();
			
		} else {			
			echo parent::render($file, $params, true);	
		}	
	
	}
	
	public function redirect($url) {
		
		if(is_array($url) && isset($url[0]))
			$url = $url[0];
	
		if(Yii::app()->request->isAjaxRequest){
			
			$data = array();
			
			if(Yii::app()->user->hasFlash('updatedata')) {
				$flashdata = Yii::app()->user->getFlash('updatedata');		
				$data = $data + $flashdata;	
			}
			
			$data['#content']= '';
			
			
			
			header('Content-type: text/x-json');
			echo CJSON::encode($data);
			Yii::app()->end();
				
		} else {
			parent::redirect($url);
		}
	}
	
	public function refresh() {
		if(Yii::app()->request->isAjaxRequest){

			$data = array();
			
			if(Yii::app()->user->hasFlash('updatedata')) {
				$flashdata = Yii::app()->user->getFlash('updatedata');		
				$data = $data + $flashdata;	
			}
			
			
			$data['#content']= '';
			
			header('Content-type: text/x-json');
			echo CJSON::encode($data);
			Yii::app()->end();
			
		} else {
			parent::refresh();
		}
	}

Then you have to override some Request methods to support CWebUser redirects:

Put content below to new file with name protected/components/EHttpRequest.php

request->isAjaxRequest){

			$data = array();
			
			if(Yii::app()->user->hasFlash('updatedata')) {
				$flashdata = Yii::app()->user->getFlash('updatedata');		
				$data = $data + $flashdata;	
			}
			
			$data['#content']= '';
			
			header('Content-type: text/x-json');
			echo CJSON::encode($data);
			
			Yii::app()->end();	
		} else {
			parent::redirect($url);
		}
	}
	
	public function refresh() {
		if(Yii::app()->request->isAjaxRequest){

			$data = array();
			
			if(Yii::app()->user->hasFlash('updatedata')) {
				$flashdata = Yii::app()->user->getFlash('updatedata');		
				$data = $data + $flashdata;	
			}
			
			
			$data['#content']= '';
			
			header('Content-type: text/x-json');
			echo CJSON::encode($data);
			
			
			Yii::app()->end();	
		} else {
			parent::refresh();
		}
	}
}

Set new class name for request component in your main config protected/config/main.php

It should looks like this

  .... 
  'components'=>array(
		'user'=>array(
			// enable cookie-based authentication
			'allowAutoLogin'=>true,
		),
		
		'request'=>array(
                     'class'=>'EHttpRequest',
               ),

   ....

That’s all. Now you have full ajax simple web application.
Offcourse it has some limitations and issues, further I’ll extend this article.

P.S. I have forgotten to say about styles.
Just add these lines to your css file.

#loading {
	position:fixed;
	padding:3px;
	background:#80B646;
	color:#fff;
	display:none;
	z-index:999;
	right:0;
	top:0;
}
#loading.activity {
	display:block;
}