I have:
{{ form_start(form, {'action': path('admin_user_edit_exec',{'user_id':id,'babis':'777'}),'attr': {'class': 'smart-form userform','id':'formid'} } ) }}
I want to add smthing like: onsubmit:"return validateForm()"
to call a js function but struggling with syntax,everywhere tried to put the onsubmit.
Form stopped working, where i should put it?
There's no problem about rendering that attribute in the html tag. You can do it the same way you'd do it with any other attribute (class, id ...).
For instance (I've changed the action a bit to adapt it to my SF installation):
{{ form_start(form, {'action': path('_demo_hello',{'name':5}),'attr': {'class': 'smart-form userform','id':'formid', 'onsubmit':'return validateForm();'} } ) }}
would render:
<form name="contact" method="post" action="/symfony/web/app_dev.php/demo/hello/5" class="smart-form userform" id="formid" onsubmit="return validateForm();">
If you have the js function validateForm well set up, it should work. Anyway, if it didn't, that would be a problem related to your js and not to SF.
As pointed out in the comments, it'd be better practice to attach the submit event to the element using js instead of adding it in the HTML tag. You can do that using either jQuery or pure js (which is pretty straightforward too, no need to rely on jQuery always).
Related
Say I have a form named myform with an input field named myinput. By default, symfony will generate HTML widget like:
<input name="myform[myinput]" ... >
This doesn't work with my current javascripts and I need to get the following instead:
<input name="myinput" ...>
I've searched quite a bit and found 2 ways to achieve this:
return null in getBlockPrefix() method in form type class.
create the form using FormFactoryInterface::createNamed() and pass null as name.
It seems like the first method is not recommended, since it would limit the ability to customize form rendering using prefixed blocks.
The 2nd method was recommended here and here.
However both methods will change the name of the form to null and as a result symfony will generate the form like:
<form name="" ...>
This is probably because form_div_layout.html.twig looks like:
<form name="{{ name }}" ...
Which doesn't validate as HTML5.
According to this page, "this is not a bug".
I know I can override the form_start block in the template and remove the name altogether, but it seems that the form wasn't designed to be used with null names in general (hence no check for name length in the template).
So my question is: What is the recommended and HTML5 compatible way to remove input name prefixes for symfony forms?
It was a bug in form rendering. I've submitted a pull request to symfony repo which was accepted.
Until the change is released, a temporary solution would be to add this code to your form theme:
{# fix HTML5 validation of forms with null names #}
{% block form_start %}
{% set name = name|default(block_prefixes.1) %}
{{ parent() }}
{% endblock %}
Regarding PHP, I think either option is OK.
Regarding Twig, I'd go for creating a custom form theme and, optionally, apply it to all forms on the site. I think that's the Symfony way of rendering a form according to your, specific, needs (nullable form name).
I have problem with forms and page layouts. I render my page by:
{% block body -%}
{{ form(edit_form, {'style': 'horizontal'}) }}
<ul class="record_actions">
<li>
<a href="{{ path('organization') }}">
<button type="button" class="btn btn-default">Back to the list</button>
</a>
</li>
<li>
{{ form(delete_form) }}
</li>
</ul>
{% endblock %}
I have some style on ul record_actions. It looks like this: http://postimg.org/image/sby8jnojz/
My problem is with update button. I would like to put it into <ul> tags with 2 other buttons. Is there some possibility to put it outside of form? I like, how the form looks with {{ form(edit_form, {'style': 'horizontal'}) }}. So I wouldn't like to customize every part by {{form_widget}}. Or is there possibility to render all form and then render just this button?
Updated answer
Let's take a hypothetical controller method - where I have defined two forms edit_form and delete_form. Don't worry about these too much, they are just proof of concept. The important thing here is that I have two forms that I am sending to the template to be rendered:
// Foo\BarBundle\Controller\BazController
public function editAction()
{
// a placeholder 'edit' form
$editForm = $this->createFormBuilder()
->add('name', 'text')
->add('email', 'email')
->add('send', 'submit')
->getForm();
// a placeholder 'delete' form
$deleteForm = $this->createFormBuilder(['id' => 1])
->add('id', 'hidden')
->getForm();
// assign form views to template
return [
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
];
}
Next the template. We have two forms to render: edit_form and delete_form. There are a couple of issues we need to consider - rendering a form within a form is not allowed so we cannot render delete_form inside edit_form or vice versa.
However we can, as I explained below, with the HTML5 form attribute place form elements outside of a <form> context and still link them to that form (with the aforementioned IE* limitations). So let's do that, and suggest a workaround in due course.
The least invasive thing to do is to render the delete_form after the edit_form but place the edit_form delete button inside edit_form.
I don't know if you are using a CSS framewok to help you with layout - I am assuming Bootstrap 2.* here so you might have to update your markup - either way the idea should be clear enough:
<div class="row">
{{ form_start(edit_form, {'attr': {'id': 'edit-form'}}) }}
<div class="span4">
<ul class="record_actions">
<li>{{ form_widget(edit_form.send)}}</li>
<li><button id="delete-form-submit" form="delete-form">
Delete
</button>
</li>
</ul>
</div>
<div class="span4">
{{ form_rest(edit_form) }}
</div>
{{ form_end(edit_form) }}
</div>
{{ form(delete_form, {'attr': {'id': 'delete-form'}, 'method': 'GET'}) }}
The above HTML yields a layout similar to the following:
A few points of explanation:
I have created a two-column layout for the form. The .record_actions buttons are rendered on the left column - this is essential - but they are floated right using Bootstrap's .pull-right in this case.
Update button: the first thing I want to do is render the submit button in ul.record_actions where I want it: <li>{{ form_widget(edit_form.send)}}</li>
Delete button: I have not defined a submit button on the delete_form because I want to explicitly create it outside of the context of the delete_form, instead placing it where I have. Note that I defined a form attribute on this element called delete-form. This links this element to delete_form instead of edit_form: <li><button id="delete-form-submit" form="delete-form">Delete</button></li>
Remaining fields: in the second column I can dump all the remaining edit_form fields implicitly with {{ form_rest(edit_form) }}, as per #Kix's suggestion!
Delete form Finally, we render the delete_form outside the edit_form with {{ form(delete_form, {'attr': {'id': 'delete-form'}, 'method': 'GET'}) }}. A couple of things to note here - we are explicitly adding an id for the form with {'attr': {'id': 'delete-form'}. This is important as it is the attribute that the delete button refers to. In this case I also added 'method': 'GET' to test on my machine. You will probably want to leave this out (in which case it defaults to POST)
There you have it... This should help you define your preferred layout.
But, we still need to address IE. If you are using jQuery, you could add a click handler to the delete button, which we've assigned the id #delete-form-submit. Note that the following is a suggestion and is not tested:
$(function() {
if ($.browser.msie) { // #see: http://api.jquery.com/jquery.browser/
$('#delete-form-submit').on('click', function(e) {
e.preventDefault();
$('#delete-form').submit();
});
});
});
Now you need to worry about IE users with JS disabled... or not! ;) Hope this helps.
Original answer
I would argue that your issue is possible Symfony agnostic... Let me explain:
Do you create your form's submit button with the form builder? I assume so since you do not explicitly create one in the twig snippet you pasted above.
This is totally fine of course. I usually just define my Twig form templates like so, which I gather is the older way to do it (since the 2.4 docs don't appear to suggest the following):
<form class="form-horizontal" action="{{ path('foo_edit') }}" method="post" {{ form_enctype(edit_form) }}>
{{ form_widget(edit_form) }}
<input type="submit">
</form>
This way is totally acceptable in my view - you just don't define the submit button.
Of course this does not solve your problem, because you want to "break out" of the form. But you can actually do this with HTML5 with the form attribute, which allows you to link disparate tags to a specific tag. A generic example:
<form id="foo">
<label>Username</label>
<input type="text" name="username">
</form>
<ul>
<li><input type="submit" form="foo">
</ul>
Note that the submit button is outside form#foo but the form attribute still links to it.
Obviously its usefulness is restricted to the range of browsers you want to support, as it's a HTML5 feature.
EDIT
I checked - it has pretty wide browser support, except.... drumroll IE. Up to and including IE10 apparently. I would assume that this is a dealbreaker unfortunately.
kix's approach above could work well however, print out the individual form widgets you want explicitly and then use form_rest. I would add to this and say it might not exactly work with the HTML layout you have above - iirc you have to print out any fields explicitly before you call form_rest.
You can always render some widgets in places specific to your layout using
{{ form_widget(delete_form.yourWidgetName) }}
and then let Symfony complete the form with
{{ form_rest(delete_form) }}
When I want to add some attributes to symfony2 form elements I can just use the "attr" attribute. But how can I give some id / class / style / other to the starting form tag itself?
{{ form_widget(form.name, { 'attr': {'class': 'foo'} }) }}
This is the correct syntax for a form widget with the class foo. In other words a form field in your form.
You can apply the same for the <form> tag.
This is done by rendering
{{ form_start(form, { 'attr': {'class': 'foo', 'id': 'bar', ... } }) }}
See this short documentation on form_start here
Renders the start tag of a form. This helper takes care of printing the configured method and target action of the form. It will also include the correct enctype property if the form contains upload fields.
You might also be interested in form variables. They are documented here. All in all there is a whole documentation on all functions and variables you can use here
I wouldn't recommend you to use inline styles. Just do the styling with the ID and/or class you gave the form.
Here's the question in brief:
My blog posts at...
http://www.seanbradley.biz/blog
...totally lacks formatting. They're just a big block of plaintext. Any code or HTML tags such as /n or < br / > ...let alone h1, h2, etc...have no demonstrable effect on the way text appears on the page.
I'm running Flask with WTForms deployed on GAE. How can I fix this? Is there a way to implement a WYSGIWYG editor--like TinyMCE--into the form field for new blog post entries?
I'm going for a look as simple and elegant as...
http://www.quietwrite.com
or
http://lucumr.pocoo.org/
...or at a minimum something akin to Stackoverflow's own editor.
Formatting is rendered in the posts in all of the above upon publication (rather than via the grody overblown toolbar of an editor).
I'm not sure if what's prohibiting HTML tags from rendering in my posts is related to customization of a class in WTForms, or something which must be treated specially in GAE's datastore, or something I need to fix within Flask (e.g., the model for posts). Any clear solution on how I--as a relatively junior dev--can incorporate formatting into these blog posts earns the bounty. Specific code from the app below...
Note: there's also a Flask-Markdown extension, but I'm equally unsure as to how to integrate it to achieve the effect I want.
The question in detail, plus snippets from the code base
I'm running Flask (with Jinja templates / Werkzeug routing, of course) on Google App Engine, and confused about how to integrate a WYSIWYG editor into the page I have dedicated for blog posts...
I'm assuming, if incorporating TinyMCE, the call for the JavaScript goes in the header of the template...like so:
<script type="text/javascript" src="/static/js/tiny_mce/tiny_mce.js">
But, then--because, per se, there's no within the template or the rendered page itself, it's not merely--per TinyMCE's install docs--a matter of also adding the following block of code into the template...
<textarea cols="80" rows="10" id="articleContent" name="articleContent">
<h1>Article Title</h1>
<p>Here's some sample text</p>
</textarea>
<script type="text/javascript">
tinyMCE.init({
theme : "advanced",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
mode : "exact",
elements : "articleContent"
});
</script>
Presently, within the tags of the template file...
<label for="title">{{ form.title.label }}</label>
{{ form.title|safe }}
{% if form.title.errors %}
<ul class="errors">
{% for error in form.title.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<label for="prose">{{ form.prose.label }}</label>
{{ form.prose|safe }}
...more WTForm validation / error handling...
I suspect the problem is in the way the form is constructed / managed via WTForms. WTForms resides in my /packages/flaskext directory. Is something like the following required somewhere...?
class wtforms.fields.TextAreaField(default field arguments, choices=[], coerce=????????, option_widget=????????)
But TextAreaField is imported from (I don't know where)...and don't know if that's even the right place / thing to tweak. Is the answer in the init.py, file.py, and/or html5.py of the WTForm module?
Again, I'd be happy if just HTML tags I include in the post are rendered upon publication...but slick way to make it easy for someone who is not accustomed to HTML to likewise format their posts would be doubly appreciated. Any help pointing me in the right direction is super appreciated!
Add'l code, if needed, follows...
I have the following class in my models.py...
class PostModel(db.Model):
"""New Post Model"""
title = db.StringProperty(required = True)
prose = db.TextProperty(required = True)
when = db.DateTimeProperty(auto_now_add = True)
author = db.UserProperty(required = True)
And the following class in my forms.py...
class PostForm(wtf.Form):
title = wtf.TextField('Title: something buzz-worthy...', validators=[validators.Required()])
prose = wtf.TextAreaField('Content: something beautiful...', validators=[validators.Required()])
The issue of having your HTML be HTML-encoded is most likely an issue that happens at render time rather than at writing time. When you receive the data back and persist it in the datastore if your HTML is still HTML and not <tag> then you need to mark your rendering as safe:
{% for post in posts %}
<h2>{{ post.title | safe }}<h2>
<article>{{ post.content | safe }}</article>
{% endfor %}
Also, in WTForms, Field.Label (as in form.prose.label) actually renders a full label (no need to wrap the call to it in pre-rendered labels.)
For rendering form errors in a twig template, you just have to use the form_errors twig macro without difference if it is a global form error or a field error.
But in my case, a global error is not rendered like a field error, so I can't use the form_errors twig macro for the two cases. I decide to use the macro for the field error & I would like to get the global form errors from the Symfony\Component\Form\FormView object. The goal is to iterate the global errors in the twig template & render them like I want.
Actually, I don't find any ressources on the symfony2 documentation which can help me.
Finally, I found the solution by myself. For the people who want to do the same thing, the solution is to call $formView->get("errors") which gives you an array of FormError
I'm using symfony 2.5 and it worked perfect for me in this way.
MyController
$error = new FormError(ErrorMessages::USER_NOT_AUTHENTICATED);
$form->addError($error);
MyView
{% for error in form.vars.errors %}
<div class="alert alert-danger" role="alert">
{{ error.messageTemplate|trans(error.messageParameters, 'validators')~'' }}
</div>
{% endfor %}
hope this will save someones time.
in symfony 2.3 all accessor methods have been removed in favor of public properties to increase performance.
$formView->get("errors");
is now:
$formView->vars["errors"];
Visit UPGRADE-2.1.md and refer to section "Deprecations" for more information.