How to import non-module(!) JavaScript into Polymer 3.0 - javascript

Polymer 3 uses import to load external Javascript. For example
import {GoogleCharts} from 'google-charts';
However, it seems for this to work, the external library should use exports.
I am trying to use mapbox-gl.js library. This library, installed with:
npm install mapbox-gl
does not seem to export anything.
In HTML5, you can use mapbox-gl as follows:
<script src="node_modules/mapbox-gl/dist/mapbox-gl.js"></script>
<link href="node_modules/mapbox-gl/dist/mapbox-gl.css" rel="stylesheet" />
<script>
const options = {...}
const map = new mapboxgl.Map(options);
</script>
I tried to use 'import' to load mapbox-gl:
import {mapboxgl} from './node_modules/mapbox-gl/dist/mapbox-gl.js';
import mapboxgl from './node_modules/mapbox-gl/dist/mapbox-gl.js';
This doesn't work:
Uncaught SyntaxError: The requested module './node_modules/mapbox-gl/dist/mapbox-gl.js' does not provide an export named 'mapboxgl'
Uncaught SyntaxError: The requested module './node_modules/mapbox-gl/dist/mapbox-gl.js' does not provide an export named 'default'
So, then I tried to add the script and css to document.head from inside the module javascript (<script type="module">..</script>):
// load external mapbox-gl.js script
const mapboxgljs = document.createElement('script');
mapboxgljs.setAttribute('src', 'node_modules/mapbox-gl/dist/mapbox-gl.js');
document.head.appendChild(mapboxgljs);
// load external mapbox-gl.css
const mapboxcss = document.createElement('link');
mapboxcss.setAttribute('href', 'node_modules/mapbox-gl/dist/mapbox-gl.css');
mapboxcss.setAttribute('rel', 'stylesheet');
document.head.appendChild(mapboxcss);
This does not seem to work either. If I try to use mapbox as follows:
const map = new mapboxgl.Map(options)
I am getting:
Uncaught ReferenceError: mapboxgl is not defined
Edit:
Commenters #Salketer and #barbsan showed correct import syntax for this kind of library:
import 'node_modules/mapbox-gl/dist/mapbox-gl.js';
or
import * as mapboxgl from 'node_modules/mapbox-gl/dist/mapbox-gl.js';
Both now result in the following error message:
Uncaught TypeError: Cannot set property 'mapboxgl' of undefined
This means the mapbox-gl library now gets loaded and interpreted. However, the mapbox-gl code contains the line:
window.mapboxgl = something;
ES6-module scope does not have access to the browser 'window' object, so the code fails. See also Is there an es6 module-scope equivalent to `window`?.
For now, I am adding the HTML5 <script></script> and <link /> tags (see above) to the index.html of my project. This works, but the idea of components and modules is to load dependencies from inside those components and modules?

You are close.
With your original code to load script:
// load external mapbox-gl.js script
const mapboxgljs = document.createElement('script');
mapboxgljs.setAttribute('src', 'node_modules/mapbox-gl/dist/mapbox-gl.js');
document.head.appendChild(mapboxgljs);
At this moment, if you use the instance provided by the script: const map = new mapboxgl.Map(options)
You will get error: Uncaught ReferenceError: mapboxgl is not defined
Reason: the script isn't loaded yet, so the instance window.mapboxgl is undefined.
Solution: You have to wait until the script finished loading, using event listener. Add this code into your loading script code:
mapboxgljs.addEventListener('load', function() {
// mapboxgl is available here, do whatever you want
const map = new mapboxgl.Map(options)
})

For me the solution was to define the import without mustasch {}:
import mapboxgl from 'mapboxgl'

Just do:
import("node_modules/mapbox-gl/dist/mapbox-gl");
// do my stuff with global dependency
or if import is slower than the code to be execute
import("node_modules/mapbox-gl/dist/mapbox-gl").then(() => {
// do my stuff with global dependency
})

Related

Dynamically creating an element on which the react app mounts

