Ми}{@лbI4

Блог хеллоуворлдщика

Дублирование запросов при использовании CJuiDialog

18.03.2014 yii, cjuidialog

Понадобилось сегодня сделать мне изменения поля в CGridView используя модальное окно. И вроде бы и всё хорошо, но при N-ом количестве раз открытия и закрытия окна CJuiDialog происходит дублирование запросов. Т.е. если я хочу обновить запись, то посылаю запрос update?id=3, и потом решаю обновить ещё одну, и посылаю запрос update?id=6. В итоге предыдущие запросы тоже выполняются ещё раз! Получается каша. Далее стало ясно, что если именовать кнопку submit как 'submit_' . rand(), то никакого дублирования не происходит.

Решение

Происходит кеширование событий для CHtml, т.к. ID (или name, смотря что используете) кнопки не изменяется от вызова до вызова модального окна, тем самым происходит множественные запросы. Лечить это можно по разному.

  1. Добавлять что-нибудь в ID или name кнопки, например функцию uniqid(), или id записи, или rand().
  2. Удалить обработчики событий кнопки до вызова окна используя .undelegate() или .off() или .unbind() или .die() (зависит от ситуации).
  3. Присвоить CHtml::$liveEvents = false; и вешать самому обработчик события.

Лично моё мнение использовать 1й вариант, т.к. во втором варианте есть вероятность что-нибудь зацепить нужное из обработчиков, а третий вариант требует писанины кода . Но! 3й вариант самый лучший.

Так же можно посмотреть как это реализуется в файле CHtml строка с 2378.


P.S. На закуску с дублированием.

Решение дублирования CSS и JS без строк типа

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

Читаем, если ссылка ещё робит: http://www.eirikhoem.net/blog/2011/08/29/yii-framework-preventing-duplicate-jscss-includes-for-ajax-requests/

Или тупо подгружаем данных код в header:

$.ajaxSetup({
    global: true,
    dataFilter: function(data, type) {
        //  only 'text' and 'html' dataType should be filtered
        if (type && type != "html" && type != "text")
        {
            return data;
        }

        var selector = 'script[src],link[rel="stylesheet"]';

        // get loaded scripts from DOM the first time we execute.
        if (!$._loadedScripts) {
            $._loadedScripts = {};
            $._dataHolder = $(document.createElement('div'));

            var loadedScripts = $(document).find(selector);

            //fetching scripts from the DOM
            for (var i = 0, len = loadedScripts.length; i < len; i++)
            {
                $._loadedScripts[loadedScripts[i].src] = 1;
            }
        }

        //$._dataHolder.html(data) does not work
        $._dataHolder[0].innerHTML = data;

        // iterate over new scripts and remove if source is already in DOM:
        var incomingScripts = $($._dataHolder).find(selector);
        for (var i = 0, len = incomingScripts.length; i < len; i++)
        {
            if ($._loadedScripts[incomingScripts[i].src])
            {
                $(incomingScripts[i]).remove();
            }
            else
            {
                $._loadedScripts[incomingScripts[i].src] = 1;
            }
        }

        return $._dataHolder[0].innerHTML;
    }
});