在CakePHP中为每个视图添加特定于页面的Javascript

12

为了保持我的脚本易于维护,我将把它们移动到各自的文件中,并按控制器和操作进行组织:

// scripts which only apply to /views/posts/add.ctp
/app/webroot/js/page/posts/add.js

// scripts which only apply to /view/users/index.ctp
/app/webroot/js/page/users/index.js

虽然这样很酷,但我希望这些内容能够由控制器自动添加,因为它显然知道控制器和操作的名称。

我认为最好的地方是在AppController::beforeRender()中添加。(对吗?)

唯一的问题是我不知道如何将此添加到$scripts_for_layout变量中。我认为获取javascript助手对象的引用会起作用,但我无法从控制器中找到它!

class AppController extends Controller {
    var $helpers = array("javascript", "html", "form");

    function beforeRender() {
        // ???
    }
}
9个回答

28

在您的default.ctp布局文件中非常容易实现:

一个自动包含控制器和/或控制器操作的.css文件的示例(因为我有这个文件,可以轻松适应.js文件):

<head>
...
<?php
    if (is_file(WWW_ROOT . 'css' . DS . $this->params['controller'] . '.css')) {
        echo $html->css($this->params['controller']);
    }
    if (is_file(WWW_ROOT . 'css' . DS . $this->params['controller'] . DS . $this->params['action'] . '.css')) {
        echo $html->css($this->params['controller'] . '/' . $this->params['action']);
    }
?>
...
</head>

3
赞同简洁的方法和理解。APP.WEBROOT_DIR.DS可以用WWW_ROOT常量替换。 - deizel.
好的,这样在更改目录结构时应该会更少出错。已经更改了。 :) - deceze
1
deceze... 真的... 你就是 Stack Overflow 上唯一的 Cake 大师,我发誓。在过去的三个星期里,我自己就给了你大约 400 的声望分。 :) - nickf
1
这里有一些小问题 - 文件的包含取决于布局中的逻辑代码。更改布局,代码就会出错。此外,“is_file”在每个请求中都会被调用,为仅有的几个操作/视图添加了一些轻微的开销。我认为它应该在组件/助手级别处理。 - Vanja D.

15

就像deceze所说的那样,我们使用布局(layout)来实现它,虽然我认为我们的解决方案更加优雅 :)

在default.ctp中:

if(isset($cssIncludes)){
    foreach($cssIncludes as $css){
        echo $html->css($css);
    }
}

if(isset($jsIncludes)){
    foreach($jsIncludes as $js){
        echo $javascript->link($js);
    }
}

接着,在我们的控制器操作中,我们定义这些数组:

$this->set('cssIncludes',array('special')); // this will link to /css/special.css
$this->set('jsIncludes',array('jquery'));   // this will link to /js/jquery.js

对于每个视图都需要加载的文件,我们只需在布局顶部“静态”地添加相同类型的链接,例如:

echo $javascript->link('global');
echo $html->css('global');

这对我们来说非常有效。祝你好运!


谢谢,我一直在寻找在控制器中定义脚本的方法。虽然我不认为在.ctp文件中调用PHP代码是优雅的,但肯定有更简洁的方式。 - dewwwald

2

我对这个还比较新,但是在阅读了这篇文章之后,我在我的布局文件中添加了以下内容:

if ($handle = opendir(WWW_ROOT . 'js' . DS . $this->params['controller'] . DS . $this->params['action'])) 
{
    while (false !== ($entry = readdir($handle)))
        {
           $entry = str_replace(".js", "", $entry);
           echo $this->Html->script($entry);

    }
    closedir($handle);
}

非常好的代码片段!唯一的不好之处是您不能在两个不同的控制器/操作中使用相同的JS... - Fernando Rybka

2

我之前需要对特定页面进行包含,但在文档中发现了一种更简洁的方法。你可以将它加载到默认.ctp文件中的某个脚本块中。然后在相应的视图中,只需使用HTML助手来推送一个脚本:

You can append the script tag to a specific block using the block option:

echo $this->Html->script('wysiwyg', array('block' => 'scriptBottom'));

这个功能会向一个块中添加 <script type="text/javascript" href="/js/wysiwyg.js"></script>

In your layout you can output all the script tags added to ‘scriptBottom’:

echo $this->fetch('scriptBottom');

