ajax+clientScript

Hi all!!

I’d like to discuss a common problem that many time I found in topics.

The problem happens everytime that we have to refresh a content with clientscript in an ajax request.

The two instriment that the framework actually provide are:


$this->renderPartial('view', $params)

and


$this->renderPartial('view', $params, false, true)

The first don’t works, because all clientScript inside the refreshed content will not work, and the latter don’t work, because all client script outside the refreshed div will not work.

By the way, calling the latter make so that ajax request start a strange recursion, the first time 2 request will be send instead of 1, the second 4, then 8 - 16 and so on.

The common workaround (and what I have always done too) was to avoid to use clientscript for this widget, and that means avoiding even to use CJuiWidget, because the register script file, css file and the script itself in the clientscript.

The workaround concept itself is not bad: the main point is that if we have to render a subview, this subview should be complete and contain all the necessary script for work on his own.

Starting from this idea I implemented this patch for the bug: 768

ZController.php

This file extends CController (so you can put in components and edit /components/Contoller.php for extend it).

This extended class provide a method "renderPartialWithHisOwnClientScript", that will render the clientScript of the subView (and ONLY the clientScript of the subView) at the bottom of the subview itself.

It allows you to work normally, without workaround and without hand-creating CJuiWidget, just remember to call this new renderparital and in the normal renderPartial and in the ajaxRequest function.

NOTE: there is still a problem with id, the components with clientScript inside the content to ajax-refresh should still have a unique id in order to not confilct with external components with the same id.

This cannot be fixed with an extension because CHtml::ID_PREFIX is a const and cannot (for now) be edited.

For YiiDevelopers: this file is a small edit in CControler and CClientScript, please check if it break something and include it in the official release. Let’s have a talk about how to fix even the id-conflict problem.

I feel that this detail is the last one Yii’s week point in client side managment, let’s fix it!

Will this work with ajaxlink and ajaxbutton??

So scripts will be inserted, parsed and reloaded every ajax request?

@koz

It works with ajax-everithing and submit-ddl and whatever. Just pay attention to give a unique id to the components to refresh in order do avoid confision with component outside.

@samdark

Yes, at every ajax request it regenerate all the scripts.

This is necessary for adding components in ajax request.

Is nothing of special, just few line of code. I guess that you can do much better than me!

zaccaria i can’t put this working… on my FileController.php i got your code:




class ZController extends CController

{

	

	public function renderPartialWithHisOwnClientScript($view,$data=null, $return=false)

	{

		

		$mainClientScript=Yii::app()->clientScript;

		Yii::app()->setComponent('clientScript', new ZClientScript);

		$output=$this->renderPartial($view, $data,  true);

		$output.=Yii::app()->clientScript->renderOnRequest();

		Yii::app()->setComponent('clientScript', $mainClientScript);


		if ($return)

			return $output;

		else

			echo $output;

	}


}

class ZClientScript extends CClientScript

{

	/**

	 * Inserts the scripts at the beginning of the body section.

	 * @param string the output to be inserted with scripts.

	 */

	public function renderOnRequest()

	{

		$html='';

		foreach($this->scriptFiles as $scriptFiles)

		{

			foreach($scriptFiles as $scriptFile)

				$html.=CHtml::scriptFile($scriptFile)."\n";

		}

		foreach($this->scripts as $script)

			$html.=CHtml::script(implode("\n",$script))."\n";


		if($html!=='')

			return $html;

	}


}



then i got

class FileController extends Controller

and on my actionUpdate i got:




		$dataProvider=new CActiveDataProvider('File');

		$this->renderPartialWithHisOwnClientScriptw('index',array(

			'dataProvider'=>$dataProvider,

			'id_project'=>$id_project,

		),false,true);



and it dont work What i got to do? thanks for your help

You miss a step in extension.

Put my code in a file named ZController (so components/ZController.php).

Then in file components/Controller.php write:




class Controller extends ZController

