Вызов form_widget несколько раз с разными атрибутами — Symfony

#symfony #twig

#symfony #twig

Вопрос:

Я пытался вызвать виджет формы в своей форме несколько раз с разными атрибутами. Я получаю сообщение об ошибке — Field "x" has already been rendered, save the result of previous render call to a variable and output that instead.

Я исправил все места, которые мог, но есть некоторые поля, для которых нужны разные атрибуты. Например :-

 form_widget(foo.bar, {'value' : 'image'}) }}
 

и

 form_widget(foo.bar, { 'attr': { 'class': 'hidden' }} )
 

Есть ли возможность вызвать виджет формы с разными параметрами в одной и той же форме?

Ответ №1:

Повторное использование одного и того же виджета несколько раз, как вы это делаете, может иметь странные побочные эффекты, потому что Symfony также установит идентификатор html, и в зависимости от того, где вы размещаете поле, вы можете получить неверный html, который, например, может привести к поломке вашего JavaScript / делать странные вещи или сделать отправку форм ненадежной.

Самый безопасный способ убедиться, что у вас есть действительный HTML (например, без дублированного идентификатора), — это вручную отобразить каждый элемент, а затем использовать только элементы формы для получения текущего содержимого / ошибок. Чтобы сделать это, вы можете игнорировать form_widget помощники form_row и и просто написать HTML для полей самостоятельно, а затем вставлять только те части, которые вам нужны, например

 <input name="{{ field_name(foo.bar) }}" type="file">
 

Для этого точного примера требуется Symfony 5.2, потому field_name() что он новый. См. https://symfony.com/blog/new-in-symfony-5-2-form-field-helpers

Недостатком является то, что такие вещи, как {{ form_rest(form) }} , вероятно, больше не будут работать, но, по крайней мере, вы все равно можете использовать любой помощник, который выводит только часть формы, которая не является элементом формы, например form_error , form_label все равно должен работать, но form_widget и form_row вам следует избегать. form_end снова отображает все оставшиеся поля, поэтому вам, вероятно, также следует избегать этого. Более конкретные помощники, представленные в Symfony 5.2, также очень удобны для этого подхода. Таким образом, у вас есть полный контроль над тем, как отображаются поля, и вы можете изменять классы для каждого поля, которое вы используете в нескольких местах.

Если вы используете более старую версию Symfony, вы можете самостоятельно реализовать вспомогательные методы, взглянув на TwigExtension, который их предоставляет, и настроив его для своего приложения, например, предоставьте свой собственный AppTwigExtension с аналогичными функциями.

Если вы хотите продолжать использовать form_widget / form_row , вам следует избегать многократного отображения одной и той же формы (элементов) в одном запросе. Вы можете попробовать использовать что-то вроде вложенных запросов / ESI, но это, вероятно, будет халтурно.

Существует способ рендеринга формы несколько раз, позволяющий каждый раз отображать ее по-разному. Например, вы можете создать FormCollection с добавлением одного и того же типа дважды. Тогда у вас есть одна форма, которая содержит 2 формы (ваша форма дважды). Вы также можете создать несколько экземпляров для одной и той же формы, а затем отобразить их по-разному в своем шаблоне в зависимости от того, какой из них вы используете (foo или bar), например, что-то в этом роде (только грубый набросок, не уверен, что это точно сработает):

 public function contact(Request $request): Response
{
    $form1 = $this->createForm(FooFormType::class, null, ['attr' => ['id' => 'form1']]);
    $form1->handleRequest($request);
    if ($form1->isSubmitted() amp;amp; $form1->isValid() {
        // ...
    }

    $form2 = $this->createForm(FooFormType::class, null, ['attr' => ['id' => 'form1']]);
    $form2->handleRequest($request);
    if ($form2->isSubmitted() amp;amp; $form2->isValid() {
        // ...
    }
    // Do something

    return $this->render('....html.twig', [
        'foo' => $form1->createView(),
        'bar' => $form2->createView(),
    ]);
}
 

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