具有动态范围编译的动态模板

时间:2016-09-19 11:30:04

标签: javascript data-binding leaflet vue.js

我有一个非常具体的需求,无法通过标准数据绑定来解决。

我有一张传单地图,我想用vue视图模型绑定。

我成功地显示了geojson功能,但是我很难显示与vue.js绑定的弹出窗口

主要问题是: “如何打开弹出窗口(可能同时打开多个弹出窗口)并将其绑定到视图属性”

现在我已经找到了一个有效的解决方案,但这很可悲:

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() {
  var map = new L.Map('map-container');
  map.setView(new L.LatLng(GLOBALS.MAP.STARTCOORDINATES.lng, GLOBALS.MAP.STARTCOORDINATES.lat), GLOBALS.MAP.STARTZOOM);

  var osm = new L.TileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png');
  osm.addTo(map);

  //Initialize map dynamic layers
  var mapLayers = {};

  //View-model data-bindings
  var vm = new Vue({
    el: '#view-wrapper',
    data: {
      statements: []
    },
    methods: {
      getStatements: function() {
        return $.get('api/statements');
      },
      updateStatements: function() {
        var that = this;
        return that.getStatements().then(
          function(res) {
            that.statements = res.data;
          }
        );
      },
      refreshStatements: function() {
        mapLayers.statements.layer.clearLayers();

        if(this.statements && this.statements.length){
          var geoJsonStatements = geoJsonFromStatements(this.statements);
          mapLayers.statements.layer.addData(geoJsonStatements);
        }
      },
      handleStatementFeature: function(feature, layer) {
        var popupTemplateEl = $('#map-statement-popup-template-' + feature.properties.statement.id);
        layer.bindPopup(popupTemplateEl.html());

        var statementIndex = _.findIndex(this.statements, {statement:{id: feature.properties.statement.id}});

        if(feature.geometry.type === 'LineString') {
          this.statements[statementIndex].layer = {
            id: L.stamp(layer)
          };
        }
      },
      openStatementPopup: function(statement) {
        if(statement.layer) {
          var featureLayer = mapLayers.statements.layer.getLayer(statement.layer.id);
          featureLayer.openPopup();
        }
      }
    },
    created: function() {
      var that = this;

      //Set dynamic map layers
      var statementsLayer = L.geoJson(null, {
        onEachFeature: this.handleStatementFeature
      });

      mapLayers.statements = {
        layer: statementsLayer
      };

      map.addLayer(mapLayers.statements.layer);

      this.updateStatements().then(this.refreshStatements);

      this.$watch('statements', this.refreshStatements);
    },
    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;
  }
});

这对我来说似乎很可怕,因为我必须使用v-for遍历语句,为每个语句为我的自定义元素渲染div,隐藏它,然后在弹出窗口,用动态id技术抓住它。

我想做这样的事情:

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;
  }
});

...但我找不到“COMPILE?”的功能。基于定义的范围。基本上我想:

  • 创建自定义元素实例
  • 传递范围
  • 编译
编辑:实际上,我可以找到$ compile函数。但它经常用于将追加子项编译为html。我不想追加它然后编译它。我想编译它然后让传单附加给我。

1 个答案:

答案 0 :(得分:0)

这对你有用吗?您可以创建一个要传递给bindPopup的新元素,并在该元素上new Vue创建一个新元素,并适当地设置data

&#13;
&#13;
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);
    }
  }
});
&#13;
<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>
&#13;
&#13;
&#13;

认为你可以用$compile做同样的事情,但是$compile很难(真的没有)记录并打算供内部使用。在当前范围内将新DOM元素置于当前Vue的控制之下非常有用,但是您有一个新的范围以及一个新的DOM元素,正如您所指出的那样,该绑定正是Vue的所有内容。打算做。

您可以通过指定parent option来建立父链,因为我已更新了要执行的代码段。