Yii 1.1: How to avoid multiple Ajax Request

26 followers

CHtml::ajaxLink(), CHtml::ajaxSubmitButton() and similar methods are great, but if they are located inside a portion of the page that is already loaded via ajax something bad will happen, something you may even not notice if you are not using tools like firebugs: the sent ajax requests will multiply themselves.

What's going on exactly?

Let's set up an example:
you have a page with a list of users. Every time you click on a username a div will be loaded via ajax, and this div will contain the user details plus another ajax link that will be used to display a floating div that will contain a form to send a message to the user.

The first time you load the user everything will be going just fine, and when you click on the "send a private message to the user" link just one request will be triggered.

If you load then another user (or the same user) when you click on the "send a message" link two request will be triggered, and then 3, and then 4 and so on.

What's causing the problem?

The ajax request are triggered based on the link ID. Every time the div is reloaded through ajax a new delegate/event will be loaded pointing at the same ID of the one before him. So when you actually click on the link both delegates will do their job, because both match the clicked link.

What can I do to avoid this problem?

Easy solution

The answer is extremely simple: assign to your ajax link a random ID.

You can achieve this result doing something like this:

CHtml::ajaxLink('send a message', '/message', 
    array('replace' => '#message-div'), 
    array('id' => 'send-link-'.uniqid())
);

Total 20 comments

#15756 report it
banago at 2013/12/13 12:03pm
setting the id in a ajaxlink

I'm using some bootstrap extension. As I was fighting this problem a long time I have come to the solution through some other post where was more clearly mentioned "id has to be in the htmlOptions array..."

  • The following submit button is in a ActiveForm in a view.
  • The view is loaded from a separate action into a dialog after clicking a button in CGridView with id "source-grid".
  • If the user clicks the button, the gridview gets updated and the dialog closed.
  • The problem was like mentioned in this wiki-post but unfortunately I didn't realize that the unique-id has to be in the htmlOptions array.

So this works:

$this->widget(
    'bootstrap.widgets.TbButton', array(
        'encodeLabel' => false,
        'buttonType' => 'submit',
        'type' => 'danger',
        'label' => 'Delete',
        'htmlOptions' => array(
            'style' => 'float:right;',
            'id' => uniqid(), //has to be in htmlOptions ...
            'ajax' => array(
               'type' => 'POST',
               'url' => Yii::app()->createUrl('controller/action', 
                                                array('id' => $model->id)),
               'success' => 'js:function(data, textStatus, XMLHttpRequest) {
                                   $.fn.yiiGridView.update("source-grid");
                                   $("#dialogDelete").dialog("close");
                                   }',
               'error' => 'js:function(XMLHttpRequest, textStatus, errorThrown) {
                                     alert(textStatus);
                                     }',
               ),
        ),
    )
);
#14695 report it
Bakhtiyor M. at 2013/09/04 12:23am
Thanks Gerhard

Thanks Gerhard, I have postponed the solution for the better days (cause I have a limit of time till the deadline). So I will try your solution later. For these days I hope nobody will use pagination :).

#14692 report it
Gerhard Liebenberg at 2013/09/03 07:03pm
CGridView in CJuiTabs ajaxTab

Hi Bakhtiyor M

This is what I ended up doing - for now: CGridView in frame in CJuiTabs

Regards Gerhard

#14675 report it
Rajith R at 2013/09/03 02:16am
@Bakhtiyor M.

dont say sorry!! i think , you will get more answers from forum, Thts y i suggest it.!!

#14670 report it
Bakhtiyor M. at 2013/09/02 11:59am
Sorry Rajith R

Excuse me Rajith R, if I break some rules here. The question was asked in the forum. You can find it by the link: Strange Situation In Using Cgridview Doubling delete confirmation But I did not get any useful answer there. Then I have found this documentation. And just in case of luck I have asked that question again here. Gerhard helped me out (even partly).

#14663 report it
Rajith R at 2013/09/02 06:15am
@Bakhtiyor M.

please explain your question with sample code .

please do it in a forum post, not here. you will get the answer.

#14662 report it
Bakhtiyor M. at 2013/09/02 06:05am
Sorry Rajith R , did not get you

Sorry Rajith R I did not get your question? What was requested from me?

#14660 report it
Rajith R at 2013/09/02 05:48am
@Bakhtiyor M.

Not clear!! can you please explain with forum post.

