I'm working on Django-react project and having issue with rendering React component on Django server. To be precise, React always render me the same content even if I change it. I'm using Webpack, Babel-loader and running it on localhost.
project/templet/frontend/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Django-React</title>
</head>
<body>
<div id="app" class="columns"><!-- React --></div>
{% load static %}
<script type='text/javascript' src="{% static "frontend/main.js" %}"></script>
</body>
</html>
Entry point:
import ReactDOM from "react-dom";
import App from './components/App';
ReactDOM.render(<App/>, document.getElementById("app"));
Scripts in package.json:
"scripts": {
"dev": "webpack --mode development ./frontend/src/index.js --watch ./frontend/static/frontend/main.js",
"build": "webpack --mode production ./frontend/src/index.js --output ./frontend/static/frontend/main.js"
}
Babel config:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
}
};
So, when I write some content in App component (eg. render div with "test") I can see it in my browser, but when I want to change it, and after refresing page, get the same content from div tag
From what i can understand from your question, When you first render a div with 'test' in it, it renders, but after that, further changes dont update.
Its because the javascript is not being updated on Django and you need to use collectstatic to sync the builds which is not very efficient.
The way to go is to use django-webpack-loader and webpack-bundle-tracker.
install webpack-bundle-tracker
npm install --save-dev webpack-bundle-tracker
install django-webpack-loader:
pip install django-webpack-loader
django-webpack-loader is a python package which injects link and script tag for the bundles which webpack generates dynamically.
webpack-bundle-tracker plugin emits necessary information about webpack compilation process to a json file so django-webpack-loader can consume it.
For webpack to track changes made in your App, You need to create a server that monitors changes in your React app and bundle your JS.
Note: We make use of a node server.
// Webpack Server
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const config = require('./webpack.server.config');
new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath,
hot: true,
inline: true,
historyApiFallback: true
}).listen(3000, '0.0.0.0', function (err, result) {
if (err) {
console.log(err)
}
console.log('Listening at 0.0.0.0:3000')
});
The config files used by webpack.server.js would be at webpack.server.config.js
// webpack.server.config.js
const path = require("path");
const BundleTracker = require('webpack-bundle-tracker');
module.exports = {
mode: 'development',
entry: {
main: './frontend/src/index.js',
devServer: 'webpack-dev-server/client?http://localhost:3000'
},
output: {
filename: "[name].js",
path: path.resolve('./frontend/static/frontend/bundles/'),
publicPath: 'http://localhost:3000/frontend/static/frontend/bundles/', // django-webpack-loader overrides django's STATIC_URL with this path
},
plugins: [
new BundleTracker({filename: './frontend/webpack-stats.json'}), // Pass the correct path to the WEBPACK_LOADER in django settings
],
devServer: {
contentBase: './frontend/static/frontend/bundles/',
},
};
Note that the server will by default keep the bundles in memory and
not write to disk
When the server is stopped, there would be no sign of the bundled files as they were not compiled to memory.
To build your files to memory in development, create another config file at webpack.dev.config.js
// webpack.dev.config.js
const path = require("path");
const BundleTracker = require('webpack-bundle-tracker');
module.exports = {
mode: 'development',
entry: {
main: './frontend/src/index.js',
},
output: {
filename: "[name].js",
path: path.resolve('./frontend/static/frontend/bundles/'),
},
plugins: [
new BundleTracker({filename: './frontend/webpack-stats.json'}), // Pass the correct path to the WEBPACK_LOADER in django settings
],
};
While to build your files for Production, create another config file at webpack.prod.config.js
// webpack.prod.config.js
const path = require("path");
const BundleTracker = require('webpack-bundle-tracker');
module.exports = {
mode: 'production',
entry: {
main: './frontend/src/index.js',
},
output: {
filename: "[name].js",
path: path.resolve('./frontend/static/frontend/dist/'),
},
plugins: [
new BundleTracker({filename: './frontend/webpack-stats-prod.json'}), // Pass the correct path to the WEBPACK_LOADER in django settings
],
};
In your Django settings;
import sys
import os
# Assuming this is your base directory
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Also assuming this is your base directory
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
# In development
WEBPACK_LOADER = {
'DEFAULT': {
'BUNDLE_DIR_NAME': 'bundles/',
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'), # Path to 'webpack-stats.json'
}
}
# In production
if not DEBUG:
WEBPACK_LOADER['DEFAULT'].update({
'BUNDLE_DIR_NAME': 'dist/',
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats-prod.json') # Path to 'webpack-stats-prod.json'
})
INSTALLED_APPS = (
...
'webpack_loader',
)
Your index.html should look like this now;
{% load render_bundle from webpack_loader %} <<< Add this.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Django-React</title>
</head>
<body>
<div id="app" class="columns">
<!-- React -->
</div>
{% render_bundle 'main' %} <<< Add this.
</body>
</html>
Now, Your package.json should look like this;
"scripts": {
"dev": "node ./webpack.server.js",
"build-dev": "webpack --mode development --config ./webpack.dev.config.js",
"build-prod": "webpack --mode production --config ./webpack.prod.config.js"
}
So, To develop with automatic bundling, just run:
npm run dev
To build your files to memory after the webpack server is stopped, just run:
npm run build-dev
And finally, to build with production optimizations, just run:
npm run build-prod
I tried to modify to your project, feel to adjust according to your project structure. Check the below referrences to guide you better. They helped me out!
References:
Let's modernize the way we handle frontend code with Django
Using Webpack transparently with Django + hot reloading React components as a bonus
Modern Django: Part 1: Setting up Django and React
Related
I have a Django + React project running at localHost:8000.
VIEWS.PY
def index(request):
return render(request, 'frontend/index.html')
INDEX.HTML
{% load static %}
<script src="{% static "frontend/main.js" %}"></script>
When user click a button in main.js file, it request for 0.bundle.js to server, but server fails to serve it.
I can't do loading manually (<script src="{% static "frontend/0.main.js" %}"></script> This scripts load the 0.bundle.js but I want dynamic import).
As you can see in attached image, the frontend JS file requesting file at rootLocalhost.
Any ideas or directions will be appreciated .
configured webpack as below,new chunk files will point to Django static files
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
{
test: /\.css$/,
loader: ["style-loader", "css-loader"],
},
{
test: /\.s[ac]ss$/i,
use: [
// Creates `style` nodes from JS strings
"style-loader",
// Translates CSS into CommonJS
"css-loader",
// Compiles Sass to CSS
"sass-loader",
],
},
],
},
output: {
chunkFilename: "static/frontend/[name].bundle.js",
},
};
Short Version
When I visit localhost my django/vue app renders fine. But when I click into a link, rather than being brought to localhost/about I'm brought to http://localhost/http:/0.0.0.0:8080/about because of my webpack and vue.config.js settings.
Details
I have an application running (in docker-compose) Django on the backend and Vue on the frontend. The app uses django-webpack-loader and webpack-bundle-tracker to render the application in Django.
# Django settings.py
WEBPACK_LOADER = {
"DEFAULT": {
"CACHE": DEBUG,
"BUNDLE_DIR_NAME": "/bundles/",
"STATS_FILE": os.path.join(FRONTEND_DIR, "webpack-stats.json"),
}
}
# Django urls.py
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, re_path
from django.views.generic import TemplateView
urlpatterns = [
path("admin/", admin.site.urls),
re_path("^.*$", TemplateView.as_view(template_name="application.html"), name="app"),
]
if settings.DEBUG:
urlpatterns = (
urlpatterns
+ static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
)
<!-- Template -->
{% load render_bundle from webpack_loader %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="/favicon.ico">
<title>My Website</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons">
</head>
<body>
<noscript>
<strong>We're sorry but this application doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app">
<app></app>
</div>{% render_bundle 'app' %}<!-- built files will be auto injected -->
</body>
</html>
// vue.config.js
const BundleTracker = require("webpack-bundle-tracker");
module.exports = {
publicPath:
process.env.NODE_ENV === "production"
? "https://example.com"
: "http://0.0.0.0:8080/",
outputDir: "./dist/",
chainWebpack: config => {
config.optimization.splitChunks(false);
config
.plugin("BundleTracker")
.use(BundleTracker, [{ filename: "webpack-stats.json" }]);
config.resolve.alias.set("__STATIC__", "static");
config.devServer
.public("http://0.0.0.0:8080")
.host("0.0.0.0")
.port(8080)
.hotOnly(true)
.watchOptions({ poll: 500 })
.https(false)
.headers({ "Access-Control-Allow-Origin": ["*"] });
}
};
When I fire up the application, visiting localhost works fine, but if I try to click one of the vuetify links, it brings me to http://localhost/http:/0.0.0.0:8080/about (presumably because that's the URL I've specified as the publicPath for the dev environment). If I replace localhost with http://0.0.0.0 I get the same undesired redirection to http://0.0.0.0/http:/0.0.0.0:8080/. However, if I visit localhost:8080 I can browse the app and links click through to their proper location (eg localhost:8080/about).
I tried removing my configuration of the publicPath but the Django application is unable to serve up the vue app when I do so: the app loads a blank page with a javascript error of SyntaxError: expected expression, got '<' in app.js. I assume that's because it's trying to load http://0.0.0.0/app.js and receiving the same template response that http://0.0.0.0 serves up.
So I think I have a few options:
1) Should I keep the default vue.config.js publicPath option, and configure django to serve up app.js from the proper location (and if so, what is that location?)
2) Can I configure vue router to use a different root URL from that specified in publicPath?
If this configuration isn't possible, I can switch over to having the SSR take place in Nuxt rather than Django, but I'm curious to learn more about what sort of configuration issue is at play here.
You can configure vue router to use a different root URL.
Just replace in router.js
base: process.env.BASE_URL,
With
base: '/',
I believe is resolved by adding
mode: "history",
to your router.js file.
I am trying to use ember-index add on to generate index.jsp file instead of index.html. I have installed the ember-index addon and did the required changes as well.
My config/environment.js file looks like below
/* eslint-env node */
'use strict';
module.exports = function(environment) {
let ENV = {
modulePrefix: 'user-profile',
output: 'index.jsp',
environment,
rootURL: '/',
locationType: 'hash',
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. 'with-controller': true
},
EXTEND_PROTOTYPES: {
// Prevent Ember Data from overriding Date.parse.
Date: false
}
},
'ember-index': {
output: 'index.jsp',
content: {
file: 'example.txt',
includeInOutput: true,
includeInIndexHtml: false
}
},
APP: {
// Here you can pass flags/options to your application instance
// when it is created
}
};
return ENV;
};
Also my example.txt file looks like
<meta content="Example">
<h2>html to jsp</h2>
and finally in my index.html I have added below snippet
{{content-for 'ember-index'}}
But now when i am trying to build the ember project with ember build I am not able to see index.jsp file in my dist folder.
With a fresh app, this addon works as expected. Walk through the steps for the minimal example using a new Ember app, and you'll find what you missed.
Are you sure you're looking in the right place for the index.jsp? It is at dist/export/index.jsp after running ember build.
Did you forget to add the tag in your index.html? The readme specifies that for your files to be used, they must be specified like this:
<!-- app/index.html -->
<!DOCTYPE html>
<html>
<head>
...
{{content-for 'ember-index-1'}}
{{content-for 'ember-index-2'}}
</head>
<body>
...
</body>
</html>
The application runs fine in development, but once in production the generated webpack bundle file is serving the contents of index.html and not all the bundled modules and dependencies.
Whenever I check the bundle file on the server, everything seems alright, but the same path is served differently on the client.
webpack.config.js
//require our dependencies
var path = require('path')
var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')
module.exports = {
//the base directory (absolute path) for resolving the entry option
context: __dirname,
//the entry point we created earlier. Note that './' means
//your current directory. You don't have to specify the extension now,
//because you will specify extensions later in the `resolve` section
entry: [
'./assets/js/index'
],
output: {
path: path.resolve('./assets/bundles/'),
filename: "[name]-[hash].js"
},
plugins: [
//tells webpack where to store data about your bundles.
new webpack.NoErrorsPlugin(), // don't reload if there is an error
new BundleTracker({filename: './webpack-stats.json'}),
//makes jQuery available in every module
new webpack.ProvidePlugin({
jQuery: 'jquery',
'window.jQuery': 'jquery'
})
],
module: {
loaders: [
// we pass the output from babel loader to react-hot loader
{
test: [/\.js$/, /\.es6$/, /\.jsx?$/],
exclude: /node_modules/,
loaders: ['babel'],
},
{ test: /\.css$/, loader: "style-loader!css-loader" }
]
},
resolve: {
//tells webpack where to look for modules
modulesDirectories: ['node_modules'],
//extensions that should be used to resolve modules
extensions: ['', '.js', '.jsx']
}
}
Served Content:
Uncaught SyntaxError: Unexpected token <
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<!--<title></title>-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="/static/jquery/dist/jquery.js"></script>
<script type="text/javascript" src="/static/materialize-css/dist/js/materialize.min.js"></script>
</head>
<body>
<div id="container"></div>
<script type="text/javascript" src="/static/assets/bundles/main-210eb139915d8d6abedf.js" ></script>
</body>
Thanks in advance.
I can't get my jasmine unit-tests to load the a component for testing.
Webstorm is telling me that everything is good to go in terms of syntax. But when I load live-server to have the unit test load it doesn't seem to work.
I have it compiling to a test directory at root.
So here is my directory tree (abv for the ease):
-- client(src)
|-- common
|---- welcome.component.ts
|---- welcome.spec.ts
-- node_modules
-- tests(compiled spec and components)
|-- client(src)
|---- common
|------ welcome.component.ts
|------ welcome.spec.ts
--unit-tests.html
--systemjs.config.js
live server is giving me this:
GET /tests/client/common/welcome.component 404
welcome.spec.ts
/// <reference path="../../typings/main/ambient/jasmine/index.d.ts" />
import {WelcomeComponent} from './welcome.component';
describe('Welcome tests', () => {
let welcome:WelcomeComponent;
beforeEach(() => {
welcome = new WelcomeComponent('sir');
});
it('Nombre should equal sir', () => {
expect(welcome.nombre).toEqual('sir');
})
});
unit-tests.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>Tests</title>
<link rel="stylesheet" href="node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
<script src="node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
<script src="node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
<script src="node_modules/jasmine-core/lib/jasmine-core/boot.js"></script>
</head>
<body>
<!-- #1. add the system.js library -->
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
// #2. Configure systemjs to use the .js extension
// for imports from the app folder
System.config({
packages: {
'client': {defaultExtension: 'js'}
}
});
// #3. Import the spec file explicitly
System.import('tests/client/common/welcome.spec.js');
System.import('tests/client/common/header.spec.js')
// #4. wait for all imports to load ...
// then re-execute `window.onload` which
// triggers the Jasmine test-runner start
// or explain what went wrong.
.then(window.onload)
.catch(console.error.bind(console));
</script>
</body>
</html>
systemjs.config.js
(function(global) {
// map tells the System loader where to look for things
var map = {
'app': 'tests/client', // 'dist',
'rxjs': 'node_modules/rxjs',
'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
'#angular': '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 can add more if you need me too, the header.spec works great, but it's self-container. (expect true = true, etc)
SystemJS should be loading this for the unit-tests but it does not, the site itself works great so the Welcome.component is fine. But for some reason I cannot load the unit test.
Where would I be going wrong? I feel like this is a config issue, but I am not very familiar with unit-tests.
I had a similar issue the other day, take a look at this comment. If you have a spec-bundle.js or something similar, make sure var testing and var browser is referring to RC1 and not beta.
Also, if you have this somewhere: testing.setBaseTestProviders(
browser.TEST_BROWSER_STATIC_PLATFORM_PROVIDERS,
browser.TEST_BROWSER_STATIC_APPLICATION_PROVIDERS);
this is how it should look for RC1, it was different for beta