Een textarea met een tekenteller widget voor Flask, WTForms en Bootstrap
Het toevoegen van een WTForms textarea widget ziet er gemakkelijk uit, maar verschillen tussen Linux en Windows veroorzaken onverwachte problemen.
Ik hoopte u vandaag te kunnen vertellen dat u nu commentaar kunt geven op de blogberichten van deze website. Dat zou hebben betekend dat ik de eerste implementatie van het opmerkingensysteem heb afgerond. Helaas stuitte ik op een aantal problemen, ja natuurlijk, ik ben een programmeur, en een daarvan betrof de TextAreaField.
Ik wilde gewoon een eenvoudige uitgebreide versie van de WTForm TextAreaField, voeg gewoon een karaktertelerveld toe onder de textarea en dat is het. Ik dacht dat dit een paar uur zou duren, maar ik had het helemaal mis, maar ik heb dit op de een of andere manier opgelost en dacht om dit met u te delen. Laat me weten wat je denkt ... hmmm ... wanneer de opmerkingen online komen ... :-)
Ik gebruik Bootstrap en jQuery. Voor jQuery is de textarea met resterende karakters vele malen gedocumenteerd. Veel oplossingen stellen voor om het voorbeeld van Twitter te volgen. Dit betekent dat u de volledige tekst blijft tonen, zelfs als het aantal karakters het toegestane aantal overschrijdt. Als het aantal karakters groter is dan het toegestane aantal tonen we het aantal karakters in de kleur rood. Geen probleem, ik zal het uitvoeren.
Het opgeven van het maximum aantal tekens op één plaats
Ik wil dat de widget universeel is, dus geen hardgecodeerde waarden erin. jQuery wordt gebruikt om het werkelijke aantal karakters te tellen, maar hoe weet jQuery wat het maximaal toegestane aantal karakters is? We kunnen constanten definiëren en deze overal gebruiken, maar het is flexibeler om extra HTML5 data-attributen te implementeren voor de textarea:
- gegevens-telling-min
- data-chartcount-max
We voegen een extra element toe, dat het resterende aantal karakters onder de textarea laat zien. De data-attrbuten kunnen worden gerefereerd aan jQuery code en worden gebruikt om het aantal resterende karakters te berekenen.
Uitvoering
Ook hier kijken we eerst naar de WTForms code voor de TextAreaField. Zie ook een vorig bericht. Ik heb deze code gekopieerd en in deze gewijzigd:
class TextAreaWithCounterWidget(object):
"""
Renders a multi-line text area.
`rows` and `cols` ought to be passed as keyword args when rendering.
"""
def __init__(self):
pass
def __call__(self, field, **kwargs):
fname = 'TextAreaWithCounterWidget - __call__'
kwargs.setdefault('id', field.id)
if 'required' not in kwargs and 'required' in getattr(field, 'flags', []):
kwargs['required'] = True
return HTMLString('<textarea %s>%s</textarea>' % (
html_params(name=field.name, **kwargs),
escape(text_type(field._value()), quote=False)
) + '<span class="" id="' + field.id + '-char-count-num' + '"></span>' )
class TextAreaWithCounterField(StringField):
"""
This field represents an HTML ``<textarea>`` and can be used to take
multi-line input.
"""
widget = TextAreaWithCounterWidget()
Dan gebruik ik het als volgt:
def strip_whitespace(s):
if isinstance(s, str):
s = s.strip()
return s
class ContentItemCommentForm(FlaskForm):
message = TextAreaWithCounterField(_l('Your message'),
render_kw={'data-char-count-min': 0, 'data-char-count-max': 1000, 'rows': 4},
validators=[ InputRequired(), Length(min=6, max=1000) ],
filters=[ strip_whitespace ] )
submit = SubmitField(_l('Add'))
Soms zijn de dingen gemakkelijker dan verwacht. We kunnen eenvoudigweg onze nieuwe parameters aan het formulier toevoegen met behulp van render_kw. Vervolgens worden ze in de widget als attributen doorgegeven aan de textarea. Als we willen kunnen we deze parameters ook benaderen in de widget met behulp van kwargs. We kunnen ze noemen:
data_char_count_max = kwargs['data-char-count-max']
We kunnen ze ook uit kwargs laten knallen, dat wil zeggen lezen en verwijderen. Dan verschijnen ze niet als attributen in de textarea:
char_count_max = kwargs.pop('char_count_max', None)
Maar het is niet nodig om dit hier te gebruiken. Binnen de widget hebben we ook toegang tot de Lengte validator waarden, maar ook hier is dit niet nodig.
Ik heb ook het aantal rijen in render_kw opgegeven. Dit wordt doorgegeven aan het veld textarea . De gegenereerde HTML is toegevoegd met het extra element dat het resterende aantal karakters aangeeft. Het id van dit element is opgebouwd uit het id van textarea :
field.id + '-char-count-num'
De filterstrip_witte ruimte wordt genoemd om de voorloop- en achterloopwitte ruimte te trimmen.
De code jQuery is niet zo moeilijk. Ik gebruik Bootstrap, de klassen veranderen de kleur en stellen de vulling in:
function update_character_count(textarea_id){
var char_count_num_id = textarea_id + '-char-count-num';
if( ($("#" + textarea_id).length == 0) || ($("#" + char_count_num_id).length == 0) ){
// must exist
return;
}
var char_count_min = parseInt( $('#' + textarea_id).data('char-count-min'), 10 );
var char_count_max = parseInt( $('#' + textarea_id).data('char-count-max'), 10 );
var remaining = char_count_max - $('#' + textarea_id).val().length;
$('#' + char_count_num_id).html( '' + remaining );
if(remaining >= 0){
$('#' + char_count_num_id).removeClass('text-danger');
$('#' + char_count_num_id).addClass('pl-2 text-secondary');
}else{
$('#' + char_count_num_id).removeClass('text-secondary');
$('#' + char_count_num_id).addClass('pl-2 text-danger');
}
}
en:
$(document).ready(function(){
...
// comment form: character count
update_character_count('comment_form-message');
$('#comment_form-message').on('change input paste', function(event){
update_character_count('comment_form-message');
});
}
Testen en mismatch in karaktertelling
Nu kunnen we beginnen met het testen van de nieuwe TextAreaWithCounterField. Alles zag er goed uit tot ik in nieuwe lijnen begon. De lengte werd correct gerapporteerd door jQuery wat betekent dat ze als één enkel karakter werden geteld. Maar de WTForms validator zei dat de maximale lengte werd overschreden. Debugging tijd weer, in de widget I afgedrukt de tekens ontvangen door WTForms:
message = field.data
if message is None:
current_app.logger.debug(fname + ': message is None')
else:
current_app.logger.debug(fname + ': message = {}, len(message) = {}, list(message) = {}'.format(message, len(message), list(message)))
Dit gaf me het volgende resultaat:
len(message) = 4, list(message) = ['a', '\r', '\n', 'b']
jQuery telt \r\n als één teken, maar WTForms telt het als twee tekens. Graven in WTForms code, validators.py, zien we dat het gebruik van de Python len functie om de lengte te bepalen:
l = field.data and len(field.data) or 0
Begrijpelijk, maar in dit geval is het verkeerd! Wat moet ik doen? Ik wilde de standaard WTForms validatiefunctie niet opheffen. Gelukkig hebben we het filterveld dat voor (!) validatie wordt aangeroepen. Ik gebruikte al strip_witte ruimte en voegde een nieuw filter toe:
def compress_newline(s):
if isinstance(s, str):
s = s.replace("\r\n", "\n")
s = re.sub(r'\n+', '\n', s)
return s
Dit filter vervangt \r\n door een enkele \n. Bovendien vervangt het meerdere \n -tekens door een enkele \n. Dit laatste is slechts een zeer primitieve bescherming tegen onvermijdelijk verkeerd (en gek) indienen. De filterlijn wordt dan:
filters=[ strip_whitespace, compress_newline ] )
Nu werkte het zoals bedoeld.
Samenvatting
Het gebruik van render_kw is een eenvoudige manier om parameters door te geven aan de widget. Ik heb op het internet gezocht naar het \r\n probleem met de WTForms TextAreaField maar kon geen referenties vinden. Zeg me alsjeblieft niet dat ik de enige ben. Het probleem wordt veroorzaakt door een mismatch tussen Windows en Linux systemen. Ook kost het coderen van Javascript/jQuery tijd als u dit niet altijd doet.
Links / credits
How to specify rows and columns of a <textarea > tag using wtforms
https://stackoverflow.com/questions/4930747/how-to-specify-rows-and-columns-of-a-textarea-tag-using-wtforms
New line in text area
https://stackoverflow.com/questions/8627902/new-line-in-text-area
Using data attributes
https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes
WTForms - Fields
https://wtforms.readthedocs.io/en/stable/fields.html
WTForms - Widgets
https://wtforms.readthedocs.io/en/stable/widgets.html
Laat een reactie achter
Reageer anoniem of log in om commentaar te geven.
Opmerkingen (1)
Laat een antwoord achter
Antwoord anoniem of log in om te antwoorden.
Thanks for sharing! I wrote long comment but token has expired since I was reading the other tabs and I lost my comment :/. In short - great article and real demo, which I can see here, while typing this one again.
Recent
- Don't Repeat Yourself (DRY) met Jinja2
- SQLAlchemy, PostgreSQL, maximum aantal rijen per user
- Toon de waarden in SQLAlchemy dynamische filters
- Veilige gegevensoverdracht met Public Key versleuteling en pyNaCl
- rqlite: een alternatief voor SQLite met hoge beschikbaarheid en distributed
- Moet ik mijn Docker Swarm migreren naar Kubernetes?
Meest bekeken
- Met behulp van Python's pyOpenSSL om SSL-certificaten die van een host zijn gedownload te controleren
- Gebruik van UUIDs in plaats van Integer Autoincrement Primary Keys met SQLAlchemy en MariaDb
- Maak verbinding met een dienst op een Docker host vanaf een Docker container
- SQLAlchemy: Gebruik van Cascade Deletes om verwante objecten te verwijderen
- PyInstaller en Cython gebruiken om een Python executable te maken
- Flask RESTful API verzoekparametervalidatie met Marshmallow-schema's