#14648 report it
Bakhtiyor M. at 2013/09/01 01:46am
Multiple Confirmation Dialog in CGridView (new problem)

Dear Gerhard, Your solution partly helped me out. The confirmation does not work just after you made some pagination. This is because every time uniqid() generates a new ID. I do not know how to pass the data->id to the html options to that CButtonColumn buttons. Have you any idea?

#14615 report it
Bakhtiyor M. at 2013/08/29 12:08am
Thanks Gerhard

Thank you Gerhard, your solution helped me. Really appreciate it.

#14612 report it
Gerhard Liebenberg at 2013/08/28 03:33pm
Multiple Confirmation Dialog in CGridView (and dropdownList)

Hi Bakhtiyor,

I have exactly the same situation as yourself. This worked for me:

CGridView uses 'class' - not 'id'

CGridView binds its script to its button's 'class' - not its 'id'.

(Note the 'options' of the button - not the 'htmloptions' of the buttoncolumn.)

array(
    'class'=>'bootstrap.widgets.TbButtonColumn',
    'htmlOptions' => array(
        'nowrap'=> 'nowrap'
    ),
    'header'=>'',
 
    'template'=>'{icdelete}',
 
    'buttons'=>array(
 
        'icdelete'=>array(
            'label'=> 'Delete',
            'icon' => false,
            'imageUrl'=>Yii::app()->baseUrl.'/images/css/gridview/delete.png',
 
            'url'=>'$this->grid->controller->createUrl("deletemm2", array(
                "parentId"  =>  '.$parentId.',
                "idValue"   =>  $data->primaryKey,
            ))',
 
            /* These options prevent the loading
            of additional scripts (for the 'click' event below)
            when the view is ajax injected. */
            'options'=>array(
                'live'  => false,
                'class' => uniqid(),
            ),
 
            'click'=>"function(){
                mm2_confirm_delete(
                    '#divToUpdate',
                    $(this).attr('href')
                );                                  
                return false;
            }",
        ),
    ),
),

I also experienced the same problems with dropdownList, but here we are back to using the 'id' - not the 'class':

echo CHtml::dropDownList('myDrop1', '', $addListData,
    array(
        'class'     =>  'singleline actiondropdown',
        'size'      =>  '1',
        'prompt'    =>  'Add a new Level',
 
        'live'      =>  false,
        'id'        =>  uniqid(),
 
        'ajax'      => array(
            'type'      =>  'GET',
            'url'       =>  $this->createUrl('createchild'),
            'data'      =>  array(
                'parentId'  =>  $parentId,                                  
                'dropdownId'=> 'js:this.value',
            ),
 
            'beforeSend' => 'function(){
                loadingGif_in();
            }', 
 
            'success'   =>  'function(response){
                $("#divToUpdate").html(response);
                loadingGif_out();
            }',
 
            'error' =>  'function(jqXHR, status, error){
                alert (error + ": " + jqXHR.responseText);
                loadingGif_out();
            }',
        ),
    )
);

Alternative solution for dropdownList

Do not use the dropdownList's built-in ajax, but use its 'onchange' event to call your own js function containing the ajax.

$url = $this->createUrl('createchild');
 
echo CHtml::dropDownList('myDrop1', '', $addListData,
    array(
        'class'     =>  'singleline',
        'size'      =>  '1',
        'prompt'    =>  'Add a new Level',
        'onchange'  =>  "dropdownlistAjax(
            '".$url."',
            '".$parentId."',
            this.value,
            '#divToUpdate');
        ",
    )
);
function dropdownlistAjax($url, $parentId, $dropdownId, $divToUpdate)
{   
    try{
        var request = $.ajax({
          url       : $url,
          data      : {parentId: $parentId, dropdownId: $dropdownId},
          type      : "GET",
          cache     : false,
          dataType  : "html"
        });
 
        request.done(function(response){ 
            //update the view with the data received from the server
            $($divToUpdate).html(response);
            request = null;
        });
 
        request.error(function(jqXHR, status, error){
            alert (error + ": " + jqXHR.responseText);
            request = null;
        });
 
    }
    catch(error){
        alert(error.message + '. ' + 'Please contact us.');
        request = null;
        return false;
    }
}
#14406 report it
Bakhtiyor M. at 2013/08/09 03:44am
Multiple Confirmation Dialog in CGridView

May be this is not so correct place to ask, but in the forum I could not find the answer and no body give me a solution.

