Bootstrap 5 Form Floating Labels are always in the upper position when using flask-wtforms - jinja2

I am using flask, jinja, and wtforms to make an account creation page, and am using bootstrap's floating labels. My issue is that the labels start in the upper "floating" position on page load, as if there was something typed in to the fields, but there is nothing entered. The labels do not drop down to their default position even if I click and then click away from the elements, or type, delete it, and then click away.
Relevant sections of python:
class createAccForm(FlaskForm):
fname = StringField('First Name', [DataRequired(dataReqMsg), VCName])
lname = StringField('Last Name', [DataRequired(dataReqMsg), VCName])
email = EmailField('Email', [DataRequired(dataReqMsg), VCEmail, Email(emailMsg)])
password = StringField('Password', [DataRequired(dataReqMsg), VCPassword])
password1 = StringField('Password1', [DataRequired(dataReqMsg), VCPassword, PasswordsMatch])
submit = SubmitField('Submit')
#app.route("/createAccount")
def createAccount():
form = createAccForm()
return render_template("createAccount.html", form = form)
Relavent HTML (with jinja):
[I only included one of the form elements, all the rest are the same.]
<div class="form-floating mb-3">
{{form.fname(class="form-control", id="fname")}}
<label for="fname">First Name</label>
</div>
{% if form.errors.fname %}
{{form.errors.fname[0]}}
{% endif %}
This is what the element looks like on load:
P.S.
When I inspect the elements, I see they have the value attribute given to them by wtforms (see below), but not assigned to anything. Could that be causing bootstrap to move the labels upwards? If so, is there a way to fix it other than writing some javascript to remove the attributes (and possibly triggering a change event if necessary)?
The input element in the inspector window:
<input class="form-control" id="fname" name="fname" required type="text" value>
EDIT: I added the following code in a script tag, which did as it was supposed to (none of the inputs have that unassigned value tag anymore), but it did not fix the floating labels.
$("document").ready( () => {
$("input").removeAttr("value");
});

Apparently the placeholder attribute is required (as it says in the very first paragraph of the bootstrap docs). I should read better.
P.S.
It turns out the unassigned value attributes had nothing to do with the issue. But, if you do ever need to change what wtforms renders, this answer leading to the rendering widgets page on the wtforms docs can help.

Related

Flask WTForms SelectField add placeholder or disabled option

I am working on a webpage using flask and wtforms. When opening the webpage, my selectfield should not hold any value (i.e. be blank, have placeholder saying "please choose an option" or something in that direction).
My form is defined like this in forms.py:
class Form(FlaskForm):
selectfield = SelectField('Title', choices=[])
I leave choices as an empty list because they are created from a database through the function get_choices:
# create instance of form
form = Form()
# run function to get data from db
form.selectfield.choices = get_choices()
Here it starts to get gnarly: Since the placeholder value should be empty (i.e. "") or something like "please choose" I don't want to have it in my database. So I add the value manually:
# append
form.selectfield.choices.append('Please choose')
The html part, where I render the form looks like this:
<form method="POST" action= {{ url_for('index') }}>
{{ form.csrf_token }}
{{ form.selectfield(class_="form-control", **{"onchange":"this.form.submit()"}) }}
</form>
What have I tried:
adding 'placeholder = "please choose"' here:
{{ form.selectfield(placeholder="please choose", class_="form-control", **{"onchange":"this.form.submit()"}) }}
(as suggested by Crast here: WTForms Can I add a placeholder attribute when I init a field?)
adding default="Please choose" to my Form class as suggested by Liu Yue (How do you set a default value for a WTForms SelectField?):
class Form(FlaskForm):
selectfield = SelectField('Title', choices=[], default="Please choose")
This works partly, but the Please Choose value should not be selectable which it still is.
I feel like I might be completely on a wrong path here, and maybe oversee a very simple feature. I really can't believe that such a popular feature is not available using wtforms.
I am thankful for any advice and guidance.

The change In the HTML Input Form Doesn't Affect the DOM

