Vue

Quick start

Warning

Supported Vue version: 3 or higher

Connecting via top-level-await

<!DOCTYPE html>
<html>
  <head>
    <!-- Replace YOUR_API_KEY with the real 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'
    }
  }
});

Set dependencies and run a local server:

npm install
npm run dev

Open the app

Specifics

  1. Specify the type="module" attribute in the "script" tag that loads the compiled project JS to enable ECMAScript Modules (ESM) and top-level-await support:

    <script type="module" src="index.ts"></script>
    
  2. In package.json, add a dev dependency on the @mappable-world/mappable-types package.

    We recommend installing the latest version:

    npm i --save-dev @mappable-world/mappable-types@latest

  3. In tsconfig.json, set compilerOptions.typeRoots with a list of paths to type files. Add a path to the @mappable-world/mappable-types package there to make the mappable namespace with types appear in the global scope.

    Note

    The mappable namespace contains all the class types provided by the JS API, but they are not available in the runtime environment until mappable.ready is resolved.

  4. In tsconfig.json, set compilerOptions.paths, informing the TS compiler that the contents of the imported mappable package should be searched for at the specified path. This enables you to import types in project files as if they were located not at @mappable-world/mappable-types, but in the mappable package:

    import type {MMapLocationRequest} from 'mappable';
    

    All types must be imported from the root.

    The internal structure isn't guaranteed and can change over time.

  5. In tsconfig.json, for top-level-await to operate correctly, the compilerOptions.module parameter must be set to one of the following values: es2022, esnext, system, or preserve. The compilerOptions.target parameter must be set to es2017 or higher.

  6. In vite.config.ts, set resolve.alias to use the "complete" Vue build (vue/dist/vue.esm-bundler.js). This is necessary for mappable of Vue components to work correctly.

  7. Each object in the JS API has a Vue analog. To use the Vue API version, connect the @mappable-world/mappable-vuefy module. In lib/mappable.ts, wait until the JS API and Vuefy module are fully loaded and then export the necessary map components so that they can be used 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);
    
  8. The use of top-level-await in lib/mappable.ts guarantees that mappable.ready and mappable.import('@mappable-world/mappable-vuefy') are executed before the map components are imported, allowing any JS API objects to be used as Vue components synchronously:

    <MMap :location="LOCATION">
      <MMapDefaultSchemeLayer />
      <MMapDefaultFeaturesLayer />
      ...
    </MMap>
    

Specifying input parameters for custom classes

Vue components require explicit declaration of input parameters so that Vue knows which of them should be considered additional attributes.

For basic objects from the mappable namespace, input parameters are determined by default and no additional actions are required.

If you are a developer who creates custom classes (for example, as part of a separate package), you can determine which input parameters will be contained in the component. Determining props supports Vue format. Fro example, custom MMapSomeClass:

type MMapSomeClassProps = {
  id: string;
  counter?: number;
};
export class MMapSomeClass extends mappable.MMapComplexEntity<MMapSomeClassProps> {
  static [mappableVue.vuefy.optionsKey] = {props: ['id', 'counter']};
  //...
}

In addition to an array of strings, you can also use object syntax:

export class MMapSomeClass extends mappable.MMapComplexEntity<MMapSomeClassProps> {
  static [mappableVue.vuefy.optionsKey] = {
    props: {
      id: String,
      counter: Number
    }
  };
  //...
}

or with input parameter validation:

export class MMapSomeClass extends mappable.MMapComplexEntity<MMapSomeClassProps> {
  static [mappableVue.vuefy.optionsKey] = {
    props: {
      id: {type: String, required: true},
      counter: {type: Number, required: false}
    }
  };
  //...
}

Specifying input parameters when using third-party packages

If developers of third-party packages have not determined input parameters for their classes, you can specify them yourself when calling vuefy by passing them as a 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 class
  }
);

The object syntax and validations shown above are supported in a similar way.

Custom implementations of mappable.MMapEntity objects for Vue

When the standard conversion is insufficient, use the overrideKey key to specify your implementation for vuefy:

type MMapSomeClassProps = {
  id: string;
  counter?: number;
};
/* mappable.MMapEntity object */
export class MMapSomeClass extends mappable.MMapComplexEntity<MMapSomeClassProps> {
  static [mappableVue.vuefy.overrideKey] = MMapSomeClassVuefyOverride;
  //...
}

MMapSomeClassVuefyOverride is a method that must return a Vue component. It gets the base class, declared props, and an object with Vue and vuefy (if their methods are required) as parameters. The example below shows the creation of the MMapSomeClassV wrapper with additional logic around the component that was obtained using the base vuefy method:

export const MMapSomeClassVuefyOverride: CustomVuefyFn<MMapSomeClass> = (
  MMapSomeClassI, // base MMapSomeClass
  props, // declared input parameters
  {vuefy, Vue}
) => {
  // Standard vuefy method
  const MMapSomeClassVuefied = vuefy.entity(MMapSomeClassI, props);
  const MMapSomeClassV = Vue.defineComponent({
    props,
    name: 'MMapSomeClassV',
    components: {MMapSomeClassVuefied},
    setup() {
      // additional logic for custom implementation
    },
    template: `<MMapSomeClassVuefied v-bind="$props" ... />`
  });
  return MMapSomeClassV;
};

the resulting component can be used in the app:

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');
Previous