{



Like that should work.

P.S: no ‘false, true’ is needed now, those parameters will be ignored

This don’t work on my case, i got a list of items and two ajax links for each item, add link and delete link, when i click add link it show delete link and then when i click delete link it suposed to show add linkt then i click in the seond time it dont do nothing.

Any tip ? thanks

Can you post your code?

http://www.yiiframework.com/forum/index.php?/topic/10254-ajaxlinks-with-renderpartial/page__p__50394__fromsearch__1&#entry50394

This is my code but with your render

I solved this with a “minor” hack, I placed the JavaScript in the content to be replaced with AJAX without using CClientScript. This way the JavaScript will work even on the elements in the AJAX content. Kinda ugly but I couldn’t think of a better solution.

@chris83: my solutions does exactly what you said: it place the javascript needed by the content to be replaced in the content itself (I could find better words than your for explain it!!).

This topic wants to be a proposal for framework developer for include this possibiliy in the core framework.

I would be glad if you want to test my solution and give me some feedback!

Thanks a lot for answering!

Is this one still not in 1.1.3 version ?

Because I’m trying reorder my items list and everything is ok, but reloading CGridview js not reloading.

From changelog there are no news about that, and no staff member commented my proposal.

I will be pleased if You will workaround your problem by using my solution, the only weak point is that is necessary to give a unique id to all clientscripted items that will be ajax-replaced.

Let us know if it works!

Tnx for Your answer,

I’ll try Your workaround and give You feedback ;)

ok, I think I miss something…

My controller extends


class ItAdsFieldsController extends ZController

{....

And in my ajax controller I have:


public function actionPublish()

    {


        $curItem = ItAdsFields::model()->findByAttributes(array('field_id' => CHttpRequest::getParam('id')));


        if($curItem !== null)

        {

            d(CHttpRequest::getParam('id'));

            $curItem->publish = ($curItem->publish == '1') ? '0' : '1';

                    

            $curItem->update();


            unset($curItem);

        }


        $dataProvider=new CActiveDataProvider('ItAdsFields',array(

                'criteria' => array(

                    'order' => 'position ASC',

                ),

            )

        );


        $this->renderPartialWithHisOwnClientScriptw('admin',array(

                'dataProvider'=>$dataProvider,

                //'id_project'=>$id_project,

        ),false,true);




        return ;

    }

Zaccaria: at the end of code You can see client cript generation by Your example.

But for me it don’t work. I think I’m something missing.

One more thing, ajax regeneration must be on page change in CGridView, because when you go to the next page, the js script is still old, maden for first page.

Thanks for your answering.

Well, in this case I just 'll wait for yii fix of this problem. And know I just set AjaxUpdate parameter to false for pager and sorting working not within ajax but with GET property and the page refresh all js.

That’s simple workaraound :)

Good luck ;)

If you are using this method, be careful with the id of the actions (buttons) rendered. If you have the following rendered as renderPartial:

  echo CHtml::ajaxSubmitButton( Yii::t( 'button', 'Save'), Yii::app()->getRequest()->getUrl(), array( 'update' => '#a uniq id'), array('id' => $btn_id.'save' ) );

You need to add the a uniq id to the button else it’s going to take resources from your browser and crash your browser

$btn_id = time();

Yii does not take care of uniqueness in the page in in the partially rendered elements.

I haven’t looked at your code but I want to put a hint here: I have the same issue right now because I want to use Ajax + dialogs for CRUD operations in my CMS. I used renderPartial(…, false, true) too and stumbled over the problem that the jQuery.js was loaded with each Ajax call!

Where is the problem now? jQuery initializes each time and overwrites the existent version. Here an example: Fancybox extends $.fn so you can call methods via $.fn.fancybox.METHOD. When jQuery is loaded a second time this is not possible any more because $.fn.fancybox is undefined.

My solution was that I filter the data directly after the ajax call but before the success method. See dataFilter callback of $.ajax call.

g3ck0 is absolutely right. There’s a bug in CClientScript.

What does the processOutput parameter is to call the CClientScript->render() method in which there is a call to CClientScript->renderCoreScripts(). Here there is any kind of control over the script that are already loaded into the page. So jquery or any other core script can be loaded twice, and this makes developers go crazy. :)

g3ck0, could you post your code? how have you extended CClientScript? So we can find the best solution among all and then post the problem in the bug tracker.

Thanks!

Hi! zaccaria, could you explain how do you deal with css files? For example CDetailView requires own css files to be included, but it seems like ZController doesn’t include them in ajax response. Also suppose we have a page with jquery-ui tabs, where tab content is loaded via ajax and containts DatePicker widget. In this case jquery-ui.js is inlcluded twice: first - on page load for Tabs plugin and second - for DatePicker plugin in ajax response. What should we do with that?