When using Angular 5 with Flask and ng build --aot --watch, I am having major issues getting Angular's routing and refreshing the page to work.
I have come across many attempts to fix this, but none of them when applied to my project work (and the example projects provided when I try it there also do not seem to work).
What happens (depending on the thing I try) are these:
It can't find the path at all and I get a Jinja2 error
It can't find the document for localhost:5015/ (the port I'm using) and I get a 404 and a white screen
It loads index.html, but upon refresh it gets a 404 (implying that the browser is attempting to fetch the page from the server, not using the Angular routing system)
I get the same issue as this one: Syntax Error in Angular App: Unexpected token <
I am under the impression this is a normal issue with all SPAs and that the solution is to always redirect the server to index.html. We have done this (AFAIK), and attempted multiple ways to do this:
Our structure is roughly the following:
- ProjectDir
- reverseProxy_Calls
- scripts
- swagger_yaml_definitions
- templates
- e2e
- dist
- src
- app
And our server.py contains this (among other stuff)
Swagger(app)
app.template_folder = "./templates/dist/"
app.static_folder = "./templates/dist/"
app.static_path = "./templates/dist/"
app.static_url_path = "./templates/dist/"
app.instance_path = "./templates/dist/"
config = settings.config()
config.read("settings.cfg")
#app.route('/', defaults={'path': ''}, methods=['GET', 'POST'])
#app.route('/<path:path>', methods=['GET', 'POST'])
def get_resource(path): # pragma: no cover
# If we are devmode, proxy through flask.
if config.getKey("devmode") == True:
url = "http://localhost:%s/%s" % (config.getKey('ng_port'), path)
j = requests.get(url, stream=True, params=request.args)
return make_response(j.text)
# return the results
# return render_template('dist/index.html')
# return app.send_static_file("index.html")
return send_from_directory('dist/', path)
if __name__ == '__main__':
app.run(debug=True, port=int(config.getKey("port"))) # , host='0.0.0.0')
^ A variety of ways here show various attempts to handle this.
I have also looked at and tried projects that use variations of the following:
Flask Cli
Flask-Script
Flask-Bootstrap
None of them have solved this problem when I have tried them.
I have been told that using the hashRoute location strategy in Angular rather than the History PushState strategy will also work, however when I attempted to do that it had the same behavior of losing the page upon refresh (additionally we wish to use server side rendering in the future, and hashRoute location strategy prevents this possibility, among many other downsides).
Possibly related is that Flask seems to reload or re-render the page when it does find the index.html and routes initially, wiping anything printed to the browser dev console. I have seen this in some of the flask examples, but not all, and I'm uncertain what causes this.
When running via ng serve --aot --prod, things work exactly correctly and as expected. It's only when serving via Flask that things break down.
Apologies for the long question and thank you for your time in advance.
I am under the impression this is a normal issue with all SPAs and
that the solution is to always redirect the server to index.html. We
have done this (AFAIK), and attempted multiple ways to do this
You also need to redirect all HTTP errors to index.html.
Related
I am creating a falcon app which has many endpoints. Just like this [example][1] I want to add a function to call before every request to send to some data. I have followed their examples, however they don't work for me if the route has been added already. For example if I call endpoint /test and it is added by add_route it wont work, but if not it will work fine. I am wondering is there any other example then the two explained in the example. At the moment I am creating it in the following way.
middleware_list = [utils.FuncMiddleware(hook) for hook in
utils.before_hooks()]
middleware_list.append(middlewares.RequireJSON())
middleware_list.append(middlewares.JSONTranslator())
tve_app = falcon.API(middleware=middleware_list)
for exception_class in tveapi_exc.exception_handlers_catalog:
tve_app.add_error_handler(exception_class, exception_class.handle)
endpoint_catalog = [
('/v1', v1.public_endpoints()),
('/apps', apps.public_endpoints()),
]
for version_path, endpoints in endpoint_catalog:
for route, resource in endpoints:
#tve_app.add_sink(request_notifier, version_path + route)
tve_app.add_route(version_path + route, resource)
The other option is to add decorator before every function but this involves a lot of code re-writing. Or maybe the problem is that it is integrated with apache. Any ideas where to continue on?
[1]: How can I intercept static routes in a falcon app?
I am working on flask + Angular + MongoDb application. I am using state provider in angular. states are loading perfectly fine from application. but when i refresh the page it is throwing 404 error.
App.py: (Flask)
application = Flask(__name__)
#application.route('/')
def showMachineList():
print ("Inside the show machine list function")
return application.make_response(open('templates/index.html').read())
App.js ( Angular)
angular.module('testapp', ['ngCookies', 'ngResource',
'ngSanitize', 'ui.router', 'ui.bootstrap','ngMaterial','ngCookies'
])
.config(function($urlRouterProvider, $locationProvider,$interpolateProvider) {
$urlRouterProvider.otherwise('/');
//$interpolateProvider.startSymbol('//').endSymbol('//');
$locationProvider.html5Mode(true).hashPrefix('!');
});
State
angular.module('testapp')
.config(function ($stateProvider) {
console.log("inside create outage request")
$stateProvider
.state('cor', {
url: '/cor',
template: '<cor></cor>'
//templateUrl: '/static/client/app/acn/acn.html'
});
});
I have added the <base href="/"> in index.html as well.
when i refresh the page with "http://localhost:5000/cor" its throwing 404.
Can you let me know what i am missing?
This is fairly common problem with Single Page Applications. Looks like your Angular app can handle the cor state properly on the client-side, but your web server has no way to know that the /cor path should be routed to the same Angular application on the initial load.
Try adding a second route to your endpoint:
#application.route('/')
#application.route('/cor')
def showMachineList():
...
Alternatively, you could use a catch-all route to handle any path suffix.
When you refresh the page the browser send the URL "http://localhost:5000/cor" to the server. As I can see in your code, there's no /cor route defined, only /. I think you have to define one.
#application.route('/cor')
def showMachineList():
with open('templates/index.html') as fd:
content = fd.read()
return application.make_response(content)
The problem may be in your template: the one where you define the link to cor. You should have something like this:
link to cor
I make the assumption that you have written this instead:
link to cor
I am trying connect to mysql in bottle framework but with no success. Error tells me to give second argument to route but I do not want additional argument in url.
import bottle
import bottle_mysql
from bottle import route, run, template, app
app = bottle.Bottle()
plugin = bottle_mysql.Plugin(dbuser='root', dbpass='toor', dbname='database')
app.install(plugin)
#route('/show/<a>')
def show(a, db):
db.execute('SELECT * FROM table WHERE a > "%d"', (a,))
return template('Hello {{name}}, how are you?', name=a)
run(host='192.168.1.19', port=8080, debug=True)
Error:
TypeError('show() takes exactly 2 arguments (1 given)',)
You may want to create db as a global variable and remove it as a parameter for the function show() or possibly make it an optional variable
The author of bottle-mysql says :
to automatically detect routes that need a database connection,
the plugin searches for route callbacks that require a db keyword
argument (configurable) and skips routes that do not.
Each route needs to be configured as belonging to the app or the app's plugin can't send data via that route.
So you do need the "app." in #app.route('/show/<a>')
And your run statement on the last line needs to specify the app too otherwise it won't recognise the routes belonging to the app.
run(app, host='192.168.1.19', port=8080, debug=True)
As others have responded, you need to change:
#route('/show/<a>')
to:
#app.route('/show/<a>')
But this is not the only change you need to make to solve the issue, you also need to change:
run(host='192.168.1.19', port=8080, debug=True)
to:
app.run(host='192.168.1.19', port=8080, debug=True)
Unfortunately this last part is not clear in the documentation and took a bit of guess work to figure out, but it's actually pretty obvious actually because if you just call run without the app. prefix, you're basically running a new bottle instance, which will result in 404 errors tying to access your routes.
So I have established a pretty decent understanding of the simple architecture of an angularjs app, calling $http and posting to a php page, and receiving data back.
What I'm wondering, is how to do the same type of function with python. Is it possible to have python act the same, with self contained script files that accept post data and echo json back?
$username = $_POST['username'];
type variable assignment at the beginning of the script, and:
echo json_encode(response);
type response.
I'm wanting to use Python for some Internal Tools for my company, as it offers better libraries for remotely running powershell scripts (as the tools are all linux hosted) and overall just has libraries that fit my needs. I'm just having a difficult time finding a concise answer to how this could be set up.
---EDIT------
So I set up a quick example using the information below.
the angular:
var app = angular.module("api");
app.controller("MainController", ["$scope","$http",MainController]);
function MainController($scope,$http){
$http.post('/api',{test: "hello"})
.then(function(response){
console.log(response.data);
})
}
The flask:
from flask import Flask, request
import json
app = Flask(__name__)
#app.route('/api', methods=['POST', 'GET'])
def api():
if request.method == 'POST':
request.data
return 'You made it' # Just so I originally could see that the flask page
if __name__ == "__main__":
app.run()
I'm getting a 404 for that URL. If I change the angular to look at 'localhost:5000/api' (where my flask app is running),it gives me the error of "Unsupported URL Type".
I am seeing when I do the first case, it tries to look at http://localhost/api , which is correct! except for the port. Which is why I tried to specify the port.
Any suggestions for a next step?
Use flask.
You could host your app on a flask "server" and return the content you'd like too with a python processing.
http://flask.pocoo.org/
Use the documentation to setup a route where you'll POST your data using jquery or whatever, then on the route you can do your python stuff and return a JSON to your angular app if you need to.
from flask import request
#app.route('/test', methods=['POST', 'GET'])
def test():
if request.method == 'POST':
print request.data['your_field']
return your_json_data
So, I have a flask application that works well when it's not deployed. Imagine it has pages for /index/ and /home/ and /home/field/. Now that I have deployed the app using Apache and mod_wsgi, it needs to have a prefix for every URL.
Now, it should look like /newapp/index/ and /newapp/home/ and /newapp/home/field/.
So, I changed all of the URLs to include the new /newapp/ prefix, but the only URL that works with it is the original /index/ URL. All of the others return a
Not Found The requested URL was not found on the server.
in the browser when I click for that URL. I definitely handle that route in my main.py, so I don't know why it would not be found.
Anyone know what is going on?
EDIT: adding some code
Basically I changed all my code in main.py from:
Original:
#app.route('/')
#app.route('/index/', methods=['GET', 'POST'])
def index():
#Stuff
#app.route('/home/')
def farms():
#More stuff
#app.route('/home/<selector>')
def fields(selector):
#Further stuff
To....
New Code
#app.route('/newapp/')
#app.route('/newapp/index/', methods=['GET', 'POST'])
def index():
#Stuff
#app.route('/newapp/home/')
def farms():
#More stuff
#app.route('/newapp/home/<selector>')
def fields(selector):
#Further stuff
I did this because the domain I am using already has another Flask app, so I had to differentiate between the two. Also, I expect there to be more flask apps in the future, so this newapp will end up being an identifier for any given flask app.
I changed main.py as well has all of my hrefs in my templates. So, the hrefs went from
href=/index/
to
href=/newapp/index/
And, I get the error that I posted above whenever I try to click on a link
Further info:
So, checking out the apache error logs one error says, File does not exist: /var/www/flask_util.js, because in my main.py I call from flask_util_js import FlaskUtilJs. I'm not sure if this has anything to do with the URL routing, but it might
You don't need to add the prefix in your code.
Say you have code like
#app.route('/hi', methods = ['GET','POST'])
def myIndex():
return "Hello World!", 200
And you set your alias like to deploy it to www.myserver.com/app/
WSGIScriptAlias /app /var/www/myDir/myApp/app.wsgi
the server should automatically map www.myserver.com/app/hi to /hi in your application.
However if you set the mapping to
#app.route('/newapp/hi', methods = ['GET','POST'])
def myIndex():
return "Hello World!", 200
You WSGI app would only receive the call for /hi (server strips away the prefix) so it would find no matching Path and return not found.