I have a <div> section. With ajax link I load into this <div> my first CGridView (A). On selection change in the (A) a load to the same <div> the second CGridView (B). With the ajax link in this page I can return to the (A) again. Last column of the (A) contains CButtonColumn, by which I can delete the row of the (A). Before deleting it shows a confirmation dialog. After traversing between CGridViews (A and B) if I delete a row in the (A) it shows multiple time (as much as I traversed between A and B) a confirmation dialog asking weather do I like to delete the row. Why this is so? I also added to the delete button 'id'=>'deleteBtn'.uniqid(), but this is not helped?

#13324 report it
marcovtwout at 2013/05/22 10:51am
Re: Workaround

In my case, I constantly update the content of a CJuiDialog through ajax.

Assigning a uniqueID is a quick fix, but not using the live handler (which binds the event to the document) seems to be the proper solution: ('htmlOptions' => array('live'=>false)).

EDIT: assigning a uniqueID is required too:

('htmlOptions' => array('live'=>false, 'id'=>uniqid('ytajax'))).
#10531 report it
firefly at 2012/11/02 10:27am
my conclusion

ok, here is what I found out and how this post helped me:

when using CHtml::ajaxSubmitButton or CHtml::AjaxLink 1 all the time CHtml::ajaxSubmitButton and CHtml::AjaxLink should be used with 'live'=>false and should have an unique id:

array('id'=>'uniqueId', 'live'=>false)

2 if it is a normal request then it should look like this:

$this->renderPartial('_view', array(
                   'model'=>  $model,
               ), false, false);

3 if it is an AJAX request, then if should look like:

$this->renderPartial('_view', array(
                   'model'=>  $model,
               ), false, true);

All this result in the following controller action code:

public function actionActionName($id, $processOutput = false){
        if (Yii::app()->request->isAjaxRequest) {
            $processOutput = true;
        }
 
        ...
 
        $this->renderPartial('_view', array(
                   'model'=>  $model,
               ), false, $processOutput);

In this way all the time when it's an AJAX request, the javaScript code is rebind to the element.

hope that this helps someone...

#10002 report it
Rajith R at 2012/09/28 12:49am
Found solution

http://www.yiiframework.com/forum/index.php/topic/35912-chtmlajaxlink-with-yiiapp-request-geturl/page__st__20

http://www.yiiframework.com/forum/index.php/topic/35912-chtmlajaxlink-with-yiiapp-request-geturl/page__st__20

http://www.yiiframework.com/forum/index.php/topic/35912-chtmlajaxlink-with-yiiapp-request-geturl/page__st__20
#9977 report it
Rajith R at 2012/09/26 07:14am
when we click on ajaxlink the previous ajaxlink is working

please look at the problem

the forum post http://www.yiiframework.com/forum/index.php/topic/35912-chtmlajaxlink-with-yiiapp-request-geturl/page__p__172871#entry172871

http://www.yiiframework.com/forum/index.php/topic/35912-chtmlajaxlink-with-yiiapp-request-geturl/page__p__172871#entry172871

this is related to this topic

if(isset($_REQUEST['name']) and $_REQUEST['name']!=NULL)
{
echo CHtml::ajaxLink("name", Yii::app()->createUrl('/site/manage' ), array('type' =>'GET','data' =>array( 'name' => $name,'admissionnumber'=>'','Students[batch_id]'=>$bat ),'dataType' => 'text',  'update' =>'#student_panel_handler',array()));
 
}
 
if(isset($_REQUEST['admissionnumber']) and $_REQUEST['admissionnumber']!=NULL)
{
echo CHtml::ajaxLink("add.No", Yii::app()->createUrl('/site/manage' ), array('type' =>'GET','data' =>array( 'name' => $name,'admissionnumber'=>'','Students[batch_id]'=>$bat ),'dataType' => 'text',  'update' =>'#student_panel_handler',array()));
 
}
 
if(isset($_REQUEST['Students']['batch_id']) and $_REQUEST['Students']['batch_id']!=NULL)
{
echo CHtml::ajaxLink("Batch", Yii::app()->createUrl('/site/manage' ), array('type' =>'GET','data' =>array( 'name' => $name,'admissionnumber'=>'','Students[batch_id]'=>$bat ),'dataType' => 'text',  'update' =>'#student_panel_handler',array()));
 
}
 
 
 
 
 
 
<?php echo CHtml::ajaxLink('A', Yii::app()->createUrl('/site/manage' ),array('data' =>array('name' =>$name,'admissionnumber'=>$ad,'Students[batch_id]'=>$bat, 'val' => 'A'),'dataType' => 'text','update'=>'#student_panel_handler'),array()); 
 
<?php echo CHtml::ajaxLink('B', Yii::app()->createUrl('/site/manage' ),array('data' =>array('name' =>$name,'admissionnumber'=>$ad,'Students[batch_id]'=>$bat, 'val' => 'B'),'dataType' => 'text','update'=>'#student_panel_handler'),array()); 
 
<?php echo CHtml::ajaxLink('C', Yii::app()->createUrl('/site/manage' ),array('data' =>array('name' =>$name,'admissionnumber'=>$ad,'Students[batch_id]'=>$bat, 'val' => 'C'),'dataType' => 'text','update'=>'#student_panel_handler'),array()); 
 
<?php echo CHtml::ajaxLink('D', Yii::app()->createUrl('/site/manage' ),array('data' =>array('name' =>$name,'admissionnumber'=>$ad,'Students[batch_id]'=>$bat, 'val' => 'C'),'dataType' => 'text','update'=>'#student_panel_handler'),array());

the first 3 links are active only after some submit. after that when we click on that "name" link the link "A" with previous values are working..

like that when we click on that "add.No" link the link "B" with previous values are working..

when we click on that "Batch" link the link "C" with previous values are working..

this is the problem. the link shown is correct but working is previous one.

#9085 report it
Pete H at 2012/07/18 07:44am
A different solution

I have been given the run around by this problem for 2 days... I had multiple forms being renderPartial'd over and over via ajax calls.

I have found the following neat Yii based solution which seems to work for me - as ever you mileage may vary.

So you have a _form being rendered from a view:

<?php echo $this->renderPartial('_form', array('model'=>$model)); ?>

In the _form partial view you have and ajaxlink/submit button etc

echo CHtml::ajaxLink(
'Or create new',
'/index.php?r=customer/create',
array(
'success'=>'function(data){
    $("#popup").show("slow");
    $("#popup").html(data);
}'
),
array("id"=>"createnewcustomer", "live"=>false)
); ?>

