7

I have a template for one of my forms and I want to add css class to the element before I render it but since I am printing the element as a whole I am not sure how to alter the attributes.

I was told on IRC to have a look at the merge method but so far no luck.

{% set submit_attributes = form.submit.attributes %}
{% submit_attributes.addClass('test') %}
{% form.submit.attributes|merge(submit_attributes) %}
{{ form.submit }}

or

{% form.submit.attributes.addClass('test') %}
{{ form.submit }}
6
  • 1
    Have you tried: {% set form.submit.attributes = form.submit.attributes.addClass('test') %} - similar to your second version but just putting something in {%} doesn't really do anything.
    – Cottser
    Sep 9 '15 at 12:55
  • 1
    Although I'm not sure if you can set with dots.
    – Cottser
    Sep 9 '15 at 13:00
  • 1
    Nope, I tried that(and retried just to be sure) and it will result in fatal error.
    – user21641
    Sep 9 '15 at 13:27
  • 1
    Well you can alter the attributes in preprocess but I can't think of a way to do what you're specifically trying to do directly in the Twig template. Is there a specific reason why form.submit has to also contain/print the attributes? I'm curious what that variable looks like.
    – Cottser
    Sep 9 '15 at 13:52
  • 1
    It's a normal submit button and I do not want to use preprocess because I have to have everything in templates so nothing is hardcoded.
    – user21641
    Sep 9 '15 at 15:29
4

Here is my solution for adding a placeholder to a custom text field being used for inputting search terms in a form. I could not use element.attributes.setAttributes() because in my case the key for attributes is #attributes.

{% set attr = form.search_api_fulltext['#attributes']|merge({'placeholder': 'Enter search term'|t}) %}
{% set search = form.search_api_fulltext|merge({'#attributes': attr}) %}
{{ search }}

Here is the same expanded a little for clarity:

{% set search = form.search_api_fulltext %}   
{% set attr = form.search_api_fulltext['#attributes'] %}
{% set attr = attr|merge({'placeholder': 'Enter search term'|t}) %}
{% set search = search|merge({'#attributes': attr}) %}

{{ search }}

Or add a third merge and merge the field back into the form. Then output the form as usual:

{% set search = form.search_api_fulltext %}
{% set attr = form.search_api_fulltext['#attributes'] %} 
{% set attr = attr|merge({'placeholder': 'Enter search term'|t}) %}
{% set search = search|merge({'#attributes': attr}) %} 

{% set form = form|merge({'search_api_fulltext': search}) %}
{{ form }}
3

It looks like at this moment this is just not possible. I have created an issue at drupal.org for this. Maybe some day it will get implemented and we can get rid of the phptemplate engine for good.

https://www.drupal.org/node/2567759

2

Well, I really don't know if you have a particular reason(like keeping everything on the HTML file) to do this in Twig, not in preprocess, but I can give you a code that worked for me on preprocess:

function hook_form_FORM_ID_alter(&$form, &$form_state) {
  $form['actions']['submit']['#attributes']['class'] = array('btn-primary form-control');
}

This code should be in the your_theme.theme file(like the old template.php for Drupal 7).

1
  • 1
    It is possible to add classes from within the preprocess, but in Drupal 8 ,due to performance reasons, it is recommended to do this directly in the twig template file (drupal.org/node/1920746). Quite a shift from the 'leave processing out of templates' mantra of Drupal 7. Still trying to get use to this change in mindset myself.
    – Benjen
    Apr 7 '16 at 2:47
2

It is possible using merge filter, I do this with great success on menu items:

{% set item = item|merge({'attributes': item.attributes.addClass('new-class')}) %}

I think you should able to do something like this with deeper nesting:

{% set form_submit = form.submit %}
{% set form_submit = form_submit|merge({'attributes': form_submit.attributes.addClass('test')}) %}
{% set form = form|merge({'submit': form_submit}) %}
{{ form.submit }}

Though you could just as well render form_submit and exclude it from being rendered along with form elsewhere.

1

for twig form add attribute :

  {# add a "foo" class to it #}
  {{ form_widget(form.name, { 'attr': {'class': 'foo'} }) }}
 for buttons 
 {{ form_widget(delete_form.submit, { 'attr': {'class': 'btn btn-danger btn-lg', 'type': 'button'} }) }}

=============================================

if your question for drupal form then code is give below and it's working for me.

function MY_MODULE_Name_form_alter(&$form, &$form_state, $form_id) {
    if ($form_id == 'user_register_form') {
      $form['actions']['submit']['#attributes'] = array('onclick' => 'if(!confirm_reg(this.id)){return false;}');   
    }
 }

and this code should not be in theme file it's in your modulename.module file hope this helps you...

1

Add to your templates folder

input--submit.html.twig

{%
set classes = [
   'test',
   'test2',
 ]
%}
<input{{ attributes.addClass(classes) }} />
0

Everyone is quite right, the 'drupal' way of doing things is to have hook_form_alter - preferably hook_form_FORM_ID_alter and add the class before it gets rendered.

If you are looking for an alternative way - you could get the form element HTML from drupal using render($form['submit']) (I am assuming this is for the submit button) so

$submitHTML = render($form['submit']);

You can then pass this HTML to your TWIG template as a variable.

$markup = array('markup' => $submitHTML);
$template = $twig->loadTemplate('myTwigFile.twig');
$output = $template->render($markup);

In your TWIG template you can wrap the HTML in whatever you want and simply render the raw HTML you got from drupal.

<div class="clearfix">
  {{markup[0]|raw}}
</div>

Note that I am referencing the submit button markup as the first element of the markup array - so you could add other form elements to the same array.

I have omitted some plumbing code (setup) - but hopefully you get the idea.

4
  • Early rendering is bad, just pass the render array to your Twig template and render it there.
    – Cottser
    Sep 16 '15 at 19:36
  • Please tell me more. What I usually try to do is have as little logic as possible in the TWIG file so that it almost resembles an HTML file with 'new tags'. Do you mean I should access properties / elements of the Drupal Render Array in the TWIG file? Sep 17 '15 at 20:02
  • Sorry I left my comment in a hurry, what I meant is that if you pass a render array to a Twig template you can just put {{ my_var }} where $variables['my_var'] is a render array. Twig in D8 knows how to print render arrays without any intervention :)
    – Cottser
    Sep 18 '15 at 0:57
  • That sounds excellent. Sadly I have not tried D8. In D7 I simply load twig as a library and have a couple of my own routines to extract data from entities and render array's. Probably not perfect, but it makes my code and markup very readable. Look forward to an excuse to get into D8 Sep 20 '15 at 8:23

Your Answer

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