Building Web Apps with Django and Angular 2+ CLI -- Words (1532)

Django Angular Webpack
Django and Angular 2+ CLI

Angular CLI is a command line utility which enables you to quickly generate and build Angular 2+ apps .It takes care of much of the configuration and let you focus on implementing your next idea so it's a great tool for bootstraping modern JavaScript projects based on Angular 2+ .

In this tutorial we are going to see how to serve and integrate an Angular 2+ web application (as a frontend ) with a Django backend .

Communication between Django and Angular 2+ happens through a Restful API ,so for testing if our integration works, as expected ,you need to have a small testing API built in the Django backend .

You can use any Rest API generator for Django such as Django Rest Framework (DRF) or Tastypie to build some API endpoints .

I have previously created an API for a simple product manager app with Django and DRF so I'm going to use it in this tutorial to test the integration with the Angular 2+ frontend app .

Requirements

Before we can start building our demo app ,there are some requirements that we need to have installed on our system :

Python ,

Virtualenv and PIP .

Node.js and NPM .

So make sure you install these requirements on your system before we start .

Lets get started with a new Django project .

virtualenv myenv 
source myenv/bin/activate 
pip install django 
django-admin.py startproject django-ngx-demo 

We have created a virtual environment ,activate it then installed Django and created a new project .

Next lets generate a new Angular 2+ application inside a subfolder of Django project ,using the Angular CLI .

First if you don't have Angular CLI installed ,start by install it from npm with :

npm install -g @angular/cli 

Then use it to generate a new Angular 2+ app inside django project root folder :

cd django-ngx-demo
ng new frontend 

Development setup

For development we can run two local servers without any issues ,one for Django using runserver command and the other one for Angular using ng serve command .

Actually we have a small issue that we are going to see how to solve in this tutorial .

It's related to cross origin requests ,,since we have two servers with different ports the same origin policy in browsers will block any http requests from our front end angular app to django api backend .

Using a proxy

You can simply create a proxy.conf.json file then add :

{
    "/products": {
        "target": "http://localhost:8000",
        "secure": false
    }
}

Then change npm start script to use a proxy with this configuartion :

"start": "ng serve --proxy-config proxy.conf.json",    

Now you need to use

npm start 

To start your server instead of ng serve .

See more information about proxying support in webpack's dev server

If this somehow doesn't work lets see a second solution to this issue :

Adding CORS headers to Django backend

If you send an API request from Angular CLI server to Django local server your browser will throw an error like this :

XMLHttpRequest cannot load http://localhost:8000/products. 
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://127.0.0.1:4200' is therefore not allowed access.

So lets get rid of this error by setting CORS headers on our Django backend.

First start by installing django-cors-headers with pip :

pip install django-cors-headers

Add it to installed apps in settings.py :

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

Add 'corsheaders.middleware.CorsMiddleware' middleware :

MIDDLEWARE = [  
    ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
]

Then set CORSORIGINALLOW_ALL to True in settings.py :

CORS_ORIGIN_ALLOW_ALL = True     

See all available options here

Next you can start both servers in different terminals and start testing .

Now the Same Origin Policy won't block the requests since an allow all origin header is present on the http responce .

So if you are using the first approach ,make sure to only use relative urls or otherwise use the second approach .

Next lets send some requests to our Django API endpoints .

Create Django API endpoints with Django Rest Framework

I have previously created a simple app with Django and Django Rest Framework for a products inventory manager so I'm going to use that for testing .

If you don't have an API yet,you can follow this tutorial

In my case I have 4 endpoints

/products to get or create products

/transactions to get or create transactions

/families to get or create families

/locations to get or create locations

Configure Angular http module

Open frontend/src/app/app.module.ts then import HttpModule from @angular/http then add it to AppModule imports .

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';

@NgModule({
declarations: [
    AppComponent
],
imports: [
    BrowserModule ,
    HttpModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

Sending API requests from Angular to Django DRF

Then open frontend/src/app/app.component.ts and add :

import { Component } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/toPromise';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
url : string = 'http://localhost:8000/products';


constructor(private http : Http){}
public getProducts(){

    this.http.get(this.url).toPromise().then((res)=>{
    console.log(res.json());
    });

}
}

Next open frontend/src/app/app.component.html then add :

<div style="text-align:center">
    <button (click)="getProducts()">Get /products </button> 
</div>

We have injected http and then added a new getProducts() method which sends a GET request to http://localhost:8000/products

Next we created a button and bound its click event to getProducts() method .

P.S

If you got an error saying :

ERROR TypeError: this.http.get(...).toPromise is not a function(…)

Just add this import :

import 'rxjs/add/operator/toPromise';

Now you can test your api by clicking on the button ,you should see the response with your data on the console .

If you have finished developing your application it's time for production where we are going to place both Django and Angular apps on the same domain .

Production setup

In production we shouldn't use two urls or domains instead we have to build our Angular app then serve it through Django in order to get everything on the same domain .

Building Angular app

You can build your Angular web app by running :

npm run build 

which outputs the production ready app in frontend/dist .

Configuring Django staticfiles

Next lets configure Django staticfiles to serve JavaScript and CSS from frontend/dist .

Open your project settings.py then add :

ANGULAR_APP_DIR = os.path.join(BASE_DIR, 'frontend/dist')

STATICFILES_DIRS = [
    os.path.join(ANGULAR_APP_DIR),
]   

Next set STATIC_ROOT to a folder where collectstatic will put collected static files

STATIC_URL = '/static/'

STATIC_ROOT = os.path.join(BASE_DIR, 'static')

Now collectstatic will be able to collect the static files from our frontend correctly .

It's time to run collectstatic so open your terminal ,navigate inside your project root folder then run :

python manage.py collectstatic

All your project static files will be placed inside static folder and will be available from /static/ url

Serving Angular App index.html file with Django

all static files including index.html will be copied to static folder so we need a way to serve index.html from static folder when the user visits /

Luckily for us django provides a view function that uses the static folder as a templates folder

in urls.py add this code :

from django.contrib.staticfiles.views import serve

urlpatterns = [

    url(r'^$', serve,kwargs={'path': 'index.html'}),    

There is one another thing to do in order for our angular app to be served correctly .

Since Angular app includes files from root path :

<script type="text/javascript" src="inline.bundle.js"></script>
<script type="text/javascript" src="polyfills.bundle.js"></script>
<script type="text/javascript" src="styles.bundle.js"></script>
<script type="text/javascript" src="vendor.bundle.js"></script>
<script type="text/javascript" src="main.bundle.js"></script></body>

We need either to change that to /static/ or better yet add a redirect url to Django urls to redirect to /static/ whenever a request to a static file is made :

from django.views.generic import RedirectView
from django.contrib.staticfiles.views import serve

urlpatterns = [

    url(r'^$', serve,kwargs={'path': 'index.html'}),    
    url(r'^(?!/?static/)(?!/?media/)(?P<path>.*\..*)$',
    RedirectView.as_view(url='/static/%(path)s', permanent=False)),
    #...
]

Conclusion


So we have seen how to integrate and serve Angular app ( as frontend) with Django as backend in both development and production environments .

We have also seen how to use the Angular CLI proxy to solve the issues related to CORS and Same Origin Policy when using two domains or actually ports in development phase .



mrnerd is a Web Entrepreneur & Developer, Digital Marketer and a Passionate Blogger. He writes about software and website development,money making tips and SEO etc.. He is a tech addict and a computer geek with a lot of hobbies.