I'm getting started on Angular 2 after much success with Angular 1. I followed both the Quickstart and the Tour of Heroes tutorials and everything works like a charm.
The lite server gets kicked off, I see tsc running in watch mode and I even see BrowserSync is hooked up. Great!
However, I need to start making things a bit more real world.
Instead of using lite server, how do I get all this working using a flask dev or gunicorn server, serving the initial index.html file as a rendered jinja template?
Giving the flask dev server a very näive try, I basically copy the contents of the example index.html from the tutorial into my jinja template, then run npm run tsc:w and finally fire up my flask dev server and hope for the best. Things compile fine. But in the browser I see problems:
angular2-polyfills.js:332 Error: SyntaxError: Unexpected token <
at ZoneDelegate.invoke (http://127.0.0.1:5000/static/node_modules/angular2/bundles/angular2-polyfills.js:332:29)
at Zone.run (http://127.0.0.1:5000/static/node_modules/angular2/bundles/angular2-polyfills.js:227:44)
at http://127.0.0.1:5000/static/node_modules/angular2/bundles/angular2-polyfills.js:576:58
Evaluating http://127.0.0.1:5000/app/main.js
Error loading http://127.0.0.1:5000/app/main.js`
Looking at the culprit transpile main.js file I see:
(function(System, SystemJS, require) {<!doctype html>
<html>
<head lang='en'>
So, yeah, that's not gonna work...clearly my wiring is haywire.
There is a lot of black magic going on with the shims, the polyfills, reactive extensions, systemjs, angular2 itself, and then toss in tsc and lite server. Admittedly, I don't have all this down yet and it will take time, but I'm hoping to get my project into a sane state fairly quickly.
(I don't mind using lite server (BrowserSync is a nice perk) in development as long as I can configure it to proxy the real flask server which will return the render jinja templates.)
Update
Here is the actual template index file with some minor changes that I've made:
<!doctype html>
<html>
<head lang="en">
{% block head %}
<meta charset="utf-8">
<title>Angular 2 QuickStart</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- 1. Load libraries -->
<!-- IE required polyfills, in this exact order -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>
<script src="node_modules/angular2/es6/dev/src/testing/shims_for_IE.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<!-- 2. Configure SystemJS -->
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/main')
.then(null, console.error.bind(console));
</script>
{% endblock %}
</head>
<!-- 3. Display the application -->
<body>
{% block content %}{% endblock %}
<my-app>Loading...</my-app>
<script>
(function(globals) {
this.MyConfig = {
staticDir: '{{ config["STATIC_DIR"] }}'
};
}(this));
</script>
</body>
</html>
I had the same problem and here is how I solved it;
With a directory structure as follows:
+- MyAppName
+-- ServerApp
+--- //...flask files here
+-- ClientApp
+--- node-modules
+--- app
+--- //...more node+angular application files
I exposed the ClientApp folder in my flask application at the URL .../client-app/... using the following code:
from flask import Flask, send_from_directory
import os
BASE_URL = os.path.abspath(os.path.dirname(__file__))
CLIENT_APP_FOLDER = os.path.join(BASE_URL, "ClientApp")
# This is required by zone.js as it need to access the
# "main.js" file in the "ClientApp\app" folder which it
# does by accessing "<your-site-path>/app/main.js"
@app.route('/app/<path:filename>')
def client_app_app_folder(filename):
return send_from_directory(os.path.join(CLIENT_APP_FOLDER, "app"), filename)
# Custom static data
@app.route('/client-app/<path:filename>')
def client_app_folder(filename):
return send_from_directory(CLIENT_APP_FOLDER, filename)
Head over to your index.html file (I placed mine at ServerApp\templates\index.html so that I could simply do render_template('index.html')) and make it look something like this:
<html>
<head>
<title>Angular 2 QuickStart</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="client-app/assets/css/style.css">
<!-- 1. Load libraries -->
<!-- Polyfill(s) for older browsers -->
<script src="client-app/node_modules/es6-shim/es6-shim.min.js"></script>
<script src="client-app/node_modules/zone.js/dist/zone.js"></script>
<script src="client-app/node_modules/reflect-metadata/Reflect.js"></script>
<script src="client-app/node_modules/systemjs/dist/system.src.js"></script>
<!-- 2. Configure SystemJS -->
<script src="client-app/systemjs.config.js"></script>
<script src=""></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
</head>
<!-- 3. Display the application -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
the 'client-app' prepended to the paths is the route I chose to expose my client_app_folder() function at
Configure your client application's package finder to use the set route ('client-app/...' in this case). I use system.js and therefore I made my systemjs.config.js file look like this:
(function(global) {
// map tells the System loader where to look for things
var map = {
'app': 'client-app/app', // 'dist',
'rxjs': 'client-app/node_modules/rxjs',
'angular2-in-memory-web-api': 'client-app/node_modules/angular2-in-memory-web-api',
'@angular': 'client-app/node_modules/@angular'
};
// packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main: 'main.js', defaultExtension: 'js' },
'rxjs': { defaultExtension: 'js' },
'angular2-in-memory-web-api': { defaultExtension: 'js' },
};
var packageNames = [
'@angular/common',
'@angular/compiler',
'@angular/core',
'@angular/http',
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
'@angular/router',
'@angular/router-deprecated',
'@angular/testing',
'@angular/upgrade',
];
// add package entries for angular packages in the form '@angular/common': { main: 'index.js', defaultExtension: 'js' }
packageNames.forEach(function(pkgName) {
packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
});
var config = {
map: map,
packages: packages
}
// filterSystemConfig - index.html's chance to modify config before we register it.
if (global.filterSystemConfig) { global.filterSystemConfig(config); }
System.config(config);
})(this);
I only modified the map variable
Godspeed!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With