8

Does anyone know how to serve an Angular single page application using Flask?

I'm having trouble serving the default route, '/', which should load index.html and the associated components. Here's my Flask function:

@app.route('/')
def hello_world():
    return send_file('templates/dist/templates/index.html')

When I visit localhost:5000, I get an empty Chrome browser window:

Empty Chrome window with Angular / Flask

and the following errors in Chrome's dev console:

enter image description here

This is what should appear in Chrome:

enter image description here

I expect the errors are because Flask doesn't know where to find the supporting files to render the Angular components. As instructed in Angular's quickstart tutorial, my index.html is mostly empty, with app-root as a placeholder for the HTML body element:

<body>
  <app-root></app-root>
<script type="text/javascript" src="runtime.js"></script><script type="text/javascript" src="polyfills.js"></script><script type="text/javascript" src="styles.js"></script><script type="text/javascript" src="vendor.js"></script><script type="text/javascript" src="main.js"></script></body>

Does anyone know how to tell Flask to let Angular render a page?

3 Answers 3

12

To simplify the setup, consider using Angular CLI to place all of the files in a distribution directory during the build process, i.e., by specifying the outputPath in angular.json. You can use the angular.json assets section to move your Python files during the build.

angular.json

"your-project": {
  "root": "your-project-directory",
  "sourceRoot": "your-project-directory/src",
  "projectType": "application",
  "architect": {
    "build": {
    "builder": "@angular-devkit/build-angular:browser",
    "options": {
      "outputPath": "dist",
      "index": "your-project-directory/src/index.html",
      "main": "your-project-directory/src/main.ts",
      ...

      "assets": [
        {
          "glob": "**/*",
          "input": "your-project-directory/src/assets/",
          "output": "assets"
        },
        {
          "glob": "**/*",
          "input": "your-project-directory/src/python/",
          "output": "."
        }



In the top level of the dist directory, place your main.py with the basic Flask setup along with index.html. Note the static_proxy to ensure that supporting files are served.

main.py

from flask import Flask, send_from_directory

app = Flask(__name__)


@app.route('/<path:path>', methods=['GET'])
def static_proxy(path):
  return send_from_directory('./', path)


@app.route('/')
def root():
  return send_from_directory('./', 'index.html')


if __name__ == '__main__':
  # This is used when running locally only. When deploying use a webserver process 
  # such as Gunicorn to serve the app.
  app.run(host='127.0.0.1', port=8080, debug=True)


@app.errorhandler(500)
def server_error(e):
  return 'An internal error occurred [main.py] %s' % e, 500
Sign up to request clarification or add additional context in comments.

4 Comments

Please note that starting from Angular 8, strict MIME types are required. Your static_proxy should add the mimetype="application/javascript keyword argument to the send_from_directory call for ever path that ends with .js. Otherwise you will get an empty page and a lot of errors: "Failed to load module script: The server responded with a non-JavaScript MIME type of "". Strict MIME type checking is enforced for module scripts per HTML spec."
@RobinDeSchepper I've been searching on how to do this but I can't find a proper answer. Can you give us an example?
@Ajmal if path.endswith(".js"): return send_from_directory('./', path, mimetype="application/javascript")
This doesn't really work with routing. The routes work if you start from the root and follow the links with the app but if you enter an angular route directly into the browser it just 404s.
6

This is an old question but accepted answer did not work for me. So here is my solution that works with angular routing:


My folder structure:

/angular
    /src
    angular.json
    ... angular project files
/public
    index.html
    ... angular build files
/venv
server.py

In my angular.json I changed outputPath to achive above structure:

"outputPath": "../public"

And in server.py:

import os.path

@app.route("/custom-endpoint")
def custom_endpoint_handler():
    return jsonify(myKey="myValue")


# Serving static files
@app.route('/', defaults={'path': ''})
@app.route('/<string:path>')
@app.route('/<path:path>')
def static_proxy(path):
    if os.path.isfile('public/' + path):
        # If request is made for a file by angular for example main.js
        # condition will be true, file will be served from the public directory
        return send_from_directory('public', path)
    else:
        # Otherwise index.html will be served,
        # angular router will handle the rest
        return app.send_static_file("index.html")

Footnote: I am new to flask. So any improvements or fixes if there are any errors are welcomed.

2 Comments

Upvoted. This is simple and worked for me, with a minor adjustment, i.e. in the else block also I did return send_from_directory(FE_BUILD_DIR, "index.html") because the index.html is also inside that build directory ("public" in your example)... but, anyway, Thanks a lot
What's in the path variable?
1

As mentioned in Flask docs:

Just create a folder called static in your package or next to your module and it will be available at /static on the application.

So, if you use the template in:

templates/dist/templates/index.html
static/ # set static folder there

Or, depending how you have your application sorted:

templates/dist/static/ # set static folder there

Have a look at how they sort the application in the docs:

/application.py
/templates
    /hello.html

or if you use module folders:

/application
    /__init__.py
    /templates
        /hello.html

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.