Integration with Vue
Quick start
Warning
Supported Vue version: at least 3
Connecting via top-level-await
<!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.ts"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
import {createApp} from 'vue';
import App from './App.vue';
createApp(App).mount('#app');
<script lang="ts" setup>
import {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapMarker} from './lib/mappable';
import type {MMapLocationRequest} from 'mappable';
const LOCATION: MMapLocationRequest = {
center: [25.229762, 55.289311],
zoom: 9
};
</script>
<template>
<div style="width: 600px; height: 400px">
<MMap :location="LOCATION">
<MMapDefaultSchemeLayer />
<MMapDefaultFeaturesLayer />
<MMapMarker :coordinates="[25.229762, 55.289311]" :draggable="true">
<section>
<h1>You can drag this header</h1>
</section>
</MMapMarker>
</MMap>
</div>
</template>
import * as Vue from 'vue';
const [mappableVue] = await Promise.all([mappable.import('@mappable-world/mappable-vuefy'), mappable.ready]);
export const vuefy = mappableVue.vuefy.bindTo(Vue);
export const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapMarker} = vuefy.module(mappable);
{
"compilerOptions": {
"target": "es2017",
"lib": ["dom", "dom.iterable", "esnext"],
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"typeRoots": ["./node_modules/@types", "./node_modules/@mappable-world/mappable-types"],
"paths": {
"mappable": ["./node_modules/@mappable-world/mappable-types"]
}
}
}
{
"type": "module",
"scripts": {
"dev": "vite"
},
"devDependencies": {
"@mappable-world/mappable-types": "^0.0.10",
"vue": "^3.4.21",
"@vitejs/plugin-vue": "^5.0.4",
"typescript": "^4.9.5",
"vite": "^5.2.8"
}
}
import {defineConfig} from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
vue: 'vue/dist/vue.esm-bundler.js'
}
}
});
Install the dependencies and start the local server:
npm install
npm run dev
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.ts"></script>
-
In
package.json
adding a dev-dependency on the package@mappable-world/mappable-types
.It is recommended to install the latest version:
npm i --save-dev @mappable-world/mappable-types@latest
-
In
tsconfig.json
we setcompilerOptions.typeRoots
with a list of paths to file types. Adding the path to the package@mappable-world/mappable-types
there, so that the namespacemappable
with types appears in the global scope.Note
The namespace
mappable
contains all the class types that the JS API provides, but they are not available in the runtime until the resolvingmappable.ready
. -
In
tsconfig.json
we setcompilerOptions.paths
, in which we inform the ts compiler that when importing the packagemappable
, its content should be searched for in the specified path. Thanks to this, you can import types in project files as if they are not in the@mappable-world/mappable-types
, but in themappable
package:import type {MMapLocationRequest} from 'mappable';
All types must be imported from the root.
The internal structure is not guaranteed and may change over time.
-
In
tsconfig.json
, for top-level-await to work correctly, the parametercompilerOptions.module
must be set to one of the following values:es2022
,esnext
,system
orpreserve
. Also thecompilerOptions.target
parameter must bees2017
or higher. -
In
vite.config.ts
we setresolve.alias
to use the "full" Vue build (vue/dist/vue.esm-bundler.js
). This is necessary for the correct operation of themappable
Vue components. -
For each object in the JS API, there is a Vue analog. To use the Vue API version, connect the
@mappable-world/mappable-vuefy
module. In thelib/mappable.ts
file, we wait for the JS API and vuefy module to be fully loaded, after which we export the necessary map components for use in other parts of the project:import * as Vue from 'vue'; const [mappableVue] = await Promise.all([mappable.import('@mappable-world/mappable-vuefy'), mappable.ready]); export const vuefy = mappableVue.vuefy.bindTo(Vue); export const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer} = vuefy.module(mappable);
-
Using top-level-await in
lib/mappable.ts
guarantees the execution ofmappable.ready
andmappable.import('@mappable-world/mappable-vuefy')
before importing map components, which allows you to synchronously use any JS API objects as Vue components:<MMap :location="LOCATION"> <MMapDefaultSchemeLayer /> <MMapDefaultFeaturesLayer /> ... </MMap>
Specifying input props for your own classes
Vue components require explicit declaration of props so that Vue knows which of them should be treated as additional attributes.
For basic objects from the mappable
namespace, the input parameters are defined by default, and no additional actions are required.
If you are a developer who creates your own classes (for example, within a separate package), then you can determine which input parameters will be in the component. The props definition supports the Vue format. For example, the custom class MMapSomeClass
:
type MMapSomeClassProps = {
id: string;
counter?: number;
};
export class MMapSomeClass extends mappable.MMapComplexEntity<MMapSomeClassProps> {
static [mappableVue.vuefy.optionsKey] = {props: ['id', 'counter']};
//...
}
In addition to the array of strings, we can also use the object syntax:
export class MMapSomeClass extends mappable.MMapComplexEntity<MMapSomeClassProps> {
static [mappableVue.vuefy.optionsKey] = {
props: {
id: String,
counter: Number
}
};
//...
}
or with validation of input parameters:
export class MMapSomeClass extends mappable.MMapComplexEntity<MMapSomeClassProps> {
static [mappableVue.vuefy.optionsKey] = {
props: {
id: {type: String, required: true},
counter: {type: Number, required: false}
}
};
//...
}
Specifying input props when using third-party packages
If the developers of third-party packages haven't defined input parameters for their classes, you should specify them when calling vuefy yourself by passing them in as the second argument:
const mappableVue = await mappable.import('@mappable-world/mappable-vuefy');
const vuefy = mappableVue.vuefy.bindTo(Vue);
const {MMapSomeClass as MMapSomeClassV} = vuefy.module(
{MMapSomeClass},
{
MMapSomeClass: ['id', 'counter'] // props for the MMapSomeClass
}
);
Similarly, the object syntax and validations shown above are supported.
Custom implementations of objects mappable.MMapEntity for Vue
When the standard conversion is not sufficient, use the overrideKey
key to specify your own implementation of vuefy:
type MMapSomeClassProps = {
id: string;
counter?: number;
};
/* object mappable.MMapEntity */
export class MMapSomeClass extends mappable.MMapComplexEntity<MMapSomeClassProps> {
static [mappableVue.vuefy.overrideKey] = MMapSomeClassVuefyOverride;
//...
}
MMapSomeClassVuefyOverride
is the method that should return the Vue component. As parameters, it gets a base class, declared props, and an object with Vue and vuefy if their methods are required. In the example below, a wrapper MMapSomeClassV
is created with additional logic around the component obtained by the basic vuefy method:
export const MMapSomeClassVuefyOverride: CustomVuefyFn<MMapSomeClass> = (
MMapSomeClassI, // basic MMapSomeClass
props, // declared props
{vuefy, Vue}
) => {
// Standard vuefy method
const MMapSomeClassVuefied = vuefy.entity(MMapSomeClassI, props);
const MMapSomeClassV = Vue.defineComponent({
props,
name: 'MMapSomeClassV',
components: {MMapSomeClassVuefied},
setup() {
// additional logic of the user implementation
},
template: `<MMapSomeClassVuefied v-bind="$props" ... />`
});
return MMapSomeClassV;
};
the resulting component can be used in the application:
const mappableVue = await mappable.import('@mappable-world/mappable-vuefy');
const vuefy = mappableVue.vuefy.bindTo(Vue);
const MMapSomeClassV = vuefy.entity(MMapSomeClass);
const app = createApp({
components: {MMapSomeClassV},
template: `<MMapSomeClassV id="some_id" />`
});
app.mount('#app');