The key to not have the element be bound multiple times to the js is to put "live"=>false as one of the HTML options: from: http://www.yiiframework.com/doc/api/1.1/CHtml#clientChange-detail

live: boolean, whether the event handler should be attached with live/delegate or direct style. If not set, liveEvents will be used. This option has been available since version 1.1.6.

direct style seems to attach to this element and this element only, and once. Add this to all your ajaxLink ajaxSubmitButtons etc and the problem may go away for you

HTH someone.. nearly killed me !

#8993 report it
chayo at 2012/07/11 05:21pm
avoid multiple ajax request on CHtml

to avoid ajax request on CHtml, just use CHtml::$liveEvents=false; or CHtml::ajaxLink('label','url',array(),array('live'=>false)); i usually place this on beforeAction($action) controller method; on controller:

public function beforeAction($action){

if(Yii::app()->request->isAjaxRequest) CHtml::$liveEvents=false; parent::beforeAction($action); }

edited: not work on CHtml::ajaxLink method but with CHtml::$liveEvents=false; you can use Yii::app()->clientScript->('unbindAjax','$("#ajaxLinkId").unbind("click")'); for every ajax request that have CHtml::ajaxLink to avoid multiple ajax request.

#8668 report it
rix.rix. at 2012/06/18 11:23am
Just had the same problem

thanks for starting this post.

This is what worked for me:

In the htmlOptions array of your ajaxLink, give the link an id of uniqid()

array('id'=>uniqid(),'class'=>'ajax')

Then possibly in your controller you'll need to disable unnecessary loading of .js files. Check the net tab in firebug to make sure your app is not requesting the same .js files everytime you do an ajax post...

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

#4207 report it
Joblo at 2011/06/15 07:39pm
Other workaround

In my opinion, the problem is the clientChange function of CHtml. Why to register a script and not output the ajax stuff directly if used in a ajax area?

I have detected this article after the release of the extension ajaxutil

It should be easy to add a option 'directOut' to the ajaxOptions of CHtml::ajaxXX elements in the Yii core - see me feature request ajax-handling

Besides: The extension handles the problems with pagers, when loaded inside an ajax area too.

Leave a comment

Please to leave your comment.