0

I have a form based on FormBase. All the form is wrapped by form.html.twig. I want to render $form['charts'] outside the <form>. I was able to do this, but {{ children }} still render all the form with the charts (charts it's duplicated).

Is their a better approach? I was wondering if I should use a normal controller and embed the form.

All this has to do with #theme_wrapper (that wrap the all content with the form template, but with everything already pre-render in #childrens). I must admit I don't really understand the #theme_wrapper logic.

<?php

namespace Drupal\mymodule\Form;



use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a CDR Dashboard form.
 */
class SurveyQuestionsChartsForm extends FormBase {


  private AccountInterface $account;


  /**
   * @param \Drupal\Core\Session\AccountInterface $current_user
   */
  public function __construct(AccountInterface $account) {
    $this->account = $account;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    // Instantiates this form class.
    return new static(
      // Load services required to construct this class.
      $container->get('current_user')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'mymodule_myform';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, $section = '') {

   

    $form['charts'] = $charts->build();
    $form['charts']['#weight'] = 50;

    $sections = $analysis->getSections();

    $form['section'] = [
      '#type' => 'select',
      '#title' => $this->t('Thématique'),
      '#options' => $sections,
      '#default_value' => $section,
    ];

    $form['actions'] = [
      '#type' => 'actions',
      '#weight' => 10,
    ];
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Afficher'),
    ];


    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {

  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $section = $form_state->getValue('section');
    $form_state->setRedirect($this->getRequest()->get('_route'), [
      'section' => $section,
    ]);
  }

}

mymodule.module

function mymodule_preprocess_form(&$variables) {
  $variables['charts'] = $variables['element']['charts'];
}

form.html.twig

{{ charts }}

<form{{ attributes.addClass(classes) }}>
  {{ children }}
</form>
2

1 Answer 1

2

Yes, a clean solution would be a controller to render the chart. The problem with the <form> wrapper template, you are too late in the render pipeline. In a wrapper template the variable {{ children }} is already rendered. But still there should be some things you can try from within the form. You could hide the element so that it is not rendered inside the form and then unhide it to render it outside. Or use #prefix/#suffix:

$form['#prefix'] = $charts->build();
3
  • With #prefix/#suffix I have to render the array in the form controller has #prefix/#suffix only accept string, this feel like a hack. Using a normal controller, how I access the $form_state in the controller to access the form value?
    – gagarine
    Aug 20 at 20:57
  • Did you try the other option? Hide the chart element by setting #printed = TRUE and later remove #printed to render it outside of the form tag.
    – 4uk4
    Aug 20 at 21:29
  • I will try the #printed = TRUE options ASAP. This should works.
    – gagarine
    Aug 21 at 18:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.