I need to find out how Ebay does something about it's product forms, so that I can apply it to my application. It's essential for form data security. Here is the thing;
On Ebay, when you click on a product, and where it's atributes like capacity, condition,quantity appear, and (in my case) you change the quantity and add it to your shopping cart, for example when you type
10 as quantity but default value is 1, the DOM doesn't change to 10,
but still, 10 items go to the cart. Also, if you make a change in the
DOM itself via browser inspector (say make it 10), the default number of
1 doesn't change to 10. As a result, the only case of what happening is; you type the quantity, and even though the DOM doesn't change, the application still knows the desired quantity. (Please note that other tags like h3 can be manipulated through the DOM Inspector, namely the changes are applied immediately)
I need help figuring out how this is done. Thank you in advance.
Any changes made to value property will not be updated to value attribute of input.
Any changes made to value attribute of the input will be updated to value property of input only until value property is not dirty (i.e not changed from somewhere else). Once the value property of input is dirty any changes made to value attribute will not update the value property of input.
You can use Element.value to set or get the value property of input.
You can use Element.getProperty('value') to get and Element.setProperty('value') to set the value attribute of input.
As far as I saw, once you change the value in your input field manually after the browser loads the page, your change from DOM does not change the input field. If my observation is correct, then a solution can be to change and restore the value using JQuery. So, if I am right, something like the following may help:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
$(document).ready(function(){
var val = document.getElementById('fname');
s = val.value;
val.value = "4";
val.value = s;
});
</script>
</head>
<body>
<form action="/action_page.php">
First name: <input type="text" name="fname" value="John" id="fname"><br>
Last name: <input type="text" name="lname" value="Doe"><br>
<input type="submit" value="Submit form">
</form>
</body>
</html>
I am not sure if this is how it's done though.

How to make Symfony/Bootstrap checkbox errors display outside of the form_widget?

