如何将侦听器附加到 Django 管理中的日期选择?



我有一个模型的 Django 管理页面,其中包含一个名为"date"的DateField

# models.py
class Article(models.Model):
date = models.DateField(
help_text="Article publication date"
)
# admin.py
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
change_form_template = "article_extension.html"

我已经扩展了此模型的管理员添加/更改模板,以包含选择要在选择日期时执行的 JavaScript。

在"管理员添加/更改"页面中,此字段的默认小组件是带有日历选择器的文本元素。 页面源代码没有显示选择器的任何代码,所以我唯一可以附加 JavaScript 的是输入框:

<input type="text" ... id="id_date">

在模板的 JavaScript 中,我在此输入框中为"输入"事件附加了一个 EventListener:

# article_extension.html
{% extends "admin/change_form.html" %}
{% block extrahead %}
block.super 
<script type="text/javascript">
window.onload = function() {
var dateInput = document.getElementById("id_date");
dateInput.addEventListener('input', dateListener);
function dateListener() {
console.log("date event recognized");
}
}
</script>
{% endblock %}

然后,当我从日历选择器中选择日期时,输入框的内容会更改以反映所选日期,但事件侦听器不会触发。 但是,如果我在输入框中手动键入内容,则会触发事件侦听器,并且控制台中会显示"识别事件日期"。 显然,当日历用于将内容放入输入框中时,这不会被识别为"输入"。

如何将 EvenListener 附加到输入框或日历,以便在从日历中选择日期时触发?

为什么您当前的解决方案不起作用?

这没有像你预期的那样工作的原因是 django-admin 日期时间字段已经由整个 javascript 负载运行。它不是"本机"日期时间输入。

每当用户交互更改元素的值时,都会触发input事件。如果另一个 javascript 函数以编程方式更改了该值,则不会触发输入事件。

当您从日历中选择一个日期时,就会发生这种情况。输入的值已更新,但不会触发输入事件。

我该如何解决它?

我想到了 3 种解决方案:

选项 1

使用其他小部件。那里有一大堆日期选择器,我相信其中一些会有一个简单的 API 来添加值更改时的回调。我不知道一个好的,但谷歌将是你的朋友。

选项 2 和 3 涉及使用当前小部件。如果我们在代码中挖掘一点,我们会看到加载了 2 个对此小部件很重要的 js 文件:

  • django/contrib/admin/static/admin/js/calendar.js
  • django/contrib/admin/static/admin/js/admin/DateTimeShortcuts.js

经过一番挖掘后,您会发现DateTimeShortcuts中有一个名为handleCalendarCallback的函数。这是设置值的函数。

handleCalendarCallback: function(num) {
var format = get_format('DATE_INPUT_FORMATS')[0];
// the format needs to be escaped a little
format = format.replace('\', '\\')
.replace('r', '\r')
.replace('n', '\n')
.replace('t', '\t')
.replace("'", "\'");
return function(y, m, d) {
DateTimeShortcuts.calendarInputs[num].value = new Date(y, m - 1, d).strftime(format);
DateTimeShortcuts.calendarInputs[num].focus();
document.getElementById(DateTimeShortcuts.calendarDivName1 + num).style.display = 'none';
};
},

选项 2

制作您自己的小部件,该小部件使用上述文件的新版本,并稍微编辑了一个函数。它看起来像这样:

class AdminDateWidget(forms.DateInput):
class Media:
js = [
'admin/js/calendar.js',
'admin/js/admin/MyVersionOfDateTimeShortcuts.js',
]
def __init__(self, attrs=None, format=None):
attrs = {'class': 'vDateField', 'size': '10', **(attrs or {})}
super().__init__(attrs=attrs, format=format)

然后,您可以将该小部件用于相关字段,然后编辑后的代码将运行。不幸的是,由于该文件是静态文件而不是模板,因此您不能使用继承,因此您必须复制并粘贴整个内容,然后编辑该函数。

选项 3

您会注意到,在更新值后,将调用.focus(),因此我们可以利用它并将事件侦听器附加到焦点事件。当然,我们只希望在值实际更改时才会发生某些事情,因此我们需要执行以下操作:

var dateValue;
window.onload = function() {
var dateInput = document.getElementById("id_date");
dateInput.addEventListener('focus', dateListener);
function dateListener(event) {
if (dateValue === event.target.value) {
return
}
dateValue = event.target.value;
// put the stuff you actually want to happen here
}
}

最新更新