1

I tried to debug around 2 hours, but no luck.

This is my error:

jinja2.exceptions.UndefinedError jinja2.exceptions.UndefinedError: 'item' is undefined

I can receive the data ($scope.questions) correctly. enter image description here

My index.html:

<html>

<head>
</head>

<body>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>

    <div ng-app="myApp" ng-controller="myCtrl">
        <div ng-repeat="item in questions">
            {{item.question}}
        </div>
    </div>

    <script>
        var app = angular.module('myApp', []);
        app.controller('myCtrl', function ($scope, $http) {
            $http({
                method: "GET",
                url: "http://127.0.0.1:5000/questions"
            }).then(function mySuccess(response) {
                $scope.questions = response.data;
            }, function myError(response) {
                $scope.myWelcome = response.statusText;
            });
        });
    </script>
</body>

</html>

My server side:

import os

from flask import Flask, request, render_template, Response
from flask_restful import Resource, Api

app = Flask(__name__)
api = Api(app)

question_id_serial = 2
questions = {
    1: {
        'id': 1,
        'question': 'Is this hard?',
    },
    2: {
        'id': 2,
        'question': 'Who is going to win the SuperBowl in 2017?',
    }
}

answer_id_serial = 6
answers = {
    1: {
        'id': 1,
        'question_id': 1,
        'answer': 'Yes',
        'count': 0
    },
    2: {
        'id': 2,
        'question_id': 1,
        'answer': 'No',
        'count': 0
    },
    3: {
        'id': 3,
        'question_id': 2,
        'answer': 'Eagles',
        'count': 0
    },
    4: {
        'id': 4,
        'question_id': 2,
        'answer': 'Patriots',
        'count': 0
    },
    5: {
        'id': 5,
        'question_id': 2,
        'answer': 'Seahawks',
        'count': 0
    },
    6: {
        'id': 6,
        'question_id': 2,
        'answer': 'Broncos'
    }
}


class Answer(Resource):
    def get(self, answer_id):
        return answers[answer_id]

    def put(self, answer_id):
        answer = answers[answer_id]
        data = request.get_json()
        values = {k: data.get(k, v) for k, v in answer.items()}
        answers[answer_id].update(values)
        return values

    def delete(self, answer_id):
        values = answers[answer_id]
        del answers[answer_id]
        return values

class Answers(Resource):
    def get(self):
        return answers.values()

    def post(self):
        global answer_id_serial
        answer_id_serial += 1
        data = request.get_json()
        values = {
            'id': answer_id_serial,
            'answer': data['answer'],
            'count': data.get('count', 0),
            'question_id': data['question_id']
        }

        answers[answer_id_serial] = values
        return values


class Question(Resource):
    def get(self, question_id):
        data = questions[question_id].copy()
        data['answers'] = [ans for ans in answers.values() if ans['question_id'] == question_id]
        return data

    def put(self, question_id):
        question = questions[question_id]
        data = request.get_json()
        data.pop('answers', [])
        values = {k: data.get(k, v) for k, v in question.items()}
        questions[question_id].update(values)
        values['answers'] = [ans for ans in answers.values() if ans['question_id'] == question_id]
        return values

    def delete(self, question_id):
        values = questions[question_id]
        del questions[question_id]
        values['answers'] = [ans for ans in answers.values() if ans['question_id'] == question_id]
        return values


class Questions(Resource):
    def get(self):
        output = []
        for question in  questions.values():
            question = question.copy()
            question['answers'] = [ans for ans in answers.values() if ans['question_id'] == question['id']]
            output.append(question)
        return output

    def post(self):
        global question_id_serial
        question_id_serial += 1
        data = request.get_json()
        values = {
            'id': question_id_serial,
            'question': data['question']
        }
        questions[question_id_serial] = values
        return values

api.add_resource(Questions, '/questions')
api.add_resource(Question,  '/questions/<int:question_id>')
api.add_resource(Answers,   '/answers')
api.add_resource(Answer,    '/answers/<int:answer_id>')

@app.route('/')
def show_page():
    return render_template('index.html')

@app.route('/assets/<path:path>')
def get_resource(path):
    mimetypes = {
        '.css': 'text/css',
        '.html': 'text/html',
        '.js': 'application/javascript'
    }

    content = open(path).read()
    return Response(content, mimetype=mimetypes[os.path.splitext(path)[1]])

if __name__ == '__main__':
    app.run(debug=True)
FullStackDeveloper
  • 910
  • 1
  • 16
  • 40

2 Answers2

1

Is that html from the render_template('index.html')?

If that so, you can't render the template with Flask and then try to render it again with Angular.

Basically your ng-repeat is not working, Flask can't recognize {{ item }}, hence the error.

henriquesg
  • 46
  • 4
  • "Is that html from the render_template('index.html')". Yes. Any suggestion to solve this bug? I never use Flask-REST API. I was working with ASP.NET REST API. – FullStackDeveloper Jul 04 '17 at 02:52
  • If your landing page is always going to return the questions, instead making a request to retrieve those, you can add it to the context when Flask is rendering. Something like: `render_template('index.html', questions=questions)` Then you can use the jinja2 syntax to iterate the questions in your template: `{ % for question in questions %}` You can read more here: http://flask.pocoo.org/docs/0.12/quickstart/#rendering-templates – henriquesg Jul 04 '17 at 02:55
  • I refactored to use jinja2 systax and also refactor render_template as you mentioned, but still not work. – FullStackDeveloper Jul 04 '17 at 03:06
  • did you write something like: `{% for question in questions %}
    {{ question }}
    {% endfor %}`? see if this helps: http://jinja.pocoo.org/docs/2.9/templates/
    – henriquesg Jul 04 '17 at 03:12
  • Yes. I did that. I used jinja2 syntax, instead of angularjs syntax. Still not work. I do not why I need jinja2 here instead of angularjs syntax. – FullStackDeveloper Jul 04 '17 at 03:15
  • Is hard to say why it's not working without looking at the code. Anyway, this related question might help you: https://stackoverflow.com/questions/32147748/flask-and-angularjs – henriquesg Jul 04 '17 at 03:19
  • This post works for me: https://stackoverflow.com/a/34291267/8229192 I posted my working version. Thanks for referring that post. – FullStackDeveloper Jul 04 '17 at 03:31
1

After add {% raw %} and {% endraw %} to the template, it works fine now. I refer to this post

I attached working template, hope this will help future developers who works with Flask Restapi and AngularJS:

{% raw %}
<!DOCTYPE html>
<html>

<head>
</head>

<body>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>

    <div ng-app="myApp" ng-controller="myCtrl">

        <h1 ng-repeat="question in questions">{{question.question}}</h1>

    </div>

    <script>
        var app = angular.module('myApp', []);
        app.controller('myCtrl', function ($scope, $http) {
            $http({
                method: "GET",
                url: "http://127.0.0.1:5000/questions"
            }).then(function mySuccess(response) {
                $scope.questions = response.data;
            }, function myError(response) {
                $scope.myWelcome = response.statusText;
            });
        });
    </script>
</body>

</html>
{% endraw %}
FullStackDeveloper
  • 910
  • 1
  • 16
  • 40