3

I have an Angular 4/Django application, all of the angular code in a Django application. The angular app is built using webpack.

I would like webpack to output the .js bundles into the "static" folder and an "index.html" file into the "templates" folder. The <script></script> tags that are injected will also need to be updated to use Django's "static files" notation:

{% load static %}

<script type="text/javascript" src="{% static 'dist/polyfills.js' %}"></script>

Here is my directory structure (some files omitted for brevity), where project is the top-level holding the Django project.

project
    + angular
        + app (angular application root)
            + config
            + node_modules
            + src
            - webpack.config.js
        + static
            + dist ( webpack output folder)
                - app.[hash].js
                - vendor.[hash].js
                - polyfills.[hash.js
                - index.html
        + templates
                - index.html 
  • Note: I know I can copy/move the index.html quite easily, but I need the tags to also use Django's static files.

For clarification. Here is my current "templates/index.html" file that is served by Django. This works fine as it is. I would like to have webpack generating this file if possible because I want to use the [hash] in the naming of the dis files and have that updated automatically here. So I would have app.[hash].js, vendor.[hash].js, polyfill.[hash].js and the Django template would be updated when my angular app is built by webpack.

templates/index.html

    {% load static %}
    <html>
      <head>
          <base href="/">
        <title>App</title>
        <link href="{% static 'dist/app.css' %}" rel="stylesheet">
      </head>
      <body>

        <my-app>Loading...</my-app>


       <script type="text/javascript" src="{% static 'dist/polyfills.js' %}"></script>
       <script type="text/javascript" src="{% static 'dist/vendor.js' %}"></script>
       <script type="text/javascript" src="{% static 'dist/app.js' %}"></script>

    </body>

</html>
Maverik Minett
  • 2,131
  • 3
  • 17
  • 28
  • This is the wrong way round. The index.html should be a template which is rendered by Django and then used by Angular. – Daniel Roseman Oct 05 '17 at 20:46
  • 2
    Yes, I understand and that is currently how I am implementing. What I want to accomplish is for webpack to generate the template file for Django so that I can generate bundles that have the [hash] in the name and django is aware of the file names. – Maverik Minett Oct 06 '17 at 12:43

1 Answers1

3

Webpack uses the html-webpack-plugin to generate the templates. I've found a workaround (it feels hacky, and it's certainly an extra layer of work) which uses the webpack.config.js along with the django templates.

Note that Angular2/4/5/whatever doesn't give you access to its webpack config. Use ng eject (See this answer) to extract it. The webpack.config.js below is an excerpt with modifications.

DISCLAIMER I've only ever used this with a Webpack + Django project. Changing the default Webpack config for Angular2 seems like a huge rabbit hole to go into. I would suggest avoiding this and using Django Rest Framework + Angular2 to completely separate the back-end from the front-end. But to each its own i guess; below is my solution:

// webpack.config.js
// ...
const HtmlWebpackPlugin = require('html-webpack-plugin'); // should already be here,
                                            // but it's the main player of this excerpt

module.exports = {
    // ...
    "entry": {
        "main": ["./src/main.ts"],
        "polyfills": ["./src/polyfills.ts"],
        "styles": ["./src/assets/css/styles.css"]
    },
    "output": {
        "path": path.join(process.cwd(), "dist"),
        "filename": "[name].bundle.js",
        "chunkFilename": "[id].chunk.js",
        "publicPath": '/'             // <- this is new
    },
    // ...
    "plugins": [
        // ...
        new HtmlWebpackPlugin({
            "template": "./templates/index.html", // <- path to your template
            "filename": "./path/of/output/file.html", // <- output html file
                                              // default  is './index.html'
            "inject": false // <- this allows you to place the static files
                            //    where you want them
     // ...

Then

# my_project.settings.py
# ...
# assuming your angular and django projects share the same base dir
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'src'), # <- this is webpack's output dir
)

Then in your template

<!-- templates/index.html -->
{% load static %}
<html>
  <head>
    <base href="/">
    <title>App</title>
    <link href="{% static 'dist/app.css' %}" rel="stylesheet">
  </head>
  <body>
    <my-app>Loading...</my-app>
    <script type="text/javascript" src="{% static '<%= htmlWebpackPlugin.files.chunks.polyfills.entry' %}"></script>
    <script type="text/javascript" src="{% static '<%= htmlWebpackPlugin.files.chunks.vendor.entry' %}"></script>
    <script type="text/javascript" src="{% static '<%= htmlWebpackPlugin.files.chunks.app.entry' %}"></script>

  </body>

</html>

You will have to run django's python manage.py collectstatic --clear after every webpack build (in order to clean the 'static' folder).

djangulo
  • 86
  • 6
  • Thanks, finally I figured how to prevent the plugin to inject JS wherever it wanted. I'd have a couple of questions left if you are still up to it: 1) Why the command `python manage.py collectstatic --clear`? It seems dangerous and am not sure it is useful. 2) Is there a way to make also webpack-dev-server serve the html content real time to Django or the plugin just builds? (LOL for your nickname, in Italian it sounds quite funny) – Giampaolo Ferradini Jul 18 '20 at 14:37
  • 1
    @GiampaoloFerradini, I'm quite certain the `collectstatic --clear` is used to clean out the webpack output from the previous webpack bundle. Webpack uses a unique hash each time, so the files would just build up everytime you ran it. – Joey Carlisle Jan 19 '21 at 22:03