Twig : Access to variable from outer scope in a form widget customization - scope

I'm trying to customize a specific widget, like in the documentation : http://symfony.com/doc/current/cookbook/form/form_customization.html#how-to-customize-an-individual-field
The problem is that in this custom block, I need to use a variable from my actual template. I thought "blocks have access to variables from outer scopes", but apparently not in this case :
{% extends "CDASvBundle::layout.html.twig" %}
{% block _contact_activity1_widget %}
<select name="contact[activity1]">
{% for key, child_contact_categories in contact_categories_tab %}
<option value="{{key}}">{{child_contact_categories}}</option>
{% endfor %}
</select>
It's saying that contact_categories_tab is undefined, but outside of this block (in the normal content block for example), it works !
I tried something like :
{% use 'form_div_layout.html.twig' with contact_categories_tab as contact_categories_tab %}
But that doesn't either.. Though I'm not sure I understand if I have to use use and how !
I see one other solution that I haven't tried yet : put this customization in another template. But I don't really want to do that (few lines in a new template), there should be a way to do that in only ONE template ?!

Finally found the answer in a previous post :
Each symfony form type extents AbstractType class.
AbstactType class has method:
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->set('img_src', '120x100.jpg');
$view->set('my_variable', $foo);
}
You can create this method on your form type and next in your twig:
{{ asset(img_src) }}
Source : How to get entity or pass variable to Symfony2 twig form widget?

Related

How to use safe filter on custom Django tag?

I'm trying to implement a custom Django tag that will formulate an import statement in Javascript to load my vue3 app as well as its components template html files using a get request in axios.
The custom tag in my templatetags directory looks like this:
templatetags/vuecomponents.py
from django import template
from django.templatetags.static import static
register = template.Library()
#register.simple_tag
def v_load_app_component(app_name, script_name, components):
components = components.strip("[]").split(", ")
app_script = static(f"js/{script_name}.js")
comps = [static(f"components/{name}.html") for name in components]
return f"import {{{ app_name }}} from \"{app_script}?{components[0]}={comps[0]}\""
Right now it only loads the first component as I just want a prototype. The only issue is when I drop this into a template like so:
createpost.html
<script type="module">
{% v_load_app_component "creator" "internalpostform" "[internalpostform]" %}
// OUTPUTS:
// import { creator } from "static/js/internalpostform.js?internalpostform=internalpostform.html"
creator.mount("#app")
</script>
It outputs the relevant import statement as:
import { creator } from "static/js/internalpostform.js?internalpostform=internalpostform.html"
With the double quotes escaped. Even when I tried to apply the safe filter ({% v_load_app_component "creator" "internalpostform" "[internalpostform]"|safe %}) it still escaped the output of my custom tag function.
How can I make it to where the output of my custom tag doesn't automatically have symbols converted to html entities?
I found it after a little digging in Django's documentation. The safe filter is only for variables i.e. {{ variable|safe }} but does not apply to a tag {% tag "argument"|safe %}.
To prevent Django from escaping the output of a tag you simply use {% autoescape off %}
{% autoescape off %}
{% v_load_app_component "creator" "internalpostform" "[internalpostform]" %}
{% endautoescape %}
This results in the desired behavior.

Rendering a component inside another in Nunjucks & Fractal

