I need help on using Symfony2.1 forms with method=GET and a clean URL space.
I am creating a "filter" which I'd like to set in the URL so that people can bookmark their links.
So, very simply the code:
$form = $this->createFormBuilder($defaultData)
->add('from', 'date', array('required' => false, 'widget' => 'single_text', 'format' => 'dd.MM.yyyy'))
I render the form widget and all is fine.
However when I submit the form, it produces very ugly GET parameters:
/app_dev.php/de/event?form%5Bfrom%5D=17.11.2012
This is because the input name is of course form[from]
So to clean the URL space, I made myself a theme:
{% block widget_attributes %}
{% spaceless %}
id="{{ id }}" name="{{ id }}"{% if read_only %} disabled="disabled"{% endif %}{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if pattern %} pattern="{{ pattern }}"{% endif %}
{% for attrname,attrvalue in attr %}{{attrname}}="{{attrvalue}}" {% endfor %}
{% endspaceless %}
{% endblock widget_attributes %}
where I replaced name="{{ full_name }}" with name="{{ id }}".
This works well - my URL space is cleaner:
/app_dev.php/de/event?form_from=17.11.2012
I guess I could live with that - although ideally from=xxx would be better. That is the first and more minor problem.
The second problem is that I can't get the form to bind anymore - this is obvious because the parameter "form" is no longer set - "form_from" has replaced it, but when you do a bind it is still expecting form[].
I tried to fix that like this:
$fromDate = $this->get('request')->query->get('form_from', null);
$request->query->set('form', array('from' => $fromDate);
But that doesn't work. I also suspect that I am digging a huge hole of hacks at the moment.
So, the question is: should I just live with the form%5Bfrom%5D url, or is there a better way to do all of this (without using POST obviously)?
You can set the name of the root form to empty, then your field name will be just form. Do so via
// the first argument to createNamedBuilder() is the name
$form = $this->get('form.factory')->createNamedBuilder(null, 'form', $defaultData)
->add('from', 'date', array(
'required' => false,
'widget' => 'single_text',
'format' => 'dd.MM.yyyy'
));
old thread, but worth mentioning that symfony 3 ignores getName entirely.
However, you can do the same with getBlockPrefix if you need the form name to be blank.
public function getBlockPrefix() {
return null;
}
this will result in form fields being named without a prefix.
using return null; in your implementation of AbstractType::getName seems to have the same effect these days.
Related
I'm trying to follow the syntax in the docs: https://github.com/craftcms/contact-form/blob/v2/README.md to output a select. I tried all sorts of syntax but I couldn't get it right… 😅
From the docs, I would have thought this would work, but it just adds the options as an attribute of the select field in the HTML.
{{
tag(
'select',
{
id: 'type',
name: 'message[type]',
options: [
{
label: 'option1',
value: 'option1'
}
],
class: message and message.hasErrors('message.type')
? 'error'
}
)
}}
The HTML output I get from that:
<select id="type" class="" name="message[type]" options="[{...;}]"></select>
I know I could just code it up as "html" but I'd like the keep the markup consistent and it makes the validation cleaner. Any pointer in the right direction much appreciated!
What I think you want.
I understand that you want to produce a dropdown selection using the tag() function, and you want to avoid just slapping in some HTML and calling it a day.
The HTML we want out of this...
A <select> tags options should be inside the element, as <option>s.
So the output we want is.
<select id="type" class="" name="message[type]">
<option value="option1">Option1</option>
<option value="option2">Option2</option>
</select>
tag() function vs the {% tag %} tag
We have two options for making a tag in Craft-Twig. The tag() function and the {% tag %} tag.
While they give use two routes to the same end...
tag() is better for when the tag has no innerHTML/innerText, or the contents are pulled from another function or API.
{% tag %} is better for when the tag has longer content, or that content is being dynamically generated.
I think {% tag %} is the better option for this situation, but I'll go through them both.
The tag() function
Documentation: Craft CMS - Functions - tag()
tag('tag_type',{options_object})
The tag_type is the <tag_type>. Everything else is optional.
The options_block may include two options that affect the inner contents of a tag:
text: "Woo!": Text will be HTML encoded and rendered inside your tag.
html: "<i>Yay!</i>": HTML to be slapped into your tag, with no safety-net.
Everything else is stringified and added as an attribute.
So id: "thinger becomes <tag id="thinger">
Why is your code doing this?
option={...} isn't one of tag()'s two 'inner stuff' options, so it's just turned into a string and slapped in as an attribute.
To get what you want with tag()
Just add your desired innerHtml as a string to the options_object's html key.
{{
tag(
'select',
{
html:"<option value="Option1">Option1</option><option value="Option2">Option2</option>"
}
)
}}
As you can see, though, that can be a bit cumbersome when you have long HTML to insert.
The {% tag %} tag
Documentation Craft CMS - tags -tag
{% tag %} works almost exactly like tag(), except it let's us put the 's contents inside {% tag %} and {% endtag %}.
It accepts one argument, the tag type ('element' below), and an optional object via with, with each key/value pair becoming attributes on the tag to create.
{% tag 'element' with {
class: "some class"
custom_attribute: "some value"
}
%}
Your html {{ 'and_Twig()'|upper }} here.
{% endtag %}
becomes
<element class="some class" custom_attribute="some value">Your html AND TWIG here.</element>
This is better suited for when you have verbose tag contents, or contents that are dynamically generated by other tags/functions.
To get what you want with {% tag %}
Just put your option tags inside the {% tag %}...{% endtag %}
{% tag 'select' with {
id: 'type',
name: 'message[type]',
-%}
<option value="option1">Option1</option>
<option value="option2">Option2</option>
{%- endtag %}
But I don't want to HTML it up...
No problem!
{% tag 'select' with {
id: 'type',
name: 'message[type]',
-%}
{% tag('option', {
text: "Option1"
value: "option1"
}) %}
{% tag('option', {
text: "Option2"
value: "option2"
}) %}
{%- endtag %}
I try to use repeater groups on my theme settings yaml file. So i add the above code to my theme/config/fields.yaml:
fields:
cont:
tab: Content
name: cont
label: Content
type: text
content:
tab: Content
label: Content
prompt: Add content block
span: full
type: repeater
groups:
textarea:
name: Textarea
description: Basic text field
icon: icon-file-text-o
fields:
text_area:
label: Text Content
type: textarea
size: large
quote:
name: Quote
description: Quote item
icon: icon-quote-right
fields:
quote_position:
span: auto
label: Quote Position
type: radio
options:
left: Left
center: Center
right: Right
quote_content:
span: auto
label: Details
type: textarea
Everything is working fine on theme settings backend and i can insert data on my fields.
Now i try to render those fields on my cms page but no mater what i try i never succeed. I try:
{% for fields in this.theme.content%}
{{ fields.textarea }}
{% endfor %}
also
{% for fields in this.theme.contents %}
{% if fields.groups == "textarea" %}
{{fields.groups.textarea}}
{% endif %}
{% endfor %}
But i can't render fields.
Hmm, it seems some confusion there and some wrong variable names :)
let's fix this.
End result would be like this :
{% for field in this.theme.content %}
{% if field._group == "textarea" %}
<h1>{{field.text_area}}</h1>
{% endif %}
{% if field._group == "quote" %}
<h1>{{field.quote_position}}</h1>
<h1>{{field.quote_content}}</h1>
{% endif %}
{% endfor %}
what are the mistakes [ If you are in hurry skip this :) ] (its here for your improvement it does not server any other purpose :) )
You are using contentso you need to make sure you use correct variable name here you can use this.theme.content not the this.theme.>>contents<<
next its field._group not the fields.groups
and finally fields its
fields:
text_area:
....
so you need to use them field.text_area not the field.textarea and field.quote_content so on ...
if you find any difficulties please comment.
I'm trying to use a variable in my section file but it doesn't appear to be inherited from it's parent template.
For example:
index.liquid
{% assign foo = "bar" %}
{% section 'example' %}
sections/example.liquid
<h1>{{ foo }}</h1>
{% schema %}
{
"name": "Example",
"settings": [
...
]
}
{% endschema %}
It will not output the value of {{ foo }}, instead I just get: <h1></h1> as if the variable was never defined.
I thought sections would work like snippets, where anything defined in the parent template would be in scope in the included snippet:
index.liquid
{% assign foo = "bar" %}
{% include 'example' %}
snippets/example.liquid
<h1>{{ foo }}</h1>
Where I would get <h1>bar</h1> when rendered.
Is this a bug, or intended behaviour?
Is there a way I can include a section and use variable from some form of outer-scope?
Thanks!
If this is intended behaviour, I managed to find a way round it and thought I would post my not-perfect but workable solution:
sections/example.liquid
<h1><!-- foo --></h1>
You can use capture, to get the contents of the section as a string and use string filters on the captured markup:
index.liquid
{% assign foo = "bar" %}
{% capture section %}{% section 'example' %}{% endcapture %}
{{ section | replace: '<!-- foo -->', foo }}
You could of course replace any string, with your variable. But I found HTML comments work well because if you forget to run the replace, or don't need to - nothing is rendered.
If you wanted to do something more complex, like remove some markup from the section you could:
sections/example.liquid
<div>
<!-- REMOVE_TITLE? -->
<h1>{{ section.settings.title }}</h1>
<!-- REMOVE_TITLE? -->
<ul>
{% for block in section.blocks %}
<li>{{ block.settings.image | img_url: '300x' | img_tag }}</li>
{% endfor %}
</ul>
</div>
Then you could do something like:
{% capture section %}{% section 'example' %}{% endcapture %}
{% assign parts = section | split: '<!-- REMOVE_TITLE? -->' %}
{% for part in parts %}
{% assign mod = forloop.index | modulo: 2 %}
{% if mod > 0 %}{{ part }}{% endif %}
{% endfor %}
I would assign all your variables in a snippet and the keep including this same snippet in whichever scope you need to use the variables in....
That is a fairly DRY approach.
Also anything defined in config/settings_schema.json has a global scope but it can be given new values in theme settings by end users.
Im attempting to assign a variable returned from a filter to a variable inside a template. Is this even possible?
My filter:
def return_an_object(input)
Model.find(input)
end
The liquid:
{% assign bar = {{ "foo" | return_an_object }} %}
This results in input being nil within return_an_object.
Is it at all possible to do something like this?
Answering this myself.
{% assign bar = {{ "foo" | return_an_object }} %}
Should be:
{% assign bar = "foo" | return_an_object %}
I'm trying to someting like this
{% for movie in movies%}
*{{ movie.name }}* {{ movie.price | currency }}
Ratin: {{ movie.rating }}
{% endfor %}
{% render_erb 'partial/footer' %}
footer would be a file like footer.erb.html.
How can I include as a partial as a erb file?
the solution is very easy:
{% template 'common/footer' %}
when you call render, the file extension is used to chose which template engine to use. So a regular call to render should do the trick, given that your partial has the right extension. I say should because I didn't try, but I don't see why it wouldn't work.