Title: Wagtail streamfield block: injecting jQuery into the Wagtail Admin
Tags: javascript;jquery;django;wagtail
Question: I'm creating a block for Wagtail streamfields which shows real-time code syntax highlighting using the Prism JS library. It is partially working; when I have a language selected and code in place on an existing page, the code shows up, highlighted as expected:



However, when I try to create a new Wagtail page with a new Code Block, I get a JavaScript error:



It seems like the library is being delayed on loading when there isn't content there for it to highlight. I've tried both ```$(document).ready``` and ```$(window).load```, but this is a different situation since the template containing the Prism library is loaded dynamically when you click the "Code Snippet" button. Here's the ext of the error I'm getting:

```VM730:2 Uncaught ReferenceError: Prism is not defined
    at prism_repaint (eval at globalEval (jquery-2.2.1.js:338), <anonymous&gt;:2:5)
    at populate_target_code (eval at globalEval (jquery-2.2.1.js:338), <anonymous&gt;:9:5)
    at HTMLDocument.eval (eval at globalEval (jquery-2.2.1.js:338), <anonymous&gt;:2:29)
    at fire (jquery-2.2.1.js:3182)
    at Object.add [as done] (jquery-2.2.1.js:3241)
    at jQuery.fn.init.jQuery.fn.ready (jquery-2.2.1.js:3491)
    at eval (eval at globalEval (jquery-2.2.1.js:338), <anonymous&gt;:1:13)
    at eval (<anonymous&gt;)
    at Function.globalEval (jquery-2.2.1.js:338)
    at domManip (jquery-2.2.1.js:5285)
```

Here's the Django Wagtail code I'm using for the Wagtail block:

```class CodeBlock(StructBlock):
    """
    Code Highlighting Block
    """

    WCB_LANGUAGES = get_language_choices()

    language = ChoiceBlock(choices=WCB_LANGUAGES)
    code = TextBlock()

    class Meta:
        icon = 'code'
        template = 'wagtailcodeblock/code_block.html'
        form_template = 'wagtailcodeblock/code_block_form.html'
```

...and the code for the form template for the Wagtail admin:

```<script&gt;
function prism_repaint(target_class) {
    Prism.highlightElement($(target_class)[0]);
}
function populate_target_code(label) {
    target_class = '#target-element-' + label;
    code_text = $('#' + label).val();
    $(target_class).text(code_text);
    prism_repaint(target_class);
}
</script&gt;
{% with prism_version="1.6.0" %}
    {% block extra_css %}
    <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/{{ prism_version }}/themes/prism.min.css" rel="stylesheet" /&gt;
    {% endblock extra_css %}
    {% block extra_js %}
    <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/{{ prism_version }}/prism.min.js"&gt;</script&gt;
    {% endblock extra_js %}
    <div class="{{ classname }}"&gt;
        {% if help_text %}
            <div class="object-help help"&gt;{{ help_text }}</div&gt;
        {% endif %}

        <ul class="fields"&gt;
            {% for child in children.values %}
                <li{% if child.block.required %} class="required"{% endif %}&gt;
                    {% if child.block.label %}
                        <label{% if child.id_for_label %} for="{{ child.id_for_label }}"{% endif %}&gt;{{ child.block.label }}:</label&gt;
                    {% endif %}
                    {{ child.render_form }}
                </li&gt;
                {% if child.block.label == "Language" %}
                    {# As we loop through, load each language dialect #}
                    {% for language_choice, language_name in child.block.field.choices %}
                        {% if language_choice|length %}
                            <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/{{ prism_version }}/components/prism-{{ language_choice }}.min.js"&gt;</script&gt;
                        {% endif %}
                    {% endfor %}

                    <script&gt;
                        $(document).ready(function() {
                            // Set initial language class on load
                            target_class = '#target-element-{{ child.id_for_label }}'.replace('language', 'code');
                            {% if child.id_for_label|length %}
                                $(target_class).addClass('language-' + $('#{{ child.id_for_label }}').val());
                            {% endif %}
                            // Change language being highlighted when dropdown is changed
                            $('#{{ child.id_for_label }}').bind('input propertychange', function() {
                                language_class = 'language-' + $('#{{ child.id_for_label }}').val();
                                $(target_class).removeClass().addClass(language_class);
                                prism_repaint(target_class);
                            });
                        });
                    </script&gt;
                {% endif %}
                {% if child.block.label == "Code" %}
                    <script&gt;
                        $(document).ready(function() {
                            populate_target_code('{{ child.id_for_label }}');
                            $('#{{ child.id_for_label }}').bind('input propertychange', function() {
                                populate_target_code('{{ child.id_for_label }}');
                            });
                        });
                    </script&gt;
                    <li&gt;
                        <pre&gt;<code id="target-element-{{ child.id_for_label }}"&gt;</code&gt;</pre&gt;
                    </li&gt;
                {% endif %}
            {% endfor %}
        </ul&gt;
    </div&gt;
{% endwith %}
```

I've verified that the CSS and JS are properly being injected into the code using the ```extra_css``` and ```extra_js``` template blocks. I've also attempted using the ".getScript" function, but haven't had any luck. I'm by no means a JavaScript expert, so any help would be appreciated. The entire source for the project can be found on GitHub, in case that helps: 

https://github.com/FlipperPA/wagtailcodeblock

Any help tracking fixing this would be appreciated! Thanks for your time.


Here is the accepted answer: StreamField blocks borrow Django's form media API to associate JS/CSS files with blocks - this is the recommended way to import JS/CSS libraries. Your code would become:

```from django import forms

class CodeBlock(StructBlock):
    """
    Code Highlighting Block
    """

    # ...

    @property
    def media(self):
        prism_version = "1.6.0"
        return forms.Media(
            js=["https://cdnjs.cloudflare.com/ajax/libs/prism/%s/prism.min.js" % prism_version],
            css={'all': ["https://cdnjs.cloudflare.com/ajax/libs/prism/%s/themes/prism.min.css" % prism_version]}
        )
```

Defining the JS/CSS in this way means that the imports will appear on any edit page that could potentially include your block, rather than just being inserted when your block is added.



{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 1, 'CommentCount': 0, 'ContentLicense': 'CC BY-SA 3.0', 'CreationDate': '2017:00:04/22/17', 'FavoriteCount': 2.0, 'PostTypeId': 1, 'Tags': '<javascript><jquery><django><wagtail>', 'Title': 'Wagtail streamfield block: injecting jQuery into the Wagtail Admin', 'ViewCount': 1336, 'id': 43558572, 'user_AboutMe': '<p>IT Director of Advanced Initiatives, Wharton Research Data Services\nThe Wharton School at the University of Pennsylvania</p>\n', 'user_AccountId': 1486549.0, 'user_DisplayName': 'FlipperPA', 'user_Reputation': 12932.0, 'user_Views': 1228.0, 'user_id': 1394697.0}}