Добавление 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())
Другие вопросы по тегам