An advanced template system incorporating dynamic scope compilation

My project requires a unique solution that cannot be achieved with standard data-binding methods.

I have integrated a leaflet map that I need to bind with a vue view-model.

While I was able to display geojson features linked to my view, I am facing challenges in displaying a popup bound with vue.js

The primary question at hand is: "How can I open and bind multiple popups simultaneously to a view property?"

Although I have a working solution currently, it feels cumbersome:

map.html

<div id="view-wrapper">
  <div id="map-container"></div>

  <div v-for="statement in statements" id="map-statement-popup-template-${statement.id}" style="display: none">
    <map-statement-popup v-bind:statement="statement"></map-statement-popup>
  </div>
</div>

<!-- base template for statement map popup -->
<script type="text/template" id="map-statement-popup-template">
  {{ statement.name }}
</script>

map.js

$(document).ready(function() {
  [...]

  //View-model data-bindings
  var vm = new Vue({
    el: '#view-wrapper',
    data: {
      statements: []
    },
    methods: {
      handleStatementFeature: function(feature, layer) {
        var popupTemplateEl = $('<map-statement-popup />');
        var scope = { statement: feature.properties.statement };
        var compiledElement = this.COMPILE?(popupTemplateEl[0], scope);
        layer.bindPopup(compiledElement);
      }
    },
    components: {
      'map-statement-popup': {
        template: '#map-statement-popup-template',
        props: {
          statement: null
        }
      }
    }
  });

  function geoJsonFromStatementsLocations(statements){
    var geoJson = {
      type: "FeatureCollection",
      features: _.map(statements, function(statement) {
        return {
          type: "Feature",
          geometry: {
            type: "LineString",
            coordinates: statement.coordinates
          },
          properties: {
            statement: statement
          }
        };
      });
    };
    return geoJson;
  }
});

Though the current setup works, it involves looping over statements, creating custom elements for each statement, hiding them, and using dynamic IDs to link them with popups.


I wish to simplify the process by implementing something like this :

map.html

<div id="view-wrapper">
  <div id="map-container"></div>
</div>

<!-- base template for statement map popup -->
<script type="text/template" id="map-statement-popup-template">
  {{ statement.name }}
</script>

map.js

$(document).ready(function() {
  [...]

  //View-model data-bindings
  var vm = new Vue({
    el: '#view-wrapper',
    data: {
      statements: []
    },
    methods: {
      handleStatementFeature: function(feature, layer) {
        var popupTemplateEl = $('<map-statement-popup />');
        var scope = { statement: feature.properties.statement };
        var compiledElement = this.COMPILE?(popupTemplateEl[0], scope);
        layer.bindPopup(compiledElement);
      }
    },
    components: {
      'map-statement-popup': {
        template: '#map-statement-popup-template',
        props: {
          statement: null
        }
      }
    }
  });

  function geoJsonFromStatementsLocations(statements){
    var geoJson = {
      type: "FeatureCollection",
      features: _.map(statements, function(statement) {
        return {
          type: "Feature",
          geometry: {
            type: "LineString",
            coordinates: statement.coordinates
          },
          properties: {
            statement: statement
          }
        };
      });
    };
    return geoJson;
  }
});

However, I am unable to find a method to compile based on a defined scope. Essentially, I want to:

  • Create an instance of a custom element
  • Pass it a scope
  • Compile it

EDIT : Upon further research, I did discover the $compile function. However, it is typically used to compile appended child elements in HTML. I aim to compile first and then let leaflet append it automatically.

Answer №1

Does this method suit your needs? Rather than utilizing a component, you can generate a new element to pass to bindPopup, and then initialize a new Vue on that particular element with the necessary data.