1
我能想到的最好的方法是创建自己的自定义AppView,并让所有控制器使用它:
class myController extends AppController {
  var view = 'AppView';
  ...
}

然后,在您的AppView中的某个地方,您会想要执行类似以下操作:

function __construct(&$controller, $register){
  parent::__construct($controller,$register);
  $this->addScript('<script type="text/javascript" src="/path/to/js/' . $this->controller . '/' . $this->action . '.js"></script>');
}

但是我建议先退一步,考虑一些事情。

你的脚本平均有多大?在客户端缓存脚本之前,使用外部脚本调用的开销是否比将脚本直接插入页面(内联)增加几百字节到主输出流中更好?

也许你最好在中间找到一个平衡点——按控制器拆分脚本,而不是按操作拆分。这样,在访问任何操作后的第一次访问后,客户端就拥有了所有操作的所有脚本。这样,你避免了为应用程序的所有脚本进行大量初始下载,但也避免了添加N个HTTP往返(其中N是全新用户访问的操作数)。

解决问题的另一种方法是全部使用JavaScript。只需想出一种懒加载方案即可。因此,你的应用程序只需加载一个非常小的loader.js,该脚本会确定要拉取哪些其他JavaScript源。

注意:我从未测试过我的扩展视图hack,但如果你真的想这样做,我敢打赌它会起作用。


0
使用Cake v3,您可以像这样操作。在特定的控制器中添加您想要的脚本。
//YourController.php
public function beforeRender (Event $event)
{
    array_push($this->scripts, 'Admin/gallery');
    parent::beforeRender($event);
}

在初始化时设置默认值并渲染一次 //AppController.php public function initialize() { parent::initialize();

    $this->scripts = [];
}

public function beforeRender (Event $event)
{
    /* Define scripts in beforeRender of Child */
    $this->set('scripts', $this->scripts);
    /*-----------------------------------------*/
}

现在你可以运行这个函数一次。

<?= $this->Html->script($scripts) ?>

0

我对 W1ckd 的代码片段进行了一点点(非常少量)的修改,使得可以更轻松地共享相同的 JavaScript 代码以执行不同的操作:

if ( is_dir(WWW_ROOT . 'js' . DS . $this->params['controller'] ) && ( $handle = opendir( WWW_ROOT . 'js' . DS . $this->params['controller'] ) ) )
{
    while ( false !== ( $entry = readdir( $handle ) ) )
    {
        if ( in_array( $this->params['action'], explode( '.', $entry ) ) ) {
            $entry = str_replace( ".js", "", $entry );
            echo $this->Html->script( $this->params['controller'].DS.$entry );
        }

    }
    closedir( $handle );
}

这样你就可以拥有类似于:

webroot/js/controller/view.edit.add.js

而且这个js将会包含在这三个操作中(查看,编辑和添加)。


0

关于CakePHP的博客文章约定优于配置-有什么大不了的?中有一些关键细节 - 它使用一个帮助程序来指定Javascript文件:

<?php

class JsManagerHelper extends AppHelper {

    var $helpers = array('Javascript');

    //where's our jQuery path relevant to CakePHP's JS dir?
    var $_jqueryPath = 'jquery';

    //where do we keep our page/view relevant scripts?
    var $_pageScriptPath = 'page_specific';

    function myJs() {
    return $this->Javascript->link($this->_jqueryPath . '/' .
                                    $this->_pageScriptPath .'/' .
                                    $this->params['controller'] . '_' .
                                    $this->params['action'], false);
    }

}

?>

然后您只需在视图中添加$jsManager->myJs();


0

我已经为这个确切的问题编写了一个插件。

如果你有两个文件:

  • /app/View/Post/add.ctp
  • /app/View/Post/add.js

它将把add.js包含在页面的脚本块中。

或者

  • app/View/Post/add.js
  • app/webroot/js/post/add.js

它将在页面的脚本块中包含/js/post/add.js。

里面有一些可爱的功能,而且非常简单。您甚至可以在.js文件中使用PHP并使用在操作中设置的viewVars。最重要的是,您不需要破解视图或布局文件才能使其工作,只需获取布局中的“script”块即可。您还可以将.js简单地编写到另一个视图块中。

您可以在此处查看:https://github.com/dizyart/cakephp-viewautoload

它还处于早期阶段,因此请务必发表评论和愿望清单!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接