Symfony 2自定义表单字段类型:如何只添加一次javascript和css?

时间:2012-04-04 10:30:48

标签: forms symfony twig

我想在自定义Symfony 2表单字段类型扩展中使用javascript。所以,我有这样的Twig扩展模板:

{% block some_widget %}
    <input ... />

    <script src="some.js"></script>
    <link href="some.css" />
{% endblock %}

但是我想在我的HTML中只使用一次这些脚本和链接标记,理想情况是在head标记中,而不需要修改基本模板。我试图扩展Twig块,但我无法访问表单模板中的动作模板块。或者可能是这样的:

{# widget tempate #}
{% block some_widget %}
    <input ... />

    {{ use_javascript('some.js') }}
    {{ use_css('some.css') }}
{% endblock %}

{# main action template #}
...
<head>
{{ dump_javascripts() }}
{{ dump_css() }}
</head>
...

如何使用Symfony 2 Forms + Twig执行此操作?

P.S。抱歉我的英语不好。

5 个答案:

答案 0 :(得分:8)

我必须编写一个需要javascript的自包含表单窗口小部件,我能够通过event_dispatcher监听kernel.response来实现您尝试做的事情,以便在最后添加javascript Symfony\Component\HttpFoundation\Response。这是我的表单类型的片段:

<?php

namespace AcmeBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;

class AcmeFileType extends AbstractType{

  private $twig;
  private $dispatcher;

  public function __construct(\Twig_Environment $twig, EventDispatcherInterface $dispatcher){
    $this->twig = $twig;
    $this->dispatcher = $dispatcher;
  }

  public function buildView(FormView $view, FormInterface $form, array $options){

    $javascriptContent = $this->twig->render('AcmeBundle:Form:AcmeFileType.js.twig', array());

    $this->dispatcher->addListener('kernel.response', function($event) use ($javascriptContent) {

      $response = $event->getResponse();
      $content = $response->getContent();
      // finding position of </body> tag to add content before the end of the tag
      $pos = strripos($content, '</body>');
      $content = substr($content, 0, $pos).$javascriptContent.substr($content, $pos);

      $response->setContent($content);
      $event->setResponse($response);
    });

  }

  ... 

在services.yml中定义表单类型时,它看起来像这样:

acme.form.acme_file_type:
    class: AcmeBundle\Form\AcmeFileType
    arguments:
        - @twig
        - @event_dispatcher
    tags:
        - { name: form.type, alias: acmefile }

现在,每次使用acmefile构建表单时,javascript都会附加到<body>。此解决方案并不会阻止多次出现 ,但您应该可以轻松地根据自己的需要进行改进。

如果您愿意,也可以使用$response对象来修改标题

答案 1 :(得分:1)

最好的方法是使用css&amp;提供单独的模板。脚本加载。 随着readme中的注释,开发人员必须做的只是

{% block stylesheets %}
  {{ parent() }}

  include "@MyBestBundle/Resources/view/styles.html.twig"

{% endblock %}

或尝试使用DI拦截表单呈现并添加资产。但如果可能的话,实施起来会更加困难。

答案 2 :(得分:0)

这就是我使用它的方式。希望这是你正在寻找的。

base.html.twig

<head>
   {% block stylesheets %}
       css...
   {% endblock %}
</head>

foo.html.twig

{% extends '::base.html.twig' %}

{% block stylesheets %}
   {{ parent() }}

   css that you need in foo.html.twig

{% endblock %}

答案 3 :(得分:0)

我在其他情况下发现了许多人使用的“脏”方法。 我们检查客户端脚本的加载。如果我们有一个zlkladr.js文件,它有一个全局对象'zlkladr'

{% block our_widget %}
{% spaceless %}
  ...
  <script>
    // We must load the script only once, even if many widgets on form
    if ( !window.zlkladr ) {
      document.write('<script src="{{ asset('bundles/kladr/js/zlkladr.js') }}"></sc'+'ript>');
    }
  </script>
{% endspaceless %}
{% endblock %}

答案 4 :(得分:0)

我的工作方式是创建一个自定义的树枝扩展名,在其中将JS添加到缓冲区中并在表单呈现期间将其转储到布局的末尾。

类似这样的东西:

<?php

namespace AppBundle\Twig;

use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class WysiwygExtension extends AbstractExtension
{
    /**
     * @var array
     *
     * A pool of elements IDs for Wysiwyg binding.
     */
    private $wysiwygElements = [];

    /**
     * {@inheritdoc}
     */
    public function getFunctions()
    {
        return array(
            new TwigFunction('addWysiwygBinding', [$this, 'addWysiwygBinding']),
            new TwigFunction('popWysiwygBindings', [$this, 'popWysiwygBindings']),
        );
    }

    public function addWysiwygBinding(string $id): void
    {
        $this->wysiwyglements[] = $id;
    }

    public function popWysiwygBindings(): array
    {
        $elements = array_unique($this->wysiwygElements);

        $this->wysiwygElements = [];

        return $elements;
    }
}

然后form-fields.html.twig

...
{% block wysiwyg_widget %}
{% apply spaceless %}
    {{ form_widget(form) }}
    {% do addWysiwygBinding(id) %}
{% endapply %}
{% endblock %}
...

然后layout.html.twig

<!DOCTYPE html>
<html>
  <head>
    ...
  </head>
  <body>
    ...

        {% set ids = popWysiwygBindings() %}

        {% if ids is not empty %}
            {% javascripts
                'bundles/admin/plugins/wysiwyg_1.js'
                'bundles/admin/plugins/wysiwyg_2.js'
            %}
                <script type="text/javascript" src="{{ asset_url }}"></script>
            {% endjavascripts %}
        {% endif %}

        {% for id in ids %}
            {{ include('_wysiwyg.html.twig', { id: id }) }}
        {% endfor %}
  </body>
</html>