I want to create a build of my javascript application using react which currently is just a single file defined as below. It does nothing but creates a div and keeps on changing the color of the hello-world text after it mounts.
import * as React from "react";
import * as ReactDOM from "react-dom";
class App extends React.Component {
componentDidMount() {
const colorBox = ["red","blue","green"];
const element = document.getElementById("my-div");
setInterval(() => {
element.style.color = colorBox[parseInt(Math.floor(Math.random() * colorBox.length))];
}, 3000);
}
render() {
return (
<div id={"my-div"}>
Hello, World.
</div>
);
}
}
ReactDOM.render(<App />,
document.body.appendChild(document.createElement("div")));
I am using parcel js as a bundler. I bundle my javascript as parcel build index.js and this creates a build of my javascript file inside dist folder. So far, so good. But the moment, I load this javascript using a script tag on my website as:
<script type="text/javascript" src="https://mycdn.com/bundle.index.js"></script>
It throws an error
index.js:16 Uncaught TypeError: Cannot read property 'appendChild' of null
at Object.parcelRequire.Focm.react (index.js:16)
at f (index.js:1)
at parcelRequire.YOwE (index.js:1)
at index.js:1
Why it cannot access appendChild of document.body? I also checked that document.body is null. What is happening here? What is it that I am missing?
First of all I'm not sure this is a great idea to have code in the bundle that directly affects the page like this.
My guess as to why this is happening is it has to do with when the bundle is loaded in the page. Is it in head? If so document.body may not be available.
It is probably best if you are going to do something like this to use the events associated with the document being ready.
This can get complicated depending on your needs, but if you have access to jQuery, you can use .ready().
If not you can use various vanilla js options.
I guess your problem is related to the bundle or you ask for an element before it is available because it is working fine here: https://jsfiddle.net/rL6dnhps
If you load it from the head, try to put at the end of the body in order to wait for the body to be ready or wrap it inside an IIFE like below:
(function() {
// the DOM will be available here
ReactDOM.render(<App />,
document.body.appendChild(document.createElement("div")));
})();

How do I call a function in an external js file using typescrpt

