-1

Edit: Hi I've checked the duplicates of this question, they do not ask about SPA (client vs server rendering)

I am trying to change my Flask Web App to be rendered client-side in JS instead of server-side with Jinja templating. An interviewer told me that the problem with my Flask Web App, is that the Application server usually only should be serving pure Json APIs and not try to render any existing static content (example the website content that doesn't change, the theme, css, logos, pictures etc), since it'll be a waste of the computing resources of the application server.

With regards to my web app, what it does is it makes calls to 2 different APIs and returns you carpark availability lots based on a user input(e.g your place of residence)

I've done some research into using AJAX with Flask. It seems using XMLHttpRequest based off https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX/Getting_Started is the way to go. Based off my limited understanding, if implemented correctly, would it be possible to switch my web app into one that essentially replicates the functionality of a SPA? (no page refreshes, client-side rendering)

Currently struggling with the implementation, any help would be great!

A .py script below

# one of Four routes in my "Main" Flask Script
@app.route('/address',methods = ['POST', 'GET'])
def address1():
    if request.method == 'POST':
        desired_CP_Address = request.form["address"]

        response = requests.get("https://data.gov.sg/api/action/datastore_search?resource_id=139a3035-e624-4f56-b63f-89ae28d4ae4c&q=" + desired_CP_Address)
        r = response.json()

        CP_Info = r['result']['records']
        return render_template("address1.html", text=CP_Info)

A snippet of the resulting html page

<body>
<h1>carpark.py</h1>

<!-- if invalid input display bottom -->
{% if text %}   
    <h3>Click on your desired carpark number</h3>
<hr>

    {% for x in text %}
      <p>{{ x["address"] }} : <strong><a href="">{{ x["car_park_no"] }}</a></strong> </p>
    {% endfor %}
sgeza
  • 341
  • 1
  • 5
  • 16

2 Answers2

1

if you want to make an spa, you can host the spa html/js in a html file. then you serve the json in the /address route

@app.route('/address',methods = ['POST', 'GET'])
def address1():
    if request.method == 'POST':
        desired_CP_Address = request.form["address"]

        response = requests.get("https://data.gov.sg/api/action/datastore_search?resource_id=139a3035-e624-4f56-b63f-89ae28d4ae4c&q=" + desired_CP_Address)
        r = response.json()

        return jsonify(r['result']['records'])



@app.route('/spa',methods = ['GET'])
def spa():
    return render_template('spa.html')

now, your spa.html can use javascript xhr to query the json from /address (cfr https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch ), eg.

<script>
    fetch('/address', {
      method: "POST", // *GET, POST, PUT, DELETE, etc.
      mode: "cors", // no-cors, cors, *same-origin
      cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
      credentials: "same-origin", // include, same-origin, *omit
      headers: {
        "Content-Type": "application/json; charset=utf-8",
        // "Content-Type": "application/x-www-form-urlencoded",
      },
      redirect: "follow", // manual, *follow, error
      referrer: "no-referrer", // no-referrer, *client
      body: JSON.stringify(data), // body data type must match "Content-Type" header
})
      .then(function(response) {
        return response.json();
      })
      .then(function(myJson) {
        console.log(JSON.stringify(myJson));
      });
</script>

I'd suggest you use a framework for the javascript frontend (eg. react) instead of writing everything from scratch.

Tohmaxxx
  • 423
  • 2
  • 9
  • ah so basically, instead of using flask to return render_template we use it to return data? – sgeza Oct 05 '18 at 10:52
  • yes, flask returns the json data, that data is retrieved by javascript. I made an SPA app in flask here: http://thomaxxl.pythonanywhere.com/ja/index.html#/people , check the network traffic in your browser dev tools and you'll see what it's about: flask serves json, the spa fetches it and renders it. – Tohmaxxx Oct 05 '18 at 11:29
0

In very, very simple words, moving from a server-rendered webpages Flask to API+SPA is changing handlers so they return not a rendered page, but only data, needed for that webpage to render on a client-side.

Vanilla JS approach (which I don't recommend for real production apps, there are SPA frameworks for it like React, Angular, Vue etc.) would be.

Change:

return render_template("address1.html", text=CP_Info)

To:

return json.dumps({'text': CP_Info})

Then remove all Jinja code from HTML and serve it as a static file. Additionally, you should add a JS code to your HTML file to fetch data (with XHR or jQuery or what you prefer) from the /address route and add HTML tags (elements) based on it with a plain JS or a framework like jQuery or similar.

If you done everything correctly, opened page at localhost:your_app_port/static/your_html_name.html would be almost the same, as previously at the /address route.

Fine
  • 2,114
  • 1
  • 12
  • 18
  • Right, thanks! would yo u say it would be a lot easier to rewrite the entire thing in VueJS with NodeJS(maybe?) handling the API calls/backend? that would make it a SPA wouldnt it? – sgeza Oct 05 '18 at 10:51
  • I can't say whether it easier or not because there are huge websites built around server-rendering and cost of rewriting them into SPA could be too high. But if you are building such a SPA app from the scratch you definitely should use a SPA framework. – Fine Oct 05 '18 at 11:08