textarea со счетчиком символов widget для Flask, WTForms и Bootstrap
Добавление WTForms textarea widget выглядит просто, но различия между Linux и Windows вызывают неожиданные проблемы.
Я надеялся сказать вам сегодня, что вы можете прокомментировать записи в блоге этого сайта уже сейчас. Это означало бы, что я завершил первое внедрение системы комментариев. К сожалению, я наткнулся на некоторые проблемы, да, конечно, я программист, и одна из них связана с TextAreaField.
Мне просто нужна была простая расширенная версия WTForm TextAreaField, просто добавьте поле счетчика символов под textarea и все. Я думал, что это займет несколько часов, но был совершенно неправ, но я как-то решил эту проблему и подумал поделиться этим с тобой. Сообщите мне о своих мыслях... хм... когда комментарии появятся в сети... :-)
Я использую Bootstrap и jQuery. Для jQuery textarea с оставшимися символами был документирован много раз. Многие решения предлагают следовать примеру Twitter. Это означает, что вы продолжаете показывать полный текст, даже если количество символов превышает допустимое. Если количество символов превышает допустимое, то количество символов показывается красным цветом. Нет проблем, я выполню.
Указание максимального количества символов в одном месте
Я хочу, чтобы widget был универсальным, поэтому никаких жестко закодированных значений в нем нет. jQuery используется для подсчета фактического количества символов, но как jQuery узнает, что такое максимально допустимое количество? Мы можем определить константы и использовать их везде, но более гибко реализовывать дополнительные атрибуты данных HTML5 для textarea:
- дата-чар-мин
- data-char-count-max
Мы добавляем дополнительный элемент, показывающий оставшееся количество символов под кодом textarea. На data-attrbutes можно ссылаться кодом jQuery и использовать его для вычисления количества оставшихся символов.
Реализация
Снова мы смотрим на код WTForms для TextAreaField. См. также предыдущий пост. Я скопировал этот код и модифицировал его в этот:
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()
Тогда я использую это следующим образом:
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'))
Иногда все намного проще, чем ожидалось. Мы можем просто добавить наши новые параметры в форму, используя render_kw. Затем в widget они передаются как атрибуты textarea. Если мы хотим, мы также можем получить доступ к этим параметрам в widget , используя kwargs. Мы можем назвать их:
data_char_count_max = kwargs['data-char-count-max']
Мы также можем вывести их из kwargs, имея в виду чтение и удаление. Тогда они не будут отображаться как атрибуты в textarea:
char_count_max = kwargs.pop('char_count_max', None)
Но здесь нет необходимости использовать это. Внутри widget мы также можем получить доступ к значениям валидатора Length, но опять же, здесь в этом нет необходимости.
Я также указал количество строк в render_kw. Это передается в поле textarea . Сгенерированный HTML добавляется с дополнительным элементом, показывающим оставшееся количество символов. Ид этого элемента строится из идентификатора textarea :
field.id + '-char-count-num'
Фильтрующая полоса_whitespace вызывается для обрезки ведущего и заднего белого пространства.
Код jQuery не так уж и сложен. Я использую Bootstrap, классы меняют цвет и устанавливают подкладку:
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');
}
}
и..:
$(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');
});
}
Тестирование и несовпадение по количеству символов
Теперь мы можем приступить к тестированию нового TextAreaWithCounterField. Все выглядело прекрасно, пока я не начал выходить на новые линии. Длина была сообщена правильно jQuery , что означает, что они считались как один символ. Но валидатор WTForms сказал, что максимальная длина превышена. Снова время отладки, в widget я распечатал символы, полученные 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)))
Это дало мне следующий результат:
len(message) = 4, list(message) = ['a', '\r', '\n', 'b']
jQuery считает \r\n как один символ, но WTForms считает как два символа. Войдя в код WTForms , validators.py, мы видим, что он использует функцию Python len для определения длины:
l = field.data and len(field.data) or 0
Понятно, но в данном случае это неправильно! Что делать? Я не хотел переопределять функцию проверки WTForms по умолчанию. К счастью, у нас есть поле фильтров, которое вызывается перед (!) проверкой. Я уже использовал strip_whitespace и добавил новый фильтр:
def compress_newline(s):
if isinstance(s, str):
s = s.replace("\r\n", "\n")
s = re.sub(r'\n+', '\n', s)
return s
Данный фильтр заменяет \r\n на один \n. Кроме того, он заменяет несколько символов \n на один \n. Последнее является просто очень примитивной защитой от неизбежных неправильных (и сумасшедших) подает. Тогда линия фильтров становится:
filters=[ strip_whitespace, compress_newline ] )
Теперь все сработало, как и планировалось.
Резюме
Использование render_kw является простым способом передачи параметров widget. Я искал в интернете проблему \r\n с WTForms TextAreaField , но не смог найти никаких ссылок. Пожалуйста, не говорите мне, что я единственный. Проблема вызвана несовпадением систем Windows и Linux . Кроме того, кодирование Javascript/jQuery требует времени, если вы не делаете этого постоянно.
Ссылки / кредиты
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
Оставить комментарий
Комментируйте анонимно или войдите в систему, чтобы прокомментировать.
Комментарии (1)
Оставьте ответ
Ответьте анонимно или войдите в систему, чтобы ответить.
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.
Недавний
- Скрытие первичных ключей базы данных UUID вашего веб-приложения
- Don't Repeat Yourself (DRY) с Jinja2
- SQLAlchemy, PostgreSQL, максимальное количество строк для user
- Показать значения в динамических фильтрах SQLAlchemy
- Безопасная передача данных с помощью шифрования Public Key и pyNaCl
- rqlite: альтернатива dist с высокой степенью готовности и SQLite
Большинство просмотренных
- Используя Python pyOpenSSL для проверки SSL-сертификатов, загруженных с хоста
- Использование UUID вместо Integer Autoincrement Primary Keys с SQLAlchemy и MariaDb
- Подключение к службе на хосте Docker из контейнера Docker
- Использование PyInstaller и Cython для создания исполняемого файла Python
- SQLAlchemy: Использование Cascade Deletes для удаления связанных объектов
- Flask Удовлетворительный запрос API проверка параметров запроса с помощью схем Маршмэллоу