Creating ajax elements from inside ajax request

I’m pretty new to yii and the forums, so I appologize now if this topic has been covered somewhere (yes, I have spent quite a while searching and have not yet found anything) or if there’s a rediculously simple solution I’m just missing.

What I’m trying to do is create ajax links via an existing ajax link. I’ve tested the renderPartial() call (to the view rendering the created ajax links) directly from the page and it works fine. What I have a problem with is when I make an ajax call to a controller which loads data from model and calls the renderPartial view mentioned above to spit the ajax links out onto the page. I see the links on the page, but when I view source, they don’t exist (and neither does the yii-generated jquery ready() stuff for each of the dynamically created ajax links).

If I’m not being clear, please let me know. And if anyone has a solution out there, PLEASE enlighten me. This one’s buggin the crap out of me.

Thanks in advance.

1.Use renderPartial($view,$data,false,true) so the rendering result will be processed

2.Viewing the source after ajax calls won’t show the ajax resulting codeIf you have the firefox web developer toolbar use “View generated source” or check the respone in Firebug extension

Thank you for the surprisingly quick response! I’ll give that a shot tonight. And thanks for the tip about viewing ajax generated code.

Ok, went home and tried your suggestion, but it’s not yet working for me. I’ve included the code snippets I’m using below. What am I doing wrong?

Thanks again in advance for taking the time to look.

In finder.php (the main view page navigated to by user):


<table width="100%" cellspacing="5px">

    <tr>

        <td bgcolor="#FFFFFF" width="30%">

            <div id="data2">

            <?php

                $topGroups = Groups::model()->findAllByAttributes(array('ParentGroupID' => null));

                foreach($topGroups AS $model)

                {

                   echo CHtml::ajaxLink($model->Name,array('/Finder/CategoryClick','id'=>$model->GroupId,'container'=>'data2'),array('update'=>'#data1'),array('id'=>'lnk'.$model->GroupId));

                   echo '<br />';

                }

            ?>

            </div>

        </td>

        <td bgcolor="#FFFFFF" width="30%"><div id="data1">&nbsp;</div></td>

        <td bgcolor="#FFFFFF" width="30%"><div id="data0">&nbsp;</div></td>

    </tr>

</table>

In FinderController.php:


<?php

class FinderController extends CController

{

    public function actionCategoryClick()

        {


              $this->renderPartial('/site/AjaxTest',array('Groups' => Groups::model()->findAll("ParentGroupId = :selectedId",array("selectedId"=>$_GET['id']))),false,true);

        }

}

?>

In AjaxTest.php:


<?php

    foreach($Groups As $Group)

    {

        echo CHtml::ajaxLink($Group->Name,array('/Finder/CategoryClick','id'=>$Group->GroupId,'container'=>'data1'),array('update'=>'#data0'),array('id'=>'lnk'.$Group->GroupId));

        echo "<br />";


    }


?>

Do you get any error messages? Can you see the Ajax calls in Firebug? The SQL (CFileLogRoute, enableParamLogging)?

  • Try ‘/finder/CategoryClick’ (I’m not sure about case requirements since it’s been changed).

  • Is it intentionally you fetch the view from the site directory (’/site/AjaxTest’)?

  • There’s a potential name clash when you use the same prefix (‘id’=>‘lnk’.’…’)

  • Drawback: another jQuery.js is loaded on each Ajax return.

/Tommy

Tommy,

I’m going to have to apologize now and stick my foot in my mouth. Apparently the code i posted is working. I’m not sure if it was a caching issue or what, but I’m pretty sure I didn’t change anything and when I revisited the issue this afternoon, it works wonderfully.

On another note, thanks for the suggestions/questions. As to your (bulleted) questions:

[indent]1. No longer applicable since it’s working, but i’ll keep case sensitivity in mind for the future.

  1. As to the location of AjaxTest.php, yes it does live in the site directory so I was pointing to the right place. But if it’s a ‘best practices’ kind of thing you’re asking about, this is all just a proof of concept for me right now. If you have any yii best practices (ie. directory structure for one) suggestions or links, i’d love to hear/see them.

  2. The reason I coded the link names like that (‘lnk’.[id]) is because ‘id’ will always be unique from the db, so that should work out.

  3. I don’t understand how the javascript is loaded via the ajax. It actually creates a new .js file and loads the reference into the page each call? Can you ellaborate (or point me to an existing explanation) here?[/indent]

Again, thanks for the direction, and if you have any suggestions (or see any pitfalls, etc.) with my answers above, don’t hesitate to point them out. (My background is in .net (c# & vb) with n-tiered (layered) architecture, so I’m still trying to get my head around every aspect of the MVC concept and [obviously] Yii itself (esp. best practices).)

-Chase

Chase,

Since I’d seen Spyro’s tip before, and I didn’t understand why it works, I slightly modified your example to use two of my existing tables. That’s the reason I couldn’t safely use the same prefix. My bad, your solution should be fine.

By setting the fourth parameter to true in the renderPartial call, CController::processOutput gets called.

It’s obvious that jQuery.js is fetched on every Ajax return, I can se the GET request/response in Firebug and too be sure I verified the network traffic. (The original request generated 1k- of content, the reloaded js adds another 120k. In total some 130 tcp packets.) I don’t know if reloading the jQuery library is in fact the key to rearm the click events.

Example of the js that should be returned in any circumstance:




/*<![CDATA[*/

jQuery('#lnk1').click(function(){jQuery.ajax({'url':'/mysite/index.php?r=mycontroller/CategoryClick&id=1&container=data1','cache':false,'success':function(html){jQuery("#data0").html(html)}});return false;});



Without the fourth parameter set to true, obviously no such code is emitted. I guess next step would be to find out what happens if one can stop jQuery.js from being reloaded.

Edit:

What about the browser cache? Could it mitigate this problem? Why isn’t the js cached in the first place (I think I have default settings). Memory consumption may not be a problem provided that browser memory is freed on navigating off the page.

/Tommy

Tommy,

Thanks for the post above. You really opened my eyes to some of the stuff yii is actually doing with jquery. I also found something interesting while looking a little deeper. First, I did verify that it was loading an additional jquery.js each time the ajax link was clicked (adding (compounding) another 117k to the request each time). But then I removed the ‘false,true’ parameters from the RenderPartial call and watched the Get responses again. Without those two parameters, obviously the jquery (ajax) click function was not being generated, but it DID continue to load an additional jquery.js each time increasing the size of the request each click. I think this is going to be an issue for me because the page I’m looking to use this on is one that the user could be on for a while navigating in and out of the hierarchy tree of interest.

You mentioned browser cache being a possible solution. Can you elaborate? What can I try here to keep the jquery.js from being loaded each call?

-Chase

This seems to do the job without affecting $.ajax(…,‘cache’:false,…) for other content.

Add the following script to the controller actions involved.




  Yii::app()->clientscript->registerScript(

    'my_enable_default_caching_of_scripts',

    'jQuery.ajaxSetup({\'cache\': true})'

  );



I’ve verified that only one request for jquery.js is issued (to the server).

/Tommy

I registered your client script below in my controller action and it worked great! Response is now only increasing by less than 1K per call. Thanks for all your help and insight Tommy!

-Chase

Actually, there is way to avoid jQuery and other core scripts from loading when the fourth parameter of renderPartial() is set to true, meaning processOutput() will be called.

Thus, no need to change the ajax cache parameter:




Yii::app()->clientscript->scriptMap['*.js'] = false;

or more specific

Yii::app()->clientscript->scriptMap['jquery.js'] = false;



Learning every day ^_^

/Tommy

thank you, just what i was looking for :)