I am using Webpack + html-webpack-plugin to build all my static files. The thing is, when I am using it with Google Maps API, it's not working.
I have this code:
var map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: -34.397, lng: 150.644},
zoom: 6
});
// the other code, irrelevant
}
And a HTML file:
<!doctype html>
<html>
<head>
</head>
<body>
<div id="map"></div>
<script async="async" defer="defer"
src="https://maps.googleapis.com/maps/api/js?key=<token here>&callback=initMap">
</script>
<script src="script.js"></script>
</body>
</html>
If I run just this file, everything works fine. But if I run this with, webpack it complaints about 'initMap is not a function'. I've looked inside the output, and it seems like initMap is declared not as global function, but as a function inside a module or something like that, so maybe that's the issue.
How should I use Google Maps API with webpack? I know that I can bundle some libs with my script, such as react, should I do the same? What should be the approach here?
UPD: Here is my webpack.config.js:
/* eslint-disable */
const path = require('path')
const fs = require('fs')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const nodeModules = {}
fs.readdirSync('node_modules')
.filter(function(x) {
return ['.bin'].indexOf(x) === -1
})
.forEach(function(mod) {
nodeModules[mod] = 'commonjs ' + mod
})
const htmlMinifierObj = {
collapseWhitespace: true,
removeComments: true
}
module.exports = [
// the first object compiles backend, I didn't post it since it's unrelated
{
name: 'clientside, output to ./public',
entry: {
script: [path.join(__dirname, 'clientside', 'script.js')]
},
output: {
path: path.join(__dirname, 'public'),
filename: '[name].js'
},
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel',
query: { presets:['es2015', 'stage-0'] }
}
],
},
plugins: [
//new webpack.optimize.UglifyJsPlugin({minimize: true}),
new HtmlWebpackPlugin({
template: 'clientside/index.html',
inject: 'body',
chunks: ['script'],
minify: htmlMinifierObj
})
],
}]
And the output HTML is (I've removed importing script.js from my source file, because it's added by webpack, and turned off the minimization, just for readability):
<!doctype html>
<html>
<head>
</head>
<body>
Login
<div id="map"></div>
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCGSgj5Ts10UdapzUnWlr34NS5cuoBj7Wg&callback=initMap">
</script>
<script type="text/javascript" src="script.js"></script></body>
</html>
In script.js
After your function declaration, add the function to the global scope, like this:
function initMap() {
// Some stuff
}
window.initMap = initMap;
Related
I have a react app and I have generated a bundle.min.js by creating webapack.config.js which looks like the following:
const path = require('path');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const glob = require('glob');
module.exports = {
entry: {
"bundle.js": glob.sync("build/static/?(js|css)/main.*.?(js|css)").map(f => path.resolve(__dirname, f)),
},
output: {
filename: "build/static/js/bundle.min.js",
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
plugins: [new UglifyJsPlugin()],
}
I copied the bundle.min.js from the dist\build\static\js folder and included it in the script tag inside my HTML code index.html as shown below:
<html>
<head></head>
<body>
<h1>Bundle Testing</h1>
<p>
Here it comes:
</p>
<script src="bundle.min.js"/>
</body>
</html>
But it doesn't show my app. How should I render my app content inside this HTML code? I'm sure I'm missing something.
I'm working on a project using webpack, I'm trying to use the webpack-dev-serve package to preview my changes. However when I start the server and load the page the object created by webpack does not have the functions on it Uncaught TypeError: test.a is not a function, when I console log the object webpack creates I can see its an empty object Object { }. Using the exact same webpack config to build the package and including it on my page works fine.
Here is my webpack.config:
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
target: "web",
output: {
filename: "test.js",
library: {
name: "test",
type: "var"
}
},
module: {
rules: [{
exclude: /node_modules/
}]
},
resolve: {
symlinks: false
},
plugins: [
new HtmlWebpackPlugin({
title: "Development",
template: "./src/index.html",
scriptLoading: "blocking",
inject: "head"
})
]
};
My index.js is very simple:
export function a(){
console.log("A has been called");
}
My index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<script>
console.log(test);
test.a();
</script>
</body>
</html>
I found a fix from the problem here:
WebPack output.library.type var is undefined
After adding injectClient: false to devServer, and removing scriptloading and inject from the HtmlWebpackPlugin config, webpack serve now works as expects.
So, what I'm trying to do is, generate a html file called index.html based on template.html, that have styling based on style.css and a function handleClick that declared in index.js. The code below works for the styling, but the handleClick is not working. Is this possbile?
This is my webpack.config.js
const path = require('path')
const HTMLWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: {
'page': './src/index.js'
},
output: {
path: path.join(__dirname, '/dist'),
filename: "[name].js"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
'babel-loader'
]
},
{
test: /\.css$/,
use: [
"style-loader",
"css-loader"
]
}
]
},
plugins: [
new HTMLWebpackPlugin({
filename: 'index.html',
template: './src/template.html'
})
]
}
this is my index.js
require('./style.css');
function handleClick(){
alert("called from index")
}
this is my template.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>My Page's Title</title>
</head>
<body>
<div>This background should be blue</div>
<button onclick="handleClick()"> click me </button>
</body>
</html>
and this is my style.css
div {
background-color:blue;
}
The better way is to add addEventListener to that element using your js file. You can do it like this:
<button id="buttonWithFunction"> click me </button>
<script>
// pure js
document.getElementById('buttonWithFunction').addEventListener('click', handleClick);
//with jquery loaded
$('#buttonWithFunction').on('click',function() {
handleClick();
})
You may need to wrap the following in a document.onload for that to work.
Suggest to check namespace of index.js - as i expect, webpack will wrap it in a namespace.
Try to define function on a window namespace.
window.handleClick = () => console.log('Clicked');
I have the following externals dependencies on my webpack.config.prod.js file:
const webpack = require('webpack');
const path = require('path');
const config = {
mode: 'production',
entry: './src/app.js',
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'app.js',
libraryTarget: 'amd',
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: 'babel-loader'
}
]
},
externals: {
react: 'react',
'react-dom': 'react-dom'
},
};
module.exports = config;
and I have the following script on my html file:
<!DOCTYPE html>
<html>
<head>
<title>React App</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js" crossorigin=""></script>
<script>
(function(cdn) {
require(cdn);
['react', 'react-dom'].forEach(dep => (dep) => {
define(dep, cdn, function(b) {
return b[dep];
});
});
})(['http://localhost:8080/public/bundle.js'])
</script>
</head>
<body>
<div id="app"></div>
<script src="app.js"></script>
</body>
</html>
as you can see I'm trying to load my react and react-dom dependencies from a cdn but the app.js is loading before dependencies so I'm getting:
Uncaught Error: Mismatched anonymous define() module:
function(e,t){return function(e){var t={};function n(r){if(t[r])return
t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return
e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return
n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof
Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var
r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof
e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return
r},n.n=function(e){var t=e&&e.__esModule?function(){return
e.default}:function(){return e};return
n.d(t,"a",t),t},n.o=function(e,t){return
Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=2)}([function(t,n){t.exports=e},function(e,n){e.exports=t},function(e,t,n){"use
strict";n.r(t);var r=n(0),o=n.n(r),u=n(1),i=n.n(u);function
c(e){return(c="function"==typeof Symbol&&"symbol"==typeof
Symbol.iterator?function(e){return typeof e}:function(e){return
e&&"function"==typeof
Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof
e})(e)}function f(e,t){for(var n=0;n||Object.getPrototypeOf(e)})(e)}function
p(e,t){return(p=Object.setPrototypeOf||function(e,t){return
e.proto=t,e})(e,t)}var y=function(e){function t(){return
function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a
class as a function")}(this,t),a(this,l(t).apply(this,arguments))}var
n,r,u;return function(e,t){if("function"!=typeof t&&null!==t)throw new
TypeError("Super expression must either be null or a
function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&p(e,t)}(t,o.a.Component),n=t,(r=[{key:"render",value:function(){return
o.a.createElement("div",null,"Hello
",this.props.name)}}])&&f(n.prototype,r),u&&f(n,u),t}();i.a.render(o.a.createElement(y,{name:"Jean"}),document.getElementById("app"))}])}
http://requirejs.org/docs/errors.html#mismatch
at makeError (require.min.js:1)
at intakeDefines (require.min.js:1)
at require.min.js:1
How can I load app.js after dependencies?
I'm trying to expose a ReactClass, using WebPack, to reuse it at different html pages. Is it possible? What is the correct way? For simplicity, I'll do an example using two times at one html (But it should be used in a lot of different html in the real application)
All the source are available here:
https://github.com/mqueirozcorreia/React-Fundamentals/tree/video2
The webpack.config.js successfully creates the bundled file and the code is below:
var HtmlWebpackPlugin = require('html-webpack-plugin')
var HTMLWebpackPluginConfig = new HtmlWebpackPlugin({
template: __dirname + '/app/index.html',
filename: 'index.html',
inject: 'body'
});
module.exports = {
entry: [
'./app/index.js'
],
output: {
path: __dirname + '/dist',
filename: "index_bundle.js"
},
module: {
loaders: [
{test: /\.js$/, exclude: /node_modules/, loader: "babel-loader"}
]
},
plugins: [HTMLWebpackPluginConfig]
};
The ReactClass is at app\MyReactClass.js which the code is the following:
var React = require('react');
var MyReactClass = React.createClass({
render: function () {
return (
<div>MyReactClass = Hello {this.props.name}</div>
)
}
});
And the MyReactClass is exposed the app\index.js as follows:
...
require("expose?MyReactClass!./MyReactClass.js");
The global variable "MyReactClass" has an instance of an Object (As the alert shows), but when I try to use it at dist\index.html to ReactDOM.render(), it throws that error:
Warning: React.createElement: type should not be null, undefined,
boolean, or number. It should be a string (for DOM elements) or a
ReactClass (for composite components).
https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react.js Line
18794
dist\index.html partial code:
...
<body>
<div id="app"></div>
<div id="component1"></div>
<div id="component2"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react-dom.js" type="text/javascript"></script>
</body>
<script type="text/javascript">
alert(MyReactClass);
ReactDOM.render(React.createElement(MyReactClass, { name: 'Component1' }), document.getElementById('component1'));
ReactDOM.render(React.createElement(MyReactClass, { name: 'Component2' }), document.getElementById('component2'));
</script>
</html>
What am I doing wrong?
I just figure out what I was missing.
Just need to module.exports the MyReactClass variable to work:
module.exports = MyReactClass;
So the MyReactClass.js looks like this now
var React = require('react');
var MyReactClass = React.createClass({
render: function () {
return (
<div>MyReactClass = Hello {this.props.name}</div>
)
}
});
module.exports = MyReactClass;
https://github.com/mqueirozcorreia/React-Fundamentals/blob/ba19aa5cff21f0c4881901e0a96aed6ddaea8e18/app/MyReactClass.js