new Vue({
  el: 'body',
  data: {
    popups: [1, 2, 3],
    message: "I'm Dad",
    statements: []
  },
  methods: {
    handleFeature: function(id) {
      const newDiv = document.createElement('div');
      const theStatement = {
        name: 'Some name for ' + id
        };
      newDiv.innerHTML = document.getElementById('map-statement-popup-template').innerHTML;
      new Vue({
        el: newDiv,
        data: {
          statement: theStatement
        },
        parent: this
      });

      // Mock call to layer.bindPopup
      const layerEl = document.getElementById(id);
      this.bindPopup(layerEl, newDiv);
    },
    bindPopup: function(layerEl, el) {
      layerEl.appendChild(el);
    }
  }
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<div class="leaflet-zone">
  <div v-for="popup in [1,2,3]">
    <button @click="handleFeature('p-' + popup)">Bind</button>
    <div id="p-{{popup}}"></div>
  </div>
</div>

<template id="map-statement-popup-template">
  {{ statement.name }} {{$parent.message}}
</template>

In my opinion, you could achieve a similar result using $compile. However, it is worth noting that $compile lacks proper documentation and is primarily intended for internal usage. It proves beneficial in assuming control of a new DOM element within the current Vue instance and scope. Yet, the scenario you described—having both a new scope and a new DOM element—is precisely what Vue aims to address.

You have the option to establish a parent chain by specifying the parent parameter, as shown in the updated snippet provided above.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Determine the precise location of a screen element with jQuery

Can anyone help me determine the precise position of an element on the current visible screen using jQuery? My element has a relative position, so the offset() function only gives me the offset within the parent. Unfortunately, I have hierarchical divs, ...

What is the best way to locate elements with the term "download" in them using Selenium's x-path feature?

I'm currently utilizing Selenium for web scraping purposes and I am in need of a way to identify all clickable elements that contain the word "download" (regardless of capitalization) within their link text, button text, element ID, element class, or ...

The $http service factory in Angular is causing a duplication of calls

After creating an angular factory that utilizes the $http service, I encountered a problem where the HTTP request is being made twice when checking the network tab in the browser. Factory: app.factory('myService', function ($http, $q) { var de ...

The reactivity of a Vue.js computed property diminishes when it is transmitted through an event handler

Within my main application, I've implemented a Modal component that receives content via an event whenever a modal needs to be displayed. The Modal content consists of a list with an associated action for each item, such as "select" or "remove": Vue. ...

Transform JSON reply in JavaScript/Typescript/Angular

Looking for assistance with restructuring JSON data received from a server API for easier processing. You can find the input JSON file at assets/input-json.json within the stackblitz project: https://stackblitz.com/edit/angular-ivy-87qser?file=src/assets/ ...

Updating the input value in a React application

With a list and an edit button, upon clicking the edit button, a new modal opens. How can I auto-populate the selected username email? The server-side response is {this.test.name}, where I provide him with the input value to auto-populate. However, when ...

What methods can be used to initiate form error handling in React/Javascript?

I am currently utilizing Material-UI Google Autocomplete, which can be found at https://material-ui.com/components/autocomplete/#google-maps-place, in order to prompt users to provide their address. https://i.stack.imgur.com/nsBpE.png My primary inquiry ...

acquiring specific array elements in Vue.js

Currently, I am integrating Vue.js in the frontend of a Laravel application and need to retrieve values from an array column and store them in a new array. Is there a method similar to PHP's array_column in Vue.js that can accomplish this task? ...

Looking to split the month and year input boxes when utilizing stripe version 7.2.0

Currently, I am utilizing ngx-stripe Version 7.2.0 to manage UI elements in Angular. I'm wondering if there is a method to split the Month and Year into separate text boxes within the UI of Angular 7 instead of having them combined into one field? ...

What are the issues with the latest API routing in Next.js?

I am encountering an error that says: SyntaxError: Unexpected token s in JSON at position 0 Here is my code: import { PrismaClient } from '@prisma/client'; import { IDPay } from 'idpay'; import { NextResponse } from 'next/server&ap ...

What's the best way to establish a victorious player in a game of Tic

I've been struggling to find a solution for determining the winner using WinCombos. Is there a way to compare the elements in my winCombos array with the cells of a 3x3 tic tac toe board to identify the winner? var player1 = "X"; var player2 = "O"; ...

When it comes to Redux, is it considered an anti-pattern to pass an event from a presentational component to a container component

As a newcomer to Redux, I am challenging myself to rebuild an old React app using this technology in order to gain proficiency. However, I am facing a significant challenge regarding where to place the logic within the application. My understanding is tha ...

Steer clear of retrieving all the elements from the HTML DOM at once

Scenario I am working on a HTML5+CSS+JS slideshow project that needs to be synchronized across 50 clients in a local area network using a single wireless router. Challenge Due to the heavy content, particularly images, of the slides, I intend to dynamic ...

What are some tips for integrating Bluebird into Angular frameworks?

I attempted to integrate Angular with Bluebird promises: Here is the HTML code snippet: <body ng-app="HelloApp"> <div ng-controller="HomeController">{{name}} {{also}}</div> </body> The corresponding JavaScr ...

The Ajax request fails to set a value within the done callback

Here is a function I have: var isNameUnique = false; function ValidateName() { var url = "/SomeRules/CheckIfNameExists/"; var request = $.ajax({ url: url, method: "GET", data: { sName: name}, ...

Is there a way to show output on render rather than using console.log in node.js?

I have successfully sorted the objects as shown in the link below. My next challenge is to integrate the sorted object into my render function rather than just using console.log(). I'm uncertain if converting it back into an object is the right appro ...

html - automatically populating input fields when the page loads

Currently, I have an HTML form embedded in the view and I am looking for a way to automatically populate specific input fields with json variables obtained from the server. Instead of manually writing JavaScript code for each field, my goal is to access th ...

Array of generic types in Typescript

Here's a method that I have: getFiveObjectsFromArray(array: T[]) { return array.slice(0, 5); } I've been using this method multiple times. Is there a way in TypeScript to pass a generic argument instead of using multiple types? Also, when ...

Ways to stop CKEDITOR from automatically saving textarea or contenteditable content

I've integrated the CKEDITOR plugin for a format toolbar feature on my web application. It seems that the message shown above is a default one provided by CKEDITOR. My goal is to have users start with a blank textarea every time they visit the page, ...

Submit a HTML form to a Telegram recipient

I am looking to send HTML form data, which includes input values and select options, to a telegram user. After some research, I discovered that I need to create a Telegram bot. I successfully created one using @botFather by following these steps: /newbot ...