Angular CLI/Angular 4与Python Tornado等后端框架的协作及数据交互咨询
Hey there! I totally get how frustrating it can be to untangle how Angular 4 (built with Angular CLI) works with a Python backend like Tornado, especially after days of searching and not quite connecting the dots. Let’s walk through this with practical, easy-to-follow examples so you can see the full picture.
First, let’s set the baseline: we’re using a separated frontend-backend setup. The Angular 4 app handles all UI rendering, user interactions, and client-side logic, while the Python backend (Tornado in our case) manages data storage, business logic, and exposes APIs for the frontend to talk to.
"Two-way data transfer" here usually means three key scenarios:
- Frontend fetches data from the backend (e.g., loading a list of users)
- Frontend sends data to the backend (e.g., submitting a form)
- For real-time updates (like chat or live notifications), we’ll use WebSockets to let the backend push data to the frontend without waiting for a request.
First, make sure you have the right Angular CLI version for Angular 4:
npm install -g @angular/cli@1.4.9
Then create your project and start the dev server:
ng new angular-tornado-demo cd angular-tornado-demo ng serve
You should see the default Angular page at http://localhost:4200—confirm it’s working before moving on.
First install Tornado via pip:
pip install tornado
Create a file named server.py with this code—it sets up basic REST APIs and a WebSocket endpoint for real-time communication:
import tornado.ioloop import tornado.web import tornado.websocket import json # In-memory "database" for demo purposes (replace with a real DB in production) users = [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}] active_websockets = set() class UserHandler(tornado.web.RequestHandler): def set_default_headers(self): # Handle CORS (cross-origin requests) since Angular runs on a different port self.set_header("Access-Control-Allow-Origin", "*") self.set_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS") self.set_header("Access-Control-Allow-Headers", "Content-Type") def options(self): # Respond to preflight requests required for CORS self.set_status(204) self.finish() def get(self): # Return all users as JSON self.write(json.dumps(users)) def post(self): # Add a new user from frontend-submitted data new_user = json.loads(self.request.body) new_user["id"] = len(users) + 1 users.append(new_user) self.write(json.dumps({"success": True, "user": new_user})) class WebSocketHandler(tornado.websocket.WebSocketHandler): def open(self): active_websockets.add(self) print("New WebSocket connection established") def on_message(self, message): # Broadcast incoming message to all connected clients data = json.loads(message) for ws in active_websockets: ws.write_message(json.dumps({"from": "backend", "message": f"Received: {data['message']}"})) def on_close(self): active_websockets.remove(self) print("WebSocket connection closed") def make_app(): return tornado.web.Application([ (r"/api/users", UserHandler), (r"/api/ws", WebSocketHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8888) print("Tornado server running on http://localhost:8888") tornado.ioloop.IOLoop.current().start()
Run the backend server:
python server.py
Let’s add code to Angular to interact with our Tornado APIs.
3.1 Fetch Data from Backend (GET Request)
First, enable HTTP support in Angular by importing HttpClientModule in src/app/app.module.ts:
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { HttpClientModule } from '@angular/common/http'; // Add this line import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, HttpClientModule // Add this to imports ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Create a service to handle API calls:
ng generate service user
Update src/app/user.service.ts with methods to fetch and add users:
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; @Injectable() export class UserService { private apiUrl = 'http://localhost:8888/api/users'; constructor(private http: HttpClient) { } // Fetch all users from backend getUsers(): Observable<any[]> { return this.http.get<any[]>(this.apiUrl); } // Send new user data to backend addUser(user: any): Observable<any> { return this.http.post<any>(this.apiUrl, user); } }
Now use this service in src/app/app.component.ts to load users when the component initializes:
import { Component, OnInit } from '@angular/core'; import { UserService } from './user.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { users: any[] = []; newUserName = ''; constructor(private userService: UserService) { } ngOnInit() { // Fetch users when component loads this.userService.getUsers().subscribe(users => { this.users = users; }); } }
Update src/app/app.component.html to display the user list:
<h1>User List</h1> <ul> <li *ngFor="let user of users">{{ user.id }} - {{ user.name }}</li> </ul>
Refresh your Angular page—you should see Alice and Bob listed!
3.2 Send Data to Backend (POST Request)
Add a form to app.component.html to submit new users:
<h2>Add New User</h2> <input type="text" [(ngModel)]="newUserName" placeholder="Enter user name"> <button (click)="addUser()">Add User</button>
Add the addUser method to app.component.ts:
addUser() { if (!this.newUserName.trim()) return; this.userService.addUser({ name: this.newUserName }).subscribe(response => { this.users.push(response.user); this.newUserName = ''; // Clear input field }); }
Don’t forget to import FormsModule in app.module.ts to use [(ngModel)]:
import { FormsModule } from '@angular/forms'; // Add this line @NgModule({ imports: [ BrowserModule, HttpClientModule, FormsModule // Add to imports ], // ... rest of the code })
Now when you enter a name and click "Add User", the data is sent to Tornado, stored in our in-memory list, and the new user appears on the page instantly.
3.3 Real-Time Two-Way Communication (WebSocket)
Let’s add WebSocket support for real-time updates. Update app.component.ts:
import { Component, OnInit, OnDestroy } from '@angular/core'; import { UserService } from './user.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit, OnDestroy { users: any[] = []; newUserName = ''; ws: WebSocket | null = null; messageInput = ''; messages: string[] = []; constructor(private userService: UserService) { } ngOnInit() { this.userService.getUsers().subscribe(users => { this.users = users; }); // Initialize WebSocket connection this.ws = new WebSocket('ws://localhost:8888/api/ws'); this.ws.onmessage = (event) => { const data = JSON.parse(event.data); this.messages.push(data.message); }; } sendMessage() { if (!this.messageInput.trim() || !this.ws) return; this.ws.send(JSON.stringify({ message: this.messageInput })); this.messageInput = ''; } ngOnDestroy() { // Clean up WebSocket connection when component is destroyed if (this.ws) { this.ws.close(); } } addUser() { // ... existing addUser code } }
Add a real-time chat section to app.component.html:
<h2>Real-Time Chat</h2> <input type="text" [(ngModel)]="messageInput" placeholder="Type a message"> <button (click)="sendMessage()">Send</button> <ul> <li *ngFor="let msg of messages">{{ msg }}</li> </ul>
Open two browser tabs with your Angular app—when you send a message, both tabs will receive the backend’s response in real time.
In our Tornado code, we added CORS headers to allow requests from Angular’s port. Alternatively, you can use Angular CLI’s proxy to avoid CORS issues entirely:
- Create
proxy.conf.jsonin your Angular project root:
{ "/api/*": { "target": "http://localhost:8888", "secure": false, "changeOrigin": true } }
- Update the
servecommand inangular.jsonto use the proxy:
"serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { "browserTarget": "angular-tornado-demo:build", "proxyConfig": "proxy.conf.json" }, // ... rest of the code }
- Now you can change
apiUrlinuser.service.tsto/api/users(no full URL needed), and Angular CLI will forward requests to Tornado automatically.
If you switch to Flask or Django, the core logic stays identical:
- Flask: Use
Flask-RESTfulor plain routes to expose JSON APIs, handle CORS withflask-cors. - Django: Use
Django REST Frameworkto build APIs, set up CORS withdjango-cors-headers.
Angular will interact with these backends the exact same way—via HTTP requests or WebSockets.
内容的提问来源于stack exchange,提问作者Xingsong Lin

