2016-09-19 12 views
0

私は本当に標準的なデータバインディングでは解決できない非常に特殊なニーズを持っています。ダイナミックスコープコンパイルを使用した動的テンプレート

私はvue view-modelとバインドしたいリーフレットマップを持っています。

私はちょっと私の見解に境界機能をにGeoJSON表示するには成功しましたが、私はメインの質問ですvue.js

が結合したポップアップ表示で苦労しています:ポップアップを開く方法」(おそらく複数の同時にポップアップ)と、今、私は実用的なソリューションに来ているのビュープロパティ」

にバインドするが、これはawefulです:

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をループに持っているので、これは、私にはかなりawefulようだ、私のカスタム要素のための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; 
    } 
}); 

...「コンパイル」機能が見つかりませんでした。定義されたスコープに基づいています。

  • はそれを

EDITをコンパイルし

  • はそれをスコープ
  • を渡しカスタム要素のインスタンスを作成します:基本的に私はしたい実は、私は機能をコンパイル$見つけることができます。しかし、それはしばしばをコンパイルするために使用されます。子供からhtmlへ。私はそれをコンパイルした後に追加したくない。私はそれをコンパイルしたいと思っています。

  • +0

    私はあなたの問題を完全には理解していませんが、私の感想はあなたがカスタムディレクティブを使うべきだということです。 –

    +0

    私の問題は、パラメータとして渡されたスコープでカスタム要素をコンパイルする方法がわかりません。 'handleStatementFeature'を読んで、それは私がコードする方法を知らないいくつかの行を含んでいます。 –

    +0

    私が理解できないのは、ここにその機能が必要な理由です。あなたのコンポーネントにプラグインされている 'selectedStatement'変数が必要なようです。 –

    答えて

    0

    これはあなたのために働くでしょうか?コンポーネントを使用する代わりに、bindPopupに渡す新しい要素を作成し、dataを適切に設定して、その要素のnew Vueを作成します。

    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>

    私はあなたが$compileと同じことを行うことができ考えるが、$compileを文書化し、内部使用のために意図した(実際には未)が不十分です。これは現在のスコープ内の現在のVueのコントロール下に新しいDOM要素を持って来るのに便利ですが、あなたは新しいスコープと新しいDOM要素を持っていて、注意したように、そのバインディングはまさにVueの目的です。

    スニペットを更新したので、parent optionを指定すると、親チェーンを確立できます。

    +0

    ありがとう。それは私の質問に答えることはできません。これはおそらく私の間違いですが、コードはあまり集中しておらず、後に間違いがあります。私は自分の投稿で大規模な編集を行います。基本的には、私の必要性に集中するためにリストのものを忘れるつもりです。私の質問からあなたの答えを切り離して申し訳ありません。 –

    +0

    @CyrilCHAPON私の答えに大規模な編集。 :) –

    +0

    あなたは私の一日を作った。それはうまくいくと私はそれを受け入れています。 しかし、私はvue **が実際に要素であると聞きました。そして、vueをインスタンス化することは、正確に要素に範囲をバインドするのとまったく同じです。私はこれを行うためのよりクリーンな方法があると信じています(私はサブビューが最初のものの子であることを望みます)。 しかし、あなたは私の心を吹き飛ばし、実用的なソリューションを提供しました。 –

    関連する問題