How would I be able to supersede the hierarchical dependencies in Drupal 8's twig engine to be able to loop within the i.e Lists/Views which is assigned to a block. So we would have a template: block--views-block--[machine-name]-1.html.twig You will be required to have the variable {{ content }}
Which then recursively buries itself down to field templates. Its completely killing me that one would need so many levels to produce on block of content.
I would like to iterate within the top custom block template the list.
Attempted
{% for key, value in _context %}
<li>{{ key }}</li>
{% endfor %}
To evaluate what is available to iterate down into the object but with no luck. I did though find a nice overriding object structure to reach the field attributes but that was within the field level
item.content['#item'].entity.uri.value
Thanks
i use this to "generate" a picture from my
node--news--full.html.twig
<div class="col-md-3">
{{ content.field_newsbild }}
</div>
the twig debug suggests some filenames. i took this:
field--node--field-newsbild--news.html.twig
and in there i wrote:
{% for item in items %}
<img alt="" src="{{ file_url(item.content['#item'].entity.uri.value) }}" class="img-responsive" {{ attributes }} >
{% endfor %}
hope i'll help a bit.
Related
Using Sylius 1.10, I managed to add a new field in options and it was visible.
But now, I want to add custom field in option values.
But I'm not able to show the new field in values list: there is no template for option values, only the line {{ form_row(form.values) }}, in vendor/sylius/sylius/src/Sylius/Bundle/AdminBundle/Resources/views/ProductOption/_form.html.twig that creates automatically the following
<div class="required field">
<div data-form-type="collection" id="sylius_product_option_values" class=" controls collection-widget" ...>
in which all translations are visible, and the code field, but my custom field. ;(
How can I find the rendering of this, to add my custom field?
Do I really need to explose the {{ form_row(form.values) }} line, generating the "code" field, my field and using foreach on translations?
Or I did something wrong? (This could be the right answer ;))
php bin/console debug:container app.form.extension.type.product_option_value
gives me
Information for Service "app.form.extension.type.product_option_value"
======================================================================
---------------- ---------------------------------------------------------------------------------------------------
Option Value
---------------- ---------------------------------------------------------------------------------------------------
Service ID app.form.extension.type.product_option_value
Class App\Form\Extension\ProductOptionValueTypeExtension
Tags form.type_extension (extended_type: Sylius\Bundle\ProductBundle\Form\Type\ProductOptionValueType)
form.type_extension
Public no
Synthetic no
Lazy no
Shared yes
Abstract no
Autowired yes
Autoconfigured yes
---------------- ---------------------------------------------------------------------------------------------------
! [NOTE] The "app.form.extension.type.product_option_value" service or alias has been removed or inlined when the
! container was compiled.
The correct template to overwrite is
Sylius/Bundle/UiBundle/Resources/views/Form/theme.html.twig
in this block you should add your custom field
{% block sylius_product_option_value_widget %}
<div class="ui segment">
{{ form_row(form.code) }}
{{ form_row(form.your_custom_field) }}
<div class="five fields">
{% for locale, translationForm in form.translations %}
{{ form_row(translationForm.value, {'label': locale|sylius_locale_name}) }}
{% if 0 == loop.index % 5 %}
</div>
<div class="five fields">
{% endif %}
{% endfor %}
</div>
</div>
{% endblock %}
Hope that's will be working for you.
I have a menu block that shows 4 links. I'd like to remove the last one if the user has a specific role.
The menu block is created in a specific twig file, and inside a macro, as follows:
{% import _self as menus %}
{#
We call a macro which calls itself to render the full tree.
#see http://twig.sensiolabs.org/doc/tags/macro.html
#}
{{ menus.menu_links(items, attributes, 0) }}
{% set role = user.role %}
{% macro menu_links(items, attributes, menu_level, elements) %}
{% import _self as menus %}
{% if items %}
<span class='arrow-down'></span>
<ul{{ attributes.setAttribute('id', 'edit-profil-nav') }}>
{% for item in items %}
{% set item_classes = [
'col-xs-12',
'col-sm-12',
items|length == 3 ? 'col-md-4' : 'col-md-3',
item.in_active_trail ? 'active' : 'notactive',
] %}
<li{{ item.attributes.addClass(item_classes) }}>
{{ link(item.title, item.url) }}
</li>
{% endfor %}
</ul>
{{ attach_library('cnas/responsive-navigation') }}
{% endif %}
{% endmacro %}
The major issue I have is that I can't interfere with the macro: I'd like to be able to make comparisons with the user variable, but when I dump it while being in the macro, user shows null.
I found a lot of stuff while looking for an answer, but I've seen everything and its opposite, so I'd like to know if I can do that, and how
Thank you a lot!
macro's in twig have their own scope. You will need to pass the variable user as an argument towards the macro in order to access it.
You could also pass the special variable _context to the macro.
This variables holds all the variables you passed towards the template.
In the documentation there is a way in Symfony to customize a Individual field, based on the name/id of the widget.
{% form_theme form _self %}
{% block _product_name_widget %}
<div class="text_widget">
{{ block('field_widget') }}
</div>
{% endblock %}
{{ form_widget(form.name) }}
Here, the _product_name_widget fragment defines the template to use for the field whose id is product_name (and name is product[name]).
This works for a normal widget, but not if a widget is inside a collection. Because of the extra columns. Like this:
name="productbundle_product_type[foobar][1][value]" id="productbundle_product_type_foobar_1_value"
What is the way to make the Twig customization work inside the collection?
I thought something like this, but that doesn't work:
{% for db in edit_form.list %}
{% block _productbundle_product_type_foobar_{{ db.name }}_widget %}
<div class="text_widget">
{{ block('field_widget') }}
</div>
{% endblock %}
{% endfor %}
Even the following doesn't work:
{% _productbundle_product_type_foobar_1_value_widget %}
What is the way to make it work?
I was working on a project a couple of evenings ago and encountered precisely this problem - the solution I found is a pair of blocks that look like this (stripped of project-specific code):
{# Collection markup #}
{% block my_collection_widget %}
{# Customise collection row prototype for allow_add #}
{% if prototype is defined %}
{% set data_prototype = block('my_collection_item_widget') %}
<div id="my_prototype" data-prototype="{{ data_prototype }}" style="display: none"></div>
{% endif %}
{% for prototype in form %}
{{ block('my_collection_item_widget') }}
{% endfor %}
{% endblock my_collection_widget %}
{# Collection row markup #}
{% block my_collection_item_widget %}
{# Collection contains simple, single type #}
{{ form_errors(prototype) }}
{{ form_label(prototype) }}
{{ form_widget(prototype) }}
{# OR #}
{# Collection contains a compound type, render child types independantly #}
{{ form_errors(prototype.inner_widget) }}
{{ form_label(prototype.inner_widget) }}
{{ form_widget(prototype.inner_widget) }}
{% endblock my_collection_item_widget %}
I know this is old question, but maybe people still happening upon this. This is explained fragment naming for collections.
You use _entry_ in these cases in place of the collection element number. Use the instructions in the link for fragment naming, but this might vary. Sometimes 'type' is part of the fragment's name, sometimes first letter is upper case, sometimes lower case, etc. I would use a browser developer tool to find the actual name to make sure. You might also be able to customize the names used by adding the getBlockPrefix function to the form class.
Therefore, in your case above, the customized block might look something like:
{% block _ProductBundle_product_entry_widget %}
<div> {{ form_row(form.field)}} </div>
{% endblock %}
Where 'field' would be the name of a field in the element of your collection.
A few things here:
1. Documentation Error
The docs seem to be a bit off with CamelCase entities. As for 5.3 it should be: _taskManager_taskLists_entry_widget (instead of _task_manager_task_lists_entry_widget)
You can confirm the naming by dumping the form or form field: {{ dump(form) }} or {{ dump(form.yourField) }} in your template and then look for unique_block_prefix within the vars section.
2. Do NOT use the block overrides, macros are much more easy
It makes things absolutely more complicated than necessary. Simply define a macro:
{% import _self as formMacros %}
{% macro formatCollection(form) %}
<div class="form-group row">
<div>
{{ form_widget(form.field1) }}
{{ form_widget(form.field2) }}
{{ form_widget(form.field3) }}
</div>
</div>
{% endmacro %}
then simply run it on each collection:
{% for item in form.collectionItems %}
{{ formMacros.formatCollection(item) }}
{% endfor %}
3. Prototype
Get the prototype before rendering the collection field. You can store it in a variable.
{% set prototype = form.collection.vars.prototype %}
Then simply render it whenever you like using our macro:
<div data-js="collection" data-prototype="{{ formMacros.formatCollection(prototype)|e('html_attr') }}">
I have to generate something like star rating and I have to generate some html for styling ect.
<div class="star on"><i>*</i></div>
<div class="star on"><i>*</i></div>
<div class="star on"><i>*</i></div>
<div class="star"><i></i></div>
<div class="star"><i></i></div>
I want to render using a twig function passing active stars parameters.
{{ stars(4) }}
Is correct use twig functions for generate html code?
Or maybe should I use {% include ... %}
No need in overengineering for such simple task.
If you generate your array in Controller, then it could look like this:
$stars = array(
true,
true,
true,
false,
false,
);
Then you could render your HTML in Twig:
{% for star in stars %}
<div class="star{{ star ? ' on' }}"<i>{{ star ? '*' }}</i></div>
{% endfor %}
In case if you would like to operate with Twig only, I recommend you to use macro:
{% macro stars(stars, total) %}
{% for item in 1..total %}
{{ item }}<br>
{% if item <= stars %}
<div class="star on"><i>*</i></div>
{% else %}
<div class="star"><i></i></div>
{% endif %}
{% endfor %}
{% endmacro %}
If you've defined your macro in the same template, you should call it via _self, if in another file - just like a function, but not forget to import your file into needed twig. See chapter about macros (linked above).
Following call will produce HTML structure that you described in your question:
{{ _self.stars(3,5) }}
See the Extending Twig section of its docs. According to the table in the first section on that page, using functions for content generation is natural. I create a lot of Twig functions and I suggest you create one to solve your problem.
BTW, your function can render a separate template with HTML code — do not generate the HTML code right in your Twig function's PHP code. To render a separate template from your Twig function, inject the service_container service into it, get the templating service and call the render() method on it:
return $this->container->get('templating')->render($pathToYourCustomTemplate);
Usually, it's best to inject the needed services individually, but if you inject the templating service instead of service_container, you'll get a cyclic dependencies problem. That's why injecting the whole container into Twig extensions is a reasonable exception.
I have a form with a collection type field, rendered like that:
<div id="beneficiosTab" class="opcional">
Beneficios
<ul class="beneficios" data-prototype="{{ form_widget(formAtendimento.beneficios.get('prototype')) | e }}">
{% for beneficio in formAtendimento.beneficios %}
<li>{{ form_row(beneficio.coTipoBeneficio) }}</li>
<li>{{ form_row(beneficio.vrValor) }}</li>
<li>{{ form_row(beneficio.boConcedido) }}</li>
{% endfor %}
<li>Add Beneficio</li>
</ul>
</div>
<div style="clear:both"></div>
{{ form_rest(formAtendimento) }}
The form's entity can have multiple items of the collection, or none.
When the entity has items of the collection, it works fine, but when it has none, the "for" in the twig doesn't happen, and a "Beneficios" div is generated in form_rest.
Any way I can prevent that? Thanks in advance.
This seems like a bug in the form rendering. I managed to disable the extra form rendering in the form_rest function, by adding this code just after rendering the collection elements:
{% do form.uploads.setRendered() %}
Where "uploads" is my collection field type.
This doesn't seem like best practice to me though.
So the whole rendering looks like this:
<div id="uploads" data-prototype="{{ form_widget(form.uploads.vars.prototype)|e }}">
{% for upload in form.uploads %}
{{ form_widget(upload) }}
{% endfor %}
</div>
{% do form.uploads.setRendered() %}
form_rest generate all unrendered forms. Every input is form in Symnfony2, the same goes with collection type.
You never printed out collection, so Symfony makes it for you.
If you want hide it, and still use form_rest simply print it it to:
<div style="display: none" />