Добавление Javascript в пользовательские виджеты
Этот вопрос связан с
Django: лучший способ добавить Javascript в пользовательские виджеты
Но это не то же самое.
Оригинальный вопрос спрашивает, как добавить поддерживающий JavaScript в пользовательский виджет Django, и ответ заключается в использовании forms.Media
, но это решение не работает для меня. Мой пример таков:
Виджет при визуализации в форме создает строку, которая выглядит (пример игрушки) следующим образом:
<div id="some-generated-id">Text here</div>
Теперь я также хочу добавить к выводу еще одну строку, похожую на эту:
<script>
$('#some-generated-id').datetimepicker(some-generated-options)
</script>
Первоначальная идея заключается в том, что, когда виджет отображается, оба div
а также script
получить, но это не работает. Проблема в том, что структура html
Документ выглядит так:
-body
- my widget
- my widget's javascript
-script
-calls to static files (jQuery, datetimepicker,...)
В то время, когда код JavaScript виджета загружен в браузер, js-файлы jQuery и datetimepicker еще не загружены (они загружаются в конце документа).
Я не могу сделать это используя Media
, поскольку параметры и идентификатор, которые я генерирую, жизненно важны для функции. Каков наилучший способ решить эту проблему?
1 ответ
Из документов:
Порядок, в котором активы вставляются в DOM, часто важен. Например, у вас может быть сценарий, зависящий от jQuery. Следовательно, объединение объектов Media пытается сохранить относительный порядок, в котором активы определены в каждом классе Media.
Рассмотрим этот пример:
class FooWidget(forms.TextInput):
class Media:
js = ('foo.js',)
class BarWidget(forms.TextInput):
class Media:
js = ('bar.js',)
class SomeForm(forms.Form):
field1 = forms.CharField(widget=BarWidget)
field2 = forms.CharField(widget=FooWidget)
def __init__(self, *args, **kwargs):
super(SearchForm, self).__init__(*args, **kwargs)
Теперь, когда ты звонишь form.media
скрипты будут отображаться следующим образом:
<script type="text/javascript" src="/static/bar.js"></script>
<script type="text/javascript" src="/static/foo.js"></script>
Почему bar.js
сделать до foo.js
? Поскольку django отображает их на основе порядка, в котором они были вызваны в форме, а не порядка, в котором были определены классы. Если вы хотите изменить порядок в этом примере, просто поменяйте местами положениеfield1
а также field2
в SomeForm
.
Как это поможет вам с jQuery? Вы можете отобразить свой jQuery CDN-скрипт через свой собственный виджет:
class FooWidget(forms.TextInput):
class Media:
js = ('https://code.jquery.com/jquery-3.4.1.js', 'foo.js',)
class BarWidget(forms.TextInput):
class Media:
js = ('https://code.jquery.com/jquery-3.4.1.js', 'bar.js',)
Теперь твой form.media
будет выглядеть так:
<script src="https://code.jquery.com/jquery-3.4.1.js"></script>
<script type="text/javascript" src="/static/bar.js"></script>
<script type="text/javascript" src="/static/foo.js"></script>
Обратите внимание, как /static/
не был добавлен в CDN jQuery? Это потому, что.media
атрибут проверяет, содержат ли указанные пути к файлам http
или https
, и только добавляет ваш STATIC_URL
установка относительных путей к файлам.
Также обратите внимание, что повторяющиеся имена файлов автоматически удаляются, поэтому я бы сказал, что рекомендуется включать https://code.jquery.com/jquery-3.4.1.js
в начале каждого виджета, который этого требует. Таким образом, независимо от того, в каком порядке вы их отображаете, сценарий jQuery всегда будет появляться перед файлами, которым он нужен.
Кстати, я был бы осторожен при включении чисел в имена файлов. Что касается Django 2.2, то при попытке упорядочить скрипты возникает ошибка.
Например:
class FooWidget(forms.TextInput):
class Media:
js = ('foo1.js', 'foo2.js',)
class BarWidget(forms.TextInput):
class Media:
js = ('bar1.js', 'bar13.js',)
class SomeForm(forms.Form):
field1 = forms.CharField(widget=BarWidget)
field2 = forms.CharField(widget=FooWidget)
def __init__(self, *args, **kwargs):
super(SearchForm, self).__init__(*args, **kwargs)
Будет выглядеть так:
<script type="text/javascript" src="/static/bar1.js"></script>
<script type="text/javascript" src="/static/foo1.js"></script>
<script type="text/javascript" src="/static/bar13.js"></script>
<script type="text/javascript" src="/static/foo2.js"></script>
Я пробовал различные комбинации имен, содержащих числа, и не могу следовать логике, поэтому предполагаю, что это ошибка.
Поскольку JavaScript является встроенным скриптом, вам нужно будет использовать собственный DOMContentLoaded
, чтобы дождаться загрузки jQuery.
<script>
window.addEventListener('DOMContentLoaded', function() {
(function($) {
$('#some-generated-id').datetimepicker(some-generated-options);
})(jQuery);
});
</script>
В качестве альтернативы, если вы можете поместить свой код во внешний файл сценария, вы можете использовать defer
атрибут script
тег.
<script src="myfile.js" defer="defer"></script>
См. MDN.
Вы хотите выполнить какой-нибудь скрипт плагина для добавленного div. Вам нужно добавить класс к своему элементу и связать настраиваемое событие с классом. Которая будет выполнять функцию или сценарий вашего желания.
Чтобы связать настраиваемое событие с динамически добавляемым узлом, см. Приведенный ниже код.
<!DOCTYPE html>
<html>
<head>
<title>adding javascript to custom widgets</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</head>
<body>
<section class="team">
<div class="container">
<div class="row">
<div class="container maxwidth">
<button data-tab="tab1" class="active">AA<span></span></button>
</div>
<div class="maincontent"></div>
</div>
</div>
</section>
<script>
$(function(){
$(".active").click(function(){
$(".maincontent").append("<div class='scroll'><h2>Hello</h2><div style='background:red; height:500px;'></div></div>")
$(".maincontent").find(".scroll").trigger('dynload');
});
$('.container').on('dynload', '.scroll', function(){
console.log("Append event fired");
// Additinal Script resource you want to load for plugin
$.getScript("Custom_Scrollbar.min.js") //script URL with abosolute path
.done(function() {
// Script loaded successfully calling of function
$(".scroll").mCustomScrollbar({
});
})
.fail(function() {
// Give you error when script not loaded in browser
console.log('Script file not loaded');
})
})
})
</script>
</body>
</html>
Надеюсь, это поможет!
Вы должны инициировать JS как:
<script type="text/javascript">
$( document ).ready(function() {
var some-generated-options = {};
$('#some-generated-id').datetimepicker(some-generated-options);
});
</script>
Как и вы, я создал собственный виджет, который выглядит так:
class DaysInput(TextInput):
def render(self, name, value, attrs=None):
result = super(DaysInput, self).render(name, value, attrs)
return u"""
%s
<script type="text/javascript">
$( document ).ready(function() {
$('a.id_days').click(function(e){
e.preventDefault();
$('input#id_days').val($(e.currentTarget).attr('attr-value'));
});
});
</script>
<a href="#" class="id_days" attr-value='1'>1d</a>
<a href="#" class="id_days" attr-value='2'>2d</a>
<a href="#" class="id_days" attr-value='3'>3d</a>
""" % result
class TestForm(forms.ModelForm):
days = forms.IntegerField(widget=DaysInput())