We are trying a POC of adding Typescript and Webpack to our Angularjs project.
I am able to get my webpack bundle to generate, however at runtime the program cannot find the various functions in my validator.js. Can you please offer some advice?
login-view.components.ts
declare var findFormNode: any; //function in validator.js
//LogInUser
self.login = function ($event, command) {
if (findFormNode($event.target.id)) {
...
}
}
main.ts is importing the file
import "./../../../CommonStaticFiles/include/js/Validators.js";
bundle.js
eval("/* WEBPACK VAR INJECTION */(function($) {/*\r\n\r\n VALIDATORS\r\n\r\n ... n\n\nfunction findFormNode(
error
ReferenceError: findFormNode is not defined
at LoginController.self.login (login-view.component.ts:28)
at fn (eval at compile (angular.js:NaN), <anonymous>:4:267)
at callback (angular.js:29019)
In order for your functions to be properly imported, there are few things that you have to make sure of.
First, make sure you are exporting your functions correctly. Here's an example of how to export a function from Validator.js:
export const validateFunc1 = ():void => {};
Next, you have to make sure you are using proper import syntax. In order to import the function above, you would do the following:
import {validateFunc1} from "./../../../CommonStaticFiles/include/js/Validators.js";
Alternatively, if you want to import all exported functions at once, then you can use this syntax:
import * as validatorFuncs from "./../../../CommonStaticFiles/include/js/Validators.js";
Lastly, check that the location of Validators.js is correct. It's a common mistake to be looking in the wrong directory. Your code editor can usually help you find the right path to use.

Can't figure out how to import modules in browser with javascript

This is a simple problem. I'm attempting to import modules from one javascript file to another, and then run it on Chrome. I'm using 2 javascript files and an html file, all in the same folder:
first js file (testfile1.js):
import {test} from 'testfile2.js';
test.func();
second js file (testfile2.js):
let f = function() {
console.log("TEST");
}
let test = {
func: f
};
export test;
The html file is plain, empty html file with a link to testfile1.js script in the header:
<script type="text/javascript" src="testfile1.js"></script>
Whenever I open the html file in chrome, I get the error:
testfile1.js:1 Uncaught SyntaxError: Unexpected token {
When I removed the brackets in the import statement, I get an unexpected identifier statement. Isn't this the proper way to import modules in the browser? Why is it not working at all?
Modules require type="module" not "text/javascript"
As per Jaromanda X's comment, you need to change the value of the type attribute of the <script> tag to "module" as import { test } from 'testfile2.js' is module code.
<script type="module" src="testfile1.js" />
What about dynamic import()
If you really don't feel like using type="module", any javascript file is allowed to use the dynamic import() syntax, even without type="module".
However, the dynamic import has a caveat, the function import() returns a promise, therefore, you are unable to use it synchronously. You must either await or .then a dynamic import to use the value it resolves to.
import('testfile2.js').then(({ test }) => {
// your code
});

Typescript + Google Maps API + infobox.js Uncaught TypeError: InfoBox is not a constructor

Have a project Vue.js + Typescript using Google Maps API, try to import infobox.js (npm package google-maps-infobox-window) library into and getting an error in the console:
Uncaught TypeError: InfoBox is not a constructor
Library doesn't typed yet, so all is remains - import as any, without typings. I tried 3 ways to do it:
import * as InfoBox 'google-maps-infobox-window'
var InfoBox = require('google-maps-infobox-window')
making *d.ts file with manual module/function declaration
And none of these actions has no effect, InfoBox constructor still not found.
Before import infobox.js I already has imported googlemaps (via script tag) and RichMarker.js (via require('')) and those are works perfectly.
Can anyone help with it?
Also, I tried to add "allowJs": true to tsconfig.json, still nothing
Code:
import { } from '#types/googlemaps';
const RichMarker = require('js-rich-marker');
//problems started below when I use
import { InfoBox } from 'google-maps-infobox-window';
// or this
const InfoBox = require('google-maps-infobox-window');
//...
//and try to use imported function (constructor)
let infoBox = new InfoBox({content: boxText});
In search of problem solution I tried a lot of possible ways and all npm packages related to infobox.js.
Finally, I found 'google-maps-infobox' (https://www.npmjs.com/package/google-maps-infobox), which has no exhaustive description, but it was working one at least. Moreover, it has even *.d.ts file, which provide proper import in ts-project.
So, I installed this dependency and added in import section of component
import InfoBox from 'google-maps-infobox';
and use it in this way
//in some method (function)
let infoBox: InfoBox = new InfoBox({
content: //some html markup
});
infoBox.setPosition(mapObject.getPosition());
infoBox.open(this.map);
Also, this package works good with require() import, except of InfoBox-object will be typed as any

Wiriting 'JSON.stringify' generates an error when Vue component loads

I am writing a Vue plugin, but anytime I try to add JSON.stringify to it I get a runtime error in the browser and the page goes blank. Anywhere in the Plugin file I write JSON.stringify I get the error:
Uncaught TypeError: Cannot assign to read only property 'exports' of object '#'
If I write JSON.stringify to a component directly (in the 'created' lifecycle hook, for example), nothing happens.
This is not an error during the webpack compilation, it happens only in the browser inside an eval. The line where the error occurs looks like this:
eval("/* WEBPACK VAR INJECTION */(function(module) {Object.defineProperty(__webpack_exports__ ....")
This is what is written in the plugin file
var MyPlugin = function () {
}
JSON.stringify({})
MyPlugin.secret = 'vue-plugin-secret'
MyPlugin.install = function (Vue, options) {
}
module.exports = MyPlugin
// export default MyPlugin
And this is how I get it in the component:
var MyPlugin = require('./MyPlugin')
If I comment the JSON.stringify line the error stops.
#edit
did what #Saurabh suggested, used 'import' so I had to change the export form of the plugin
//MyPlugin.js
export default MyPlugin
And in the .vue component I did
import MyPlugin from './MyPlugin';
this fixed the error, but what is the reason for this? some webpack configuration? (I'm using the default webpack configuration of vue-cli)

Categories