Connecting the API
Usual connection
<!DOCTYPE html>
<html>
<head>
<!-- Substitute the value of the real key instead of YOUR_API_KEY -->
<script src="https://js.api.mappable.world/v3/?apikey=YOUR_API_KEY&lang=en_US"></script>
<script src="index.js"></script>
</head>
<body>
<div id="app" style="width: 600px; height: 400px"></div>
</body>
</html>
async function initMap() {
await mappable.ready;
const {MMap, MMapDefaultSchemeLayer} = mappable;
const map = new MMap(
document.getElementById('app'),
{
location: {
center: [25.229762, 55.289311],
zoom: 10
}
}
);
map.addChild(new MMapDefaultSchemeLayer());
}
initMap();
{
"devDependencies": {
"http-server": "14.1.1"
},
"scripts": {
"start": "npx http-server ."
}
}
Install the dependencies and start the local server:
npm install
npm run start
Open application
Alert
The API is guaranteed to work in browsers whose audience is more than 0.5% according to Browserslist.
Specificities of the usual connection
- The JS API is distributed exclusively by the link that needs to be connected in the header of the document.
- API components are always loaded asynchronously.
- Components exist only in the global scope in the variable
mappable
. - Components are available only after the promise
mappable.ready
is resolved.
API loading parameters
apikey |
Required parameter The key issued in the Mappable Account. Note Key activation takes up to 15 minutes. |
lang |
Required parameter Locale. Set as
|
Promise ready
The promise mappable.ready
guarantees that all components of the main module Javascript API
are loaded, and the DOM
is built.
Alert
Event handlers like document.ready
, window.onload
, jQuery.ready
do not signal the end of loading JS API components.
Connecting via top-level-await (recommended)
<!DOCTYPE html>
<html>
<head>
<!-- Substitute the value of the real key instead of YOUR_API_KEY -->
<script src="https://js.api.mappable.world/v3/?apikey=YOUR_API_KEY&lang=en_US"></script>
<script type="module" src="index.js"></script>
</head>
<body>
<div id="app" style="width: 600px; height: 400px"></div>
</body>
</html>
import { MMap, MMapDefaultSchemeLayer } from './lib/mappable.js'
const map = new MMap(
document.getElementById('app'),
{
location: {
center: [25.229762, 55.289311],
zoom: 10
}
}
);
map.addChild(new MMapDefaultSchemeLayer());
await mappable.ready;
export const {MMap, MMapDefaultSchemeLayer} = mappable;
{
"devDependencies": {
"http-server": "14.1.1"
},
"scripts": {
"start": "npx http-server ."
}
}
Install the dependencies and start the local server:
npm install
npm run start
Open application
Specificities
-
In the script tag that loads the compiled project js, specify the attribute
type="module"
to activate support for ECMAScript Modules (ESM) and top-level-await:<script type="module" src="index.js"></script>
ESM support when using bundles
If a bundler is used to build the project, for example Webpack, then in the
package.json
file needs to add"type": "module"
-
In the file
lib/mappable.js
we are waiting for the JS API to be fully loaded, after which we export the necessary map components for use in other parts of the project:await mappable.ready; export const {MMap, MMapDefaultSchemeLayer} = mappable;
-
Using top-level-await in
lib/mappable.js
guarantees the execution ofmappable.ready
before importing map components, so the project code can be written shorter and neater (the asynchronous initMap function is no longer needed):import { MMap, MMapDefaultSchemeLayer } from './lib/mappable.js' const map = new MMap({...});
Connecting with Webpack
- Is it possible not to connect the JS API in the header of the
HTML
document? - Is it possible to call JS API components not from a global variable, but more habitually, as if they are supplied via an
npm
package? - Is it possible to make it so that you don't have to process the resolving of the promise `mappable.ready'?
Using Webpack
and its [externals
] option (https://webpack.js.org/configuration/externals /) this can be done. There are three ways, each has both advantages and limitations.
Method 1
<!DOCTYPE html>
<html>
<head>
<!-- Substitute the value of the real key instead of YOUR_API_KEY -->
<script src="https://js.api.mappable.world/v3/?apikey=YOUR_API_KEY&lang=en_US"></script>
<script src="build/bundle.js"></script>
</head>
<body>
<div id="app" style="width: 600px; height: 400px"></div>
</body>
</html>
import * as mappable from 'mappable';
async function initMap() {
await mappable.ready;
const {MMap, MMapDefaultSchemeLayer} = mappable;
const map = new MMap(
document.getElementById('app'),
{
location: {
center: [25.229762, 55.289311],
zoom: 10
}
}
);
map.addChild(new MMapDefaultSchemeLayer());
}
initMap();
const path = require('path');
module.exports = {
mode: 'development',
entry: './index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'build'),
},
externals: {
mappable: 'mappable'
},
devtool: 'cheap-source-map'
};
{
"devDependencies": {
"http-server": "14.1.1",
"webpack": "5.88.2",
"webpack-cli": "5.1.4"
},
"scripts": {
"build": "webpack",
"start": "npx http-server ."
}
}
Install the dependencies, build the project and run the local server:
npm install
npm run build
npm run start
Open application
Specificities
-
On the
HTML
page, we are still connecting theJS API
loader:<head> <script src="https://js.api.mappable.world/v3/?apikey=YOUR_API_KEY&lang=en_US"></script> </head>
Successful loading of the script ensures that the
mappable
variable appears in global access. -
In
webpack.config.js
declaring an external variablemappable
:module.exports = { // ... externals: { mappable: 'mappable' } };
Thanks to this, the project code makes it possible to import
mappable
as if themappable
code is delivered not through a global variable, but through annpm
package:import * as mappable from 'mappable'; // ...
Method 2
<!DOCTYPE html>
<html>
<head>
<script src="build/bundle.js"></script>
</head>
<body>
<div id="app" style="width: 600px; height: 400px"></div>
</body>
</html>
import * as mappable from 'mappable';
async function initMap() {
await mappable.ready;
const {MMap, MMapDefaultSchemeLayer} = mappable;
const map = new MMap(
document.getElementById('app'),
{
location: {
center: [25.229762, 55.289311],
zoom: 10
}
}
);
map.addChild(new MMapDefaultSchemeLayer());
}
initMap();
const path = require('path');
module.exports = {
mode: 'development',
entry: './index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'build'),
},
externalsType: 'script',
externals: {
// Substitute the value of the real key instead of YOUR_API_KEY
mappable: ['https://js.api.mappable.world/v3/?apikey=YOUR_API_KEY&lang=en_US', 'mappable']
},
devtool: 'cheap-source-map'
};
{
"devDependencies": {
"http-server": "14.1.1",
"webpack": "5.88.2",
"webpack-cli": "5.1.4"
},
"scripts": {
"build": "webpack",
"start": "npx http-server ."
}
}
Install the dependencies, build the project and run the local server:
npm install
npm run build
npm run start
Open application
Specificities
This is a modified first method:
-
It is no longer necessary to connect the tag
<script>
in the header of theHTML
page. This is done bywebpack
itself. -
In
webpack.config.js
the external variablemappable
is also declared, but it specifies the path to the API loader:module.exports = { // ... externalsType: 'script', externals: { mappable: ['https://js.api.mappable.world/v3/?apikey=YOUR_API_KEY&lang=en_US', 'mappable'] } };
Limitations of method 1 and 2
In both of the above methods, not a ready-made API is imported, but a lightweight loader. The components become fully accessible only after the mappable.ready
promise is resolved. This means that if your client code is divided into its own modules, and the JS API is used in several of your modules at once, you should wait for the mappable.ready
promise to be resolved in each of your modules.
For example, you want to display two maps on a page at once, each in its own container. It is more convenient for you to describe this functionality not in one large module, but in two different ones, for example with the names module-a.js
and module-b.js
. Then using the methods described above, the code of your modules will look something like this:
// module-a.js
import * as mappable from 'mappable';
async function initMap() {
// Wait for the resolving mappable.ready
await mappable.ready;
const {MMap} = mappable;
// The map is initialized in the first container
const map = new MMap({document.getElementById('first-map-container')});
}
initMap();
// module-b.js
import * as mappable from 'mappable';
async function initMap() {
// Wait for the resolving mappable.ready
await mappable.ready;
const {MMap} = mappable;
// The map is initialized in the first container
const map = new MMap({document.getElementById('second-map-container')});
}
initMap();
We have to wait for the mappable.ready
resolving in each module. This is inconvenient, I would like to write code easier:
// module-a.js
import * as mappable from 'mappable';
const {MMap} = mappable;
const map = new MMap({document.getElementById('first-map-container')});
// module-b.js
import * as mappable from 'mappable';
const {MMap} = mappable;
const map = new MMap({document.getElementById('second-map-container')});
To achieve this, use the third method.
Method 3
<!DOCTYPE html>
<html>
<head>
<script src="build/bundle.js"></script>
</head>
<body>
<div id="app" style="width: 600px; height: 400px"></div>
</body>
</html>
import * as mappable from 'mappable';
const {MMap, MMapDefaultSchemeLayer} = mappable;
const map = new MMap(
document.getElementById('app'),
{
location: {
center: [25.229762, 55.289311],
zoom: 10
}
}
);
map.addChild(new MMapDefaultSchemeLayer());
const path = require('path');
module.exports = {
mode: 'development',
entry: './index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'build'),
},
externalsType: 'script',
externals: {
// Substitute the value of the real key instead of YOUR_API_KEY
mappable: [
`promise new Promise((resolve) => {
if (typeof mappable !== 'undefined') {
return mappable.ready.then(() => resolve(mappable));
}
const script = document.createElement('script');
script.src = "https://js.api.mappable.world/v3/?apikey=YOUR_API_KEY&lang=en_US";
script.onload = () => {
mappable.ready.then(() => resolve(mappable));
};
document.head.appendChild(script);
})`
]
},
devtool: 'cheap-source-map'
};
{
"devDependencies": {
"http-server": "14.1.1",
"webpack": "5.88.2",
"webpack-cli": "5.1.4"
},
"scripts": {
"build": "webpack",
"start": "npx http-server ."
}
}
Install the dependencies, build the project and run the local server:
npm install
npm run build
npm run start
Open application
Specificities
This is almost the same method #2, only in webpack.config.js
when declaring the variable mappable
, the path to the API loader is specified somewhat differently and the promise mappable.ready
is resolved there.
With this connection, fully loaded JS API modules become available in the assembled project js file and the execution of mappable.ready
is guaranteed, so the project code can be written shorter and neater:
import * as mappable from 'mappable';
const {MMap} = mappable;
const map = new MMap({...});
Alert
With this connection, the project code will not start executing until the JS API components are fully loaded.
Limitations of method 3
For example, you want to show the loading animation until the map components have loaded. We connected the JS API in method #3 and wrote this code for this. Unfortunately, you will not be able to complete the task:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as mappable from 'mappable';
// Execution of this line will start only after loading mappable and the mappable.ready resolving
const reactify = mappable.reactify.bindTo(React, ReactDOM);
const {MMap} = reactify.module(mappable);
function MapView() {
const [loading, setLoading] = React.useState(true);
React.useEffect(() => {
setLoading(false);
}, []);
// This code is useless. Of course, it will be executed when mounting the component in the DOM, but
// mounting will happen only after loading mappable and resolving mappable.ready.
// Therefore, when mounting, the user will see the status 'Loading...' for a fraction of a second,
// and then useEffect will immediately happen, which will cancel the loading animation.
if (loading) {
return <div>Loading...</div>;
}
return <MMap />;
}
function App() {
return (
<MapView />
);
}
The main disadvantage of webpack connection #3 is that nothing will be executed in the project modules until the JS API components are fully loaded.
To show the loading animation, method #3 will have to be abandoned:
- Switch to webpack connection #1 or #2.
- Inside
React.useEffect
, you need to wait formappable.ready
and only then uncheckloading
. - The code using the
reactify
module must be placed inside theMapView
component so that theJS
interpreter can "read" these lines only after themappable.ready
resolving:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as mappable from 'mappable';
function MapView() {
const [loading, setLoading] = React.useState(true);
React.useEffect(() => {
mappable.ready.then(() => setLoading(false));
}, []);
if (loading) {
return <div>Loading...</div>;
}
const reactify = mappable.reactify.bindTo(React, ReactDOM);
const {MMap} = reactify.module(mappable);
return <MMap />;
}
function App() {
return (
<MapView />
);
}
Language switching
Remember that when connecting the API, language is specified. If you are creating international sites, make sure that you switch it with any connection method.
For example, to connect with Webpack in the third way, you can do this:
module.exports = {
//...
externals: {
mappable: [
`promise new Promise((resolve) => {
...
const lang = ['tr', 'tr-TR'].includes(navigator.language) ? 'tr-TR' : 'en-US';
script.src = "https://js.api.mappable.world/v3/?apikey=YOUR_API_KEY&lang=" + lang;
...
})`
]
}
};
Connecting packages and modules
The JS API provides additional packages and modules that are useful for solving specific tasks. The mappable.import
method is provided for uploading them. Read more in the documentation of packages and modules.