I'm working on a design system and just getting my head around the component way of thinking.
We're using Nunjucks and Fractal.
We have an existing component, an accordion which gets it's data from a JSON object. The props being title and content.
I've built a component, like a table of contents, which displays a list of styled links, using a simple loop, pulling in from a ToC.config.js file.
I need to include that new component within the accordion component, the ToC has a title and an array containing urls and text for each link.
At present I've just copied the HTML from the accordion's view in Fractal, but that's probably not the way to go, but visually it's as the designer requires.
I can also render the accordion correctly if I put all the links, HTML and all in the config, as a string, but that's not the way it should work either.
What I want to do, which is probably the proper way, is pull that accordion in and populate it with my ToC component.
{% render '#my-accordion', 'accordion: items' %} doesn't do what I want and I can't seem to figure out how to achieve what I need to do.
Something like so:
{% render '#my-accordion' %}
// Pass in data from ToC, somehow
{{ title: title }}
{% for item in items %}
{{ item.text etc }}
{% endfor %}
Then I would have my accordion component, where its title, links and HTML etc are those from my ToC component. Sorry for the rough pseudo above, it's more an example of what I want to do. I can't copy code to an external resource.
using render doesn't appear to be the way to go, unless I'm missing something? I can't pass the list through as a string as it has classes and aria for the current page etc and content authors will be building pages with these.
A little nudge in the right direction would be great.
You can add a render-filter that similar to include and macro.
import 'nunjucks.min.js';
var env = new nunjucks.Environment({autoescape: true});
env.addFilter('render', function (template, ctx) {
const html = env.render(template, ctx); // try-catch
return env.filters.safe(html);
});
Usage
// table.njk
<table>
{% for e in rows %}
{% 'row.njk' | render({row: e}) %}
{% endfor %}
</table>
// row.njk
<tr>
{% for c in row.cols %}
<td> {{ c.name }} </td>
{% endfor %}
</tr>

OctoberCMS Partials sharing variables life cycle

I'm new in October CMS and I try to develop some components and these components have more than one page. I include every page as a partial in the default.htm file with an if condition depends on the type property of the page.
{% set type = __SELF__.property('type') %}
{% if type == 'y' %}
{% partial 'x::yy' %}
{% elseif type == 'x' %}
{% partial 'x::xx' %}
the problem is when I have a collection of items in one page and send id of one of the items to another ajax handler and want to retrieve full info of the item, I can't pass it to next page because of the page cycle in October CMS.for example :
// firstpage
{% set posts = __SELF__.posts %}
{%for post in posts%}
<button data-request="handlerX" data-request-data="id: {{post.id}}">
{%endfor%}
//hanlerX
public function onHanldlerX(){
$post = $this->post = POST::where('id',post("id"))->first();
return redirect()->to('/my-posts/'.$post->slug);
}
//nextpage -> my-posts/$post->slug
// post informations is unavailible
what's the best way for these type of components?
Is It the right way for this?
how can I send variables to view after the OnRun method in components?

OpenCart 3.x - including a new template file

