Symfony 3 using a macro in a macro? - symfony

i want to use a macro in a macro. But how is this working?
macro in my twig:
{% import _self as formSubmacros %}
{% macro printSubcategoriesRow(SubcategoriesForm) %}
<div class="the content of the subcatecories">
</div>
{% endmacro %}
macro i write just under the first and here i want to get the 1 macro in:
{% import _self as formMacros %}
{% macro printCategoriesRow(CategoriesForm) %}
<div class="the content of the categories with the macro subcategories">
{% for SubcategoriesForm in CategoriesForm.subcategories %}
{{ formSubmacros.printSubcategoriesRow(SubcategoriesForm) }}
{% endfor %}
</div>
{% endmacro %}
But this is not working...

You should put the import into your macro, to get the proper scope. If this is not working, please provide the error message

Related

symfony's form_errors displays list item instead of just text

[ Symfony 4 ]
I've this template code in Symfony:
{{ form_widget(registrationForm.username, {'attr': {'class': 'form-control'}}) }}
{{ form_errors(registrationForm.username) }}
Instead of just displaying error text, it's generating a list item like this:
<ul><li> Username already exists </li></ul>
How to not have it generate this list item and just get the text?
I guess it is correct behavior, cause you can have multiple errors for one field for example "Username is too short" and "Field Username contains inappropriate characters", but to get only first error you can use:
{{ form_errors(registrationForm.username|first) }}
Or you can customize your form_errors rendering, first create file for form_errors, for example your_form/custom_form_errors.html.twig :
{% block form_errors %}
{% spaceless %}
<div class="error">{{ errors|first }}</div>
{% endspaceless %}
{% endblock %}
And after that include it to your view file:
{% form_theme form 'your_form/custom_form_errors.html.twig' %}
...
{{ form_errors(registrationForm.username) }}
just to extend #Andrii Filenko 's answer. You can modify the output the form_errors or any other form twig function pretty easily. it's called custom theming in Symfony.
Consider this:
// templates/register.html.twig
{% extends "base.html.twig" %}
{% form_theme registrationForm _self %}
{% block form_errors %}
{% spaceless %}
{% if errors|length > 0 %}
<ul class="changed list">
{% for error in errors %}
<li>{{ error.message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endspaceless %}
{% endblock form_errors %}
{% block body %}{% endblock %}
Output:
<ul class="changed list"><li> Username already exists </li></ul>

Twig impossible to invoke a method on a string variable

After updating "twig/twig" to "v2.4.4" I have an error when calling macro function.
Impossible to invoke a method ("widget_prototype") on a string variable (":ERP/timesheets:_form_part.html.twig").
My macro:
{% macro widget_prototype(widget, remove_text) %}
{% if widget.vars.prototype is defined %}
{% set form = widget.vars.prototype %}
{% set name = widget.vars.prototype.vars.name %}
{% else %}
{% set form = widget %}
{% set name = widget.vars.full_name %}
{% endif %}
Calling macro part:
{% include ':ERP/timesheets:_data_content_supply_from_widget.html.twig' with {form:form, name:name} %}
{% endmacro %}
<div id="post_supplies"
data-prototype="{{ _self.widget_prototype(form.supplies, 'remove'|trans({}, 'common'))|escape }}"
style="margin-bottom: 5px">
{% for widget in form.supplies.children %}
{{ _self.widget_prototype(widget, 'remove'|trans({}, 'common')) }}
{% endfor %}
</div>
How to correctly call macro? Tried calling without _self and I have the error: Unknown "widget_prototype" function.
DarkBee's answer is good, but if your macro is in the same twig file that's calling it then you will still need to import it like so:
{% import _self as my_macros %}
{{ my_macros.widget_prototype(...) }}
Seems a bit counter-intuitive but that's how it is.
You need to import the macro, not include it
{% import "my_macro.twig" as my_macro %}
{{ my_macro.function(arg1) }}

Twig Runtime Error: Impossible to invoke a method ("test") on a string variable

I have the following twig template (the code is in the same file):
{% macro renderJob(fields) %}
// renders the job UI block, but I've removed it for simplicity
Hello world.
{% endmacro %}
{% block _jobs_widget %}
<div id="jobsContainer">
{% for fields in form.children %}
{% dump fields %}
{{ _self.renderJob(fields) }}
{% endfor %}
</div>
{% endblock %}
For some reason, after upgrading to twig/twig = v2.1.0 I'm receiving the follwing error:
Impossible to invoke a method ("renderJob") on a string variable ("#AppBundle/Jobs/form/job.html.twig").
I have been trying to figure out what's causing this without any luck. This used to work just fine in 1.3.x. The fields variable contains the proper data, but it appears it can't pass it to the renderJob macro or it can't find the macro (which is kind of odd)?
Have you tried the following ?
{% import _self as renderJobMacro %}
{% macro renderJob(fields) %}
// renders the job UI block, but I've removed it for simplicity
Hello world.
{% endmacro %}
{% block _jobs_widget %}
<div id="jobsContainer">
{% for fields in form.children %}
{{ renderJobMacro.renderJob(fields) }}
{% endfor %}
</div>
{% endblock %}
I think _self is depricated from twigg 2.0, May be you need to check without _self.
Check {{ renderJob(fields) }} instead of {{ _self.renderJob(fields) }}

Dynamically call a macro in Twig?

Is possible to dynamically call a macro in Twig? For example, here is a template and a macro named "group" which builds a button group using buttons array argument. There are also two other macros, save and delete, for building save and delete buttons.
{# Make a group of buttons #}
{% macro group(buttons) %}
{% spaceless %}
{% import "::macros.html.twig" as macros %}
{% set content = '' %}
{% for button in buttons %}
{% set content = content ~ button %}
{% endfor %}
{{ macros.el('div', content, { 'class' : 'btn-group' }) }}
{% endspaceless %}
{% endmacro %}
{# Make a save button #}
{% macro save(attributes, size, image) %}
{% spaceless %}
{{ _self.primary('save'|trans({}, 'buttons'), attributes, size, image) }}
{% endspaceless %}
{% endmacro %}
{# Make a delete button #}
{% macro delete(attributes, size, image) %}
{% spaceless %}
{{ _self.danger('delete'|trans({}, 'buttons'), attributes, size, image) }}
{% endspaceless %}
{% endmacro %}
This works fine passing an array of buttons:
{% import "::buttons.html.twig" as buttons %}
{% set items = [buttons.save, buttons.delete] %}
{{ buttons.group(items) }}
But i'd like to pass macro names to group macro:
{% import "::buttons.html.twig" as buttons %}
{{ buttons.group(['save', 'delete']) }}
and get save and delete macros called automatically. Is this possible and how?
why not just do
{% import "::buttons.html.twig" as buttons %}
{{ buttons.group([buttons.save, buttons.delete]) }}
For those looking for an example of how to implement dynamic macro calling using attribute, checkout https://gist.github.com/tentacode/9728963b9f3a714608f3

Symfony 2 custom themes folder

I'm designing a multi-tenant application with Symfony2, There will be common templates and each tenant could have custom templates. I would like create a theme folder like this(like wordpress with css,img,etc...) :
Themes/commons/base.twig.html
Themes/commons/css/styles.css
Themes/commons/js/script.js
Themes/tenantID/base.twig.html
Themes/tenantID/css/styles.css
Themes/tenantID/js/script.js
Perhaps I'm taking a wrong way...?
Any suggestion ?
Thanks.
There is nothing wrong with your design. I may name "commons" as "default" but thats up to you. Approach with tenantid looks good to me. Whats your question?
https://github.com/fabpot/Twig/issues/17 - no dynamic inheritance
LiipThemeBundle might be a solution: http://symfony2bundles.org/liip/LiipThemeBundle
You can achieve all this by following symfony standard . because if you follow structure you will make full stack full site in frame work and you can also learn how to use frame work.
like in
bundle folder:
userbulndle/css
userbulndle/js
adminbundle/css etc
and use theme forming
{% block gender_widget %}
{% spaceless %}
{% if expanded %}
{% for child in form %}
<div class="radio_ele">
{{form_widget(child) }}
{{form_label(child) }}
</div>
{% endfor %}
{% else %}
{{ block('choice_widget') }}
{% endif %}
{% endspaceless %}
{% endblock %}
{# ----------------------------------------------------------- #}
{% block field_errors %}
{% spaceless %}
{% if errors|length > 0 %}
<div class="error_list">
{% for error in errors %}
{{ error.messageTemplate|trans(error.messageParameters, 'validators') }}
{% endfor %}
</div>
{% endif %}
{% endspaceless %}
{% endblock field_errors %

Resources