Un textarea avec un compteur de caractères widget pour Flask, WTForms et Bootstrap
Ajouter un WTForms textarea widget semble facile mais les différences entre Linux et Windows causent des problèmes inattendus.
J'espérais vous dire aujourd'hui que vous pouviez dès à présent commenter les articles de ce site web. Cela aurait signifié que j'aurais achevé la première mise en œuvre du système de commentaires. Malheureusement, je suis tombé sur quelques problèmes, oui bien sûr, je suis un programmeur, et l'un d'entre eux concernait le TextAreaField.
Je voulais juste une version simple et étendue du WTForm TextAreaField, il suffit d'ajouter un champ de compteur de caractères sous le textarea et c'est tout. Je pensais que cela prendrait quelques heures, mais je me suis trompé, j'ai résolu le problème et j'ai pensé à vous en faire part. Faites-moi savoir ce que vous en pensez ... hmmm ... lorsque les commentaires seront en ligne ... :-)
J'utilise Bootstrap et jQuery. Pour la jQuery , la textarea avec les caractères restants a été documentée à de nombreuses reprises. De nombreuses solutions suggèrent de suivre l'exemple de Twitter. Cela signifie que vous continuez à afficher le texte intégral même si le nombre de caractères dépasse le nombre autorisé. Si le nombre de caractères dépasse le nombre autorisé, nous indiquons le nombre de caractères dans la couleur rouge. Pas de problème, je vais mettre en œuvre.
Préciser le nombre maximum de caractères à un endroit
Je veux que le widget soit universel, donc pas de valeurs codées en dur dedans. jQuery est utilisé pour compter le nombre réel de caractères mais comment jQuery sait-il quel est le maximum autorisé ? Nous pouvons définir des constantes et les utiliser partout, mais il est plus souple de mettre en œuvre des attributs de données supplémentaires HTML5 pour le textarea :
- data-char-count-min
- data-char-count-max
Nous ajoutons un élément supplémentaire, indiquant le nombre de caractères restants sous le code textarea. Les données peuvent être référencées par le code jQuery et utilisées pour calculer le nombre de caractères restants.
Mise en œuvre
Là encore, nous examinons d'abord le code WTForms pour le TextAreaField. Voir aussi un article précédent. J'ai copié ce code et je l'ai modifié en ceci :
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()
Ensuite, je l'utilise comme suit :
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'))
Parfois, les choses sont plus faciles que prévu. Nous pouvons simplement ajouter nos nouveaux paramètres au formulaire en utilisant render_kw. Ensuite, dans le widget , ils sont passés comme attributs au textarea. Si nous le souhaitons, nous pouvons également accéder à ces paramètres dans le widget en utilisant le kwargs. Nous pouvons les référencer comme :
data_char_count_max = kwargs['data-char-count-max']
Nous pouvons également les extraire de kwargs, ce qui signifie lire et supprimer. Ils n'apparaîtront alors pas en tant qu'attributs dans le textarea :
char_count_max = kwargs.pop('char_count_max', None)
Mais il n'est pas nécessaire de l'utiliser ici. Dans le widget , nous pouvons également accéder aux valeurs du validateur de longueur, mais là encore, ce n'est pas nécessaire.
J'ai également spécifié le nombre de lignes dans render_kw. Il est transmis au champ textarea . Le HTML généré est ajouté avec l'élément supplémentaire indiquant le nombre de caractères restants. L'identifiant de cet élément est construit à partir de l'identifiant textarea :
field.id + '-char-count-num'
Le filtre strip_whitespace est appelé à couper l'espace blanc avant et arrière.
Le code jQuery n'est pas si difficile. J'utilise Bootstrap, les classes changent la couleur et fixent le rembourrage :
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');
}
}
et :
$(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');
});
}
Test et décalage dans le décompte des caractères
Nous pouvons maintenant commencer à tester le nouveau TextAreaWithCounterField. Tout avait l'air bien jusqu'à ce que je commence à entrer dans les nouvelles lignes. La longueur a été déclarée correcte par jQuery , ce qui signifie qu'ils ont été comptés comme un seul caractère. Mais le validateur WTForms a déclaré que la longueur maximale était dépassée. Encore une fois, au débogage, dans le widget , j'ai imprimé les caractères reçus par le 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)))
Cela m'a donné le résultat suivant :
len(message) = 4, list(message) = ['a', '\r', '\n', 'b']
jQuery compte \r\n comme un seul caractère mais WTForms le compte comme deux caractères. En creusant dans le code WTForms , validators.py, on voit qu'il utilise la fonction len Python pour déterminer la longueur :
l = field.data and len(field.data) or 0
Compréhensible mais dans ce cas, c'est faux ! Que faire ? Je ne voulais pas passer outre la fonction de validation par défaut WTForms . Heureusement, nous avons le champ de filtres qui sont appelés avant ( !) la validation. J'utilisais déjà strip_whitespace et j'ai ajouté un nouveau filtre :
def compress_newline(s):
if isinstance(s, str):
s = s.replace("\r\n", "\n")
s = re.sub(r'\n+', '\n', s)
return s
Ce filtre remplace \r\n par un seul \n. En outre, il remplace plusieurs caractères \n par un seul \n. Ce dernier n'est qu'une protection très primitive contre les inévitables soumissions erronées (et folles). La ligne de filtrage devient alors :
filters=[ strip_whitespace, compress_newline ] )
Maintenant, cela a fonctionné comme prévu.
Résumé
L'utilisation de render_kw est un moyen facile de passer des paramètres au widget. J'ai cherché sur internet le problème \r\n avec le WTForms TextAreaField mais je n'ai trouvé aucune référence. Ne me dites pas que je suis le seul. Le problème est causé par une inadéquation entre les systèmes Windows et Linux . En outre, le codage Javascript/jQuery prend du temps si vous ne le faites pas tout le temps.
Liens / crédits
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
Laissez un commentaire
Commentez anonymement ou connectez-vous pour commenter.
Commentaires (1)
Laissez une réponse
Répondez de manière anonyme ou connectez-vous pour répondre.
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.
Récent
- Don't Repeat Yourself (DRY) avec Jinja2
- SQLAlchemy, PostgreSQL, nombre maximal de lignes par user
- Afficher les valeurs des filtres dynamiques SQLAlchemy
- Transfert de données sécurisé grâce au cryptage à Public Key et à pyNaCl
- rqlite : une alternative à haute disponibilité et dist distribuée SQLite
- Dois-je migrer mon Docker Swarm vers Kubernetes ?
Les plus consultés
- Utilisation des Python's pyOpenSSL pour vérifier les certificats SSL téléchargés d'un hôte
- Utiliser UUIDs au lieu de Integer Autoincrement Primary Keys avec SQLAlchemy et MariaDb
- Connexion à un service sur un hôte Docker à partir d'un conteneur Docker
- SQLAlchemy : Utilisation de Cascade Deletes pour supprimer des objets connexes
- Utiliser PyInstaller et Cython pour créer un exécutable Python
- Flask RESTful API validation des paramètres de la requête avec les schémas Marshmallow