I'm not sure how I explain this... :)
I am using symfony, twig and bootstrap to make a basic registration form on my website.
I have a checkbox at the end which the user must tick to accept the terms and conditions. the label for this checkbox includes HTML (the tag) which Bootstrap (or Twig) escapes, so I have to use a custom label next to the checkbox:
<div class="row align-items-center"> <!-- this is a workaround, may not be best practise -->
{{ form_errors(form.acceptTerms) }}
{{ form_widget(form.acceptTerms, { 'attr': {'form.acceptTerms.errors': ' '} }) }}
<label class="form-check-label" for="acceptTerms">I have read and accepted the Terms and Conditions</label>
</div>
Bootstrap renders the label as part of the form_widget twig element rather than the form_label , and the form_error is also rendered within the form_widget when there is an error. So, before, my label would just be displayed in plaintext, i.e. the html <a> tags would be visible to the user. But of course, since I am showing a custom label as well as the widget which includes the label, I have to set the widget label to ' ' (empty string) so now only one label is shown.
This work perfectly, UNTIL there is an error (i.e. the user doesn't tick the box and thus cannot register).
Then the error displays within the form_widget which is next to my custom label (they are in a bootstrap row div) and it ends up pushing my label to the right, when I want the error to be above the label. I tried adding {{form_errors(form.acceptTerms) }} above it as you can see, but that just displays the error BOTH above and next to the label. This is what it looks like with the above code in place:
ERROR you must accept the terms and conditions!
[ ] ERROR you must accept the terms and conditions!I Accept the **Terms and Conditions**
then this is what I WANT it to look like:
ERROR you must accept the terms and conditions!
[ ] I accept the **Terms and Conditions**
This seems like it should be a really easy thing to do, I don't know why its this difficult.
I need to somehow stop bootstrap from rendering the form_error and form_label within the form_widget .
Thanks
In Symfony 4.1 the bootstrap form theme was updated to be compliant with the WCAG 2.0 standard. As a result, the error for a field is no longer displayed with form_errors but within your form_label. If you separate your widget and label you will notice.
More information about this change can be found here.
In order to fix your problem just get rid of form_error (you don't need it any more) and only use form_label and form_widget. If that doesn't satisfy your needs or if you do not like the new behaviour for bootstrap 4 forms in symfony 4.1, you can always use an older form theme, or create your own.
If you decide to create your own form theme make sure to update the file twig.yaml file to something like this:
twig:
form_themes:
- form/custom_form_theme.html.twig
In this case, your custom form theme is strored in templates/form/custom_form_theme.html.twig

Form enter key action with lists and AngularJS

In my AngularJS project I have an account details page where you can change your personal account information. This page allows for multiple phone numbers and e-mailaddresses to be supplied. Using mouse input (or tabbing to buttons and pressing them with space bar) works perfectly, however I'd like to add the convenience of the enter key pressing the 'logical' buttons.
My form looks like (accidentally forgot to translate a few items):
A simplified version of the HTML for the form can be found on PasteBin, I've mainly removed the directives for managing the lists.
All buttons are <button> elements except for the cancel button which an <a> to the previous page, and the submit button is <button type="submit">.
When selecting any text box and pressing enter, the first (non-disabled) <button> element is 'clicked'. Meaning if I would change the last name, hit enter, the first phone number would be removed.
When you're in a new entry of phone numbers or e-mailaddresses (the row with the green + button) it should click that button, and if it's disabled do nothing.
When you're in any other text box on the form it should hit the save button, and also if the save button's disabled, do nothing.
Both buttons will be disabled based on form validation.
There'd be no trouble in changing the type of a button from button to submit if that'd help.
I would preferably have an all HTML solution, using just semantics, but I doubt that's really possible. So the logical alternative would be to use an AngularJS directive.
Please do not provide a jQuery or plain JavaScript solution relying on IDs or something like that. I don't want to hack my way around AngularJS, rather embrace it.
In the meantime I've worked on a directive that allows me to declare what I've called 'submit scopes'.
In essence you have actions (inputs) and targets (buttons), they're bound through a service by a key you can assign in the template. To avoid keys from clashing and from simple annoying work you can create a submit-scope which will cause it's children to prepend a unique key to the value they're accessing.
Within a submit-scope you can still override an action to use a global key instead by setting the attribute global-submit="true".
Example code:
<div submit-scope>
<input type="text" submit-action />
<button type="button" submit-target>Pressing enter in the above field will click this button.</button>
</div>
You can view the entire source code and a slightly larger example on Plnkr.
I just tried to replace
<button>Cancel</button>
with
<input type="button" value="Cancel">
and it seems to work correctly...

Is it possible to use an input within a <label> field?

I have a bunch of optional "write-in" values for a survey I'm working on.
These are basically a radio button with a textbox within the answer field - the idea being that you would toggle the button and write something into the box.
What I'd like to do is have the radio button toggled whenever a user clicks in the text field - this seems like a use-case that makes a lot of sense.
Doing this:
<input type="radio" id="radiobutton"><label for="radiobutton">Other: <input type="text" id="radiobutton_other"></label>
works fine in Chrome (and I am guessing, other WebKit browsers as well), but there are weird selection issues in Firefox, so I'm assuming its a non-standard practice that I should stay away from.
Is there a way to replicate this functionality without using JavaScript? I have an onclick function that will work, but we're trying to make our site usable for people who might have NoScript-type stuff running.
Putting an input inside a label actually has a slightly different meaning. It doesn't make the input itself a label, it implicitly associates the label with the input in the same way as if they were linked by a for/id.
However, this only happens when the label doesn't already have a for attribute to override that (see HTML4 s17.9: “When present, the value of this attribute must be the same as the value of the id attribute of some other control in the same document. When absent, the label being defined is associated with the element's contents.”). It is unclear according to spec what should happen when both containment and for are present.
(And also it doesn't work in IE, which makes the point moot in practical terms.)
No, you'll need some scripting for this.
<input type="radio" id="radiobutton">
<label for="radiobutton_other">Other:</label>
<input type="text" id="radiobutton_other">
<script type="text/javascript">
var other= document.getElementById('radiobutton_other');
other.onchange=other.onkeyup= function() {
if (this.value!=='')
document.getElementById('radiobutton').checked= true;
};
</script>
It (an input inside a label) validates just fine as HTML 4.01. One potential issue I can see with your code is that both radio elements have the same ID in your example. Element IDs must be unique in HTML and XHTML documents and you should use the name attribute instead to identify a radio group.
If you are still having trouble after changing this, you will have to move the input outside of the <label> element and use scripting.