Symfony 2 настраиваемый тип поля формы: как добавить javascript и css только один раз? - программирование
Подтвердить что ты не робот

Symfony 2 настраиваемый тип поля формы: как добавить javascript и css только один раз?

Я хочу использовать javascript в пользовательском расширении типа поля формы Symfony 2. Итак, у меня есть шаблон расширения Twig:

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

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

Но я хочу иметь эти теги script и ссылку только один раз в своем HTML, в идеале в head tag, без модификации базового шаблона. Я попытался расширить блоки 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. Извините за мой плохой английский.

4b9b3361

Ответ 1

Мне пришлось написать собственный виджет формы, который требует 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>. Это решение не мешает тем, что javascript присутствует несколько раз, но вы должны легко улучшить это в соответствии с вашими потребностями.

Вы также можете поиграть с объектом $response, чтобы изменить заголовок , если хотите.

Ответ 2

Лучший способ - предоставить отдельный шаблон с загрузкой css и скриптов. С комментариями в readme, так что только разработчик должен сделать это

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

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

{% endblock %}

или попробуйте перехватить рендеринг формы с помощью DI и добавить активы. Но это сложнее сделать, если это возможно.

Ответ 3

Вот как я его использую. Надеюсь, это то, что вы ищете.

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 %}

Ответ 4

Я нашел некоторый "грязный" метод, используемый многими народами в других ситуациях. Мы проверяем загрузку script на стороне клиента. Если у нас есть файл 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 %}