10

我正在创建一个使用 ajaxRequest 的站点,当我单击一个链接时,它将使用 ajaxRequest 加载。当我加载例如 user/login UserController actionLogin时,我会使用 processOUTput 为 true 渲染视图,因此将生成该视图中所需的 js,但是如果我在该视图中有 clientScriptRegister 和事件,我该如何避免生成 scriptRegistered 两次还是多个取决于 ajaxRequest?我试图Yii::app()->clientScript->isSCriptRegistered('scriptId')检查脚本是否已经注册,但似乎如果你使用 ajaxRequest,结果总是错误的,因为它只有在渲染完成后才会为真。

控制器代码

if (Yii::app()->request->isAjaxRequest)
{
   $this->renderPartial('view',array('model'=>$model),false,true);
}

查看代码

if (!Yii::app()->clientScript->isScriptregistered("view-script"))
   Yii::app()->clientScript->registerScript("view-script","
      $('.link').live('click',function(){
         alert('test');
     })
");

如果我第一次请求控制器,它可以正常工作(警报 1 次)但是如果我再次请求同一个控制器而不刷新我的页面并且只使用 ajaxRequest,如果单击它,警报将输出两次(因为它保持在生成时,即使您已经注册过一次)

如果您在具有 jquery 功能的视图内有CActiveForm ,这也是一样的。每次您 renderPartial 时都会调用 corescript yiiactiveform。

4

3 回答 3

24

避免两次包含核心脚本

如果您的脚本已经通过较早的请求包含在内,请使用它来避免再次包含它们:

// For jQuery core, Yii switches between the human-readable and minified
// versions based on DEBUG status; so make sure to catch both of them
Yii::app()->clientScript->scriptMap['jquery.js'] = false;
Yii::app()->clientScript->scriptMap['jquery.min.js'] = false;

如果您有既要独立呈现又作为 HTML 片段包含在 AJAX 中的视图,您可以将其包装在内部if (Yii::app()->request->isAjaxRequest)以覆盖所有基础。

避免两次包含 jQuery 脚本(JS 解决方案)

还有可能防止脚本在客户端被包含两次。这不是直接支持的,而且稍微麻烦一些,但实际上它可以正常工作,并且不需要您在服务器端知道客户端发生了什么(即已经包含了哪些脚本)。

<script>这个想法是从服务器获取 HTML,然后用正则表达式替换简单地去除标签。重要的一点是您可以检测是否已经加载了 jQuery 核心脚本和插件(因为它们$在其上创建或属性)并有条件地执行此操作:

function stripExistingScripts(html) {
    var map = {
        "jquery.js": "$",
        "jquery.min.js": "$",
        "jquery-ui.min.js": "$.ui",
        "jquery.yiiactiveform.js": "$.fn.yiiactiveform",
        "jquery.yiigridview.js": "$.fn.yiiGridView",
        "jquery.ba-bbq.js": "$.bbq"
    };

    for (var scriptName in map) {
        var target = map[scriptName];
        if (isDefined(target)) {
            var regexp = new RegExp('<script.*src=".*' +
                                    scriptName.replace('.', '\\.') +
                                    '".*</script>', 'i');
            html = html.replace(regexp, '');
        }
    }

    return html;
}

如果已包含相应的脚本,则将定义文件名和对象的映射;通过此函数传递传入的 HTML,它将检查并删除<script>与先前加载的脚本相对应的标签。

辅助函数isDefined是这样的:

function isDefined(path) {
    var target = window;
    var parts = path.split('.');

    while(parts.length) {
        var branch = parts.shift();
        if (typeof target[branch] === 'undefined') {
            return false;
        }

        target = target[branch];
    }

    return true;
}

避免两次附加事件处理程序

您可以简单地使用 Javascript 对象来记住您是否已经附加了处理程序;如果是,请不要再次附加它。例如(查看代码):

Yii::app()->clientScript->registerScript("view-script","
  window.myCustomState = window.myCustomState || {}; // initialize if not exists
  if (!window.myCustomState.liveClickHandlerAttached) {
    window.myCustomState.liveClickHandlerAttached = true;
    $('.link').live('click',function(){
       alert('test');
    })
  }
");
于 2012-04-17T09:23:43.423 回答
6

最干净的方法是重写 beforeAction(),以避免任何重复的核心脚本:

class Controller extends CController {

  protected function beforeAction($action) {
        if( Yii::app()->request->isAjaxRequest ) {
            Yii::app()->clientScript->scriptMap['jquery.js'] = false;
            Yii::app()->clientScript->scriptMap['jquery-2.0.0.js'] = false;
            Yii::app()->clientScript->scriptMap['anything.js'] = false;
        }

        return parent::beforeAction($action);
  }

}

请注意,您必须输入准确的 js 文件名,不包括路径。

于 2013-06-11T20:10:06.563 回答
4

为了避免两次包含脚本文件,试试这个扩展:http ://www.yiiframework.com/extension/nlsclientscript/

为避免两次附加事件处理程序,请参阅 Jons 回答:https ://stackoverflow.com/a/10188538/729324

于 2013-01-31T09:22:19.993 回答