I need to add a new OpenCart template file into another template file.
Essentially I've created a new head file in /theme/customtheme/template/common/ called "header_home.twig".
Then in home.twig, I've changed {{ header }} to say {{ header_home }}, but it's not displaying anything.
Basically, all I did was copy header.twig and rename it to header_home.twig, and put in "xxxxx" to see if it was calling the new file, which it's not. Instead, it's not displaying anything.
Here's what my home.twig now looks like:
{{ header_home }}
<div id="common-home" class="container">
<div class="row">{{ column_left }}
{% if column_left and column_right %}
{% set class = 'col-sm-6' %}
{% elseif column_left or column_right %}
{% set class = 'col-sm-9' %}
{% else %}
{% set class = 'col-sm-12' %}
{% endif %}
<div id="content" class="{{ class }}">{{ content_top }}{{ content_bottom }}</div>
{{ column_right }}</div>
</div>
{{ footer }}
I assume I'm somehow missing a step when it comes to adding a new template file? If someone could help me with adding in a new twig file, that would be fantastic.
You can either:
Change the name of Twig rendering template on the OpenCart level in the controller/common/header as #bogalakon pointed out (preferably to do this via ocmod so that your future OpenCart updates will not overwrite your hack).
or include another template on the Twig level
I.e.:
{{ header }} {# Original rendered to HTML OpenCart header - you can move it to your header_home.twig or just drop it #}
{% include customtheme/template/common/header_home.twig %}
<div id="common-home" class="container">
...
Twig is very powerful template language. And you can do much more than just a simple include! It's nice that OpenCart officially accepted it. But for now it's just Twig 1.24.2. Please refer to the Twig documentation.
I haven't learn OpenCart 3.x, but I guess you need to change
return $this->load->view('common/header', $data);
to
return $this->load->view('common/header_home', $data);
in catalog/controller/common/header.php.
In order to connect another template via the {{header_home}} construct, you need to create the header_home.php in the folder /controller/common/directory, create the header_home.twig file in the theme /*theme_name*/template/common/ folder and in the controller , which is responsible for the formation of the page, for example in the directory /controller/common/home.php
add a line:
'$ data [' header_home '] = $ this-> load-> controller (' common / header_home '); '
before:
'$ this-> response-> setOutput ($ this-> load-> view (' common / home ', $ data)); '
To add your own custom twig file and include it inside another existing one you need to do three things:
Create the actual custom twig file.
In your case it will be:
catalog/view/theme/customtheme/template/common/header_home.twig
Create a controller for that twig file. In your case you can just copy:
/catalog/controller/common/header.php and rename that to header_home.php. Edit this controller and change the class name to ControllerCommonHeaderHome usually on line 2.
Lastly, since you are going to include header_home inside home.twig, edit /catalog/controller/common/home.php and add the line $data['header'] = $this->load->controller('common/header_home'); after $data['header'] = $this->load->controller('common/header');
That's it. After you've done the steps above, you can now include {{ header_home }} inside home.twig. If you are editing the files directly, I find that sometimes I need to login to the admin of the website, go to design>theme editor, open up the files I added or changed, hit reset, and save. Refresh your website and you should see the changes.
The answer of #bogalakon is right but if you update your OpenCart core files you will lose your changes so, I suggest you to copy the header.php controller file and rename it to header_home.php and then edit the file and replace the line
return $this->load->view('common/header', $data);
with:
return $this->load->view('common/header_home', $data);
There are several ways to give a different value.
For example:
Consider the controller
$data['header'] = $this-> load-> controller ('common / header');
A variable {{header}} is available in the template
You can place a new template in another file (header_home.twig), and then you can immediately download a new template for use:
$data['**header**'] = $this-> load-> controller ('common / ***header_home***');
If you want use {{header}} in the template.
Or,
$data['**header_home**'] = $this-> load-> controller ('common / header_home');
For use {{header_home}} in the template.

reuse variable as string

This question maybe kind of silly but I'm a newbie for symfony anyway.
prescenario I pass a variable from controller into index.html.twig by doing this
return $this->render('index/index.html.twig', [ 'department'=>$departments,'URILink'=>$URILink,'departmentDetail'=>$departmentDetails,'contentCell'=>$this->mContentCell ]);
After using {% dump %} it shows me
"department" => array:3 [▶]
"URILink" => "http://localhost/index/department/"
"departmentDetail" => array:1 [▶]
"contentCell" => "department.html.twig"
Then I need to reuse the variable contentCell as string in template to form syntax similar to this ;
<div>{{ include ('department.html.twig'),[departmentDetail:departmentDetail]</div>
For my first attempt I tried this,
<div> {{ include ({{ContentCell}}),[departmentDetail:departmentDetail]}} </div>
Unfortunately it showed me this error
A hash key must be a quoted string, a number, a name, or an expression
enclosed in parentheses (unexpected token "punctuation" of value "{".
Any idea how could I use the variable
contentCell as string value appropriately?
try this:
{% include contentCell with { departmentDetail : departmentDetail} %}
Answer here pass data to twig
You can include a template like this per:
{{ include('YourBundle:ControllerName:yourAction.html.twig', {'variableName': yourData}) }}
Or like this per http://twig.sensiolabs.org/doc/tags/include.html
{% include 'template.html' with {'foo': 'bar'} %}
after try'n try at last I found the the trick how to do it
i take similar analogy for
{{dump(var)}}
so attempt to do this
{{include (contentCell,{'departmentDetail' : departmentDetail}) }}
and it work like charm :) nice

Resources