天天看點

ArcGIS API for JavaScript 4.2學習筆記[16] 彈窗自定義功能按鈕及為要素自定義按鈕(第五章完結)

解析Popup的自定義工具按鈕如何實作。

這節對Popups這一章的最後兩個例子進行介紹和解析。

第一個【Popup Actions】介紹了彈窗中如何自定義工具按鈕(名為actions),以PopupTemplate+FeatureLayer的形式測量要素的長度為例子進行介紹。

第二個【Custom popup actions per feature】則是上一個的更新,如果說上一個例子的功能是寫死的,那麼這個例子就把這個功能寫活了。什麼意思呢?上個例子的測距僅僅能測距,沒有什麼别的特别的。而這個例子以啤酒店的分布(點要素圖層)為例,在自定義的按鈕中彈出在谷歌搜尋的結果甚至彈出這個啤酒店的網站。

actions是什麼?

actions是Popup類的一個屬性,類型是Collection,即同類型AJS對象或原生JS對象的數組容器。

它能裝什麼?

能裝一些“動作”,這些動作在點選彈窗左下角按鈕時,會觸發這個容器中的“動作”。每個彈窗都有一個預設的“動作”,就是“Zoom-To”縮放功能,就是一個小放大鏡。

看第一個例子的結果:

ArcGIS API for JavaScript 4.2學習筆記[16] 彈窗自定義功能按鈕及為要素自定義按鈕(第五章完結)

按下彈窗中的小測距尺子按鈕後,灰色文字條中就出現了這個紅色線要素的長度:11.82miles。

看第二個例子的結果:

ArcGIS API for JavaScript 4.2學習筆記[16] 彈窗自定義功能按鈕及為要素自定義按鈕(第五章完結)
ArcGIS API for JavaScript 4.2學習筆記[16] 彈窗自定義功能按鈕及為要素自定義按鈕(第五章完結)

按下按鈕後就會彈出谷歌搜尋的結果。

來看代碼解析吧!

【Part I 簡單action:測距】

給出引用

require(
    [
      "esri/Map",
      "esri/layers/FeatureLayer",
      "esri/views/MapView",
      "esri/geometry/geometryEngine",
      "dojo/domReady!"
    ],
    function(...){...}
);      

多了個geometryEngine,暫時不管它是幹啥用的,繼續往下看。

事先說明一下,CDN的引用又多了一行:

<link rel="stylesheet" href="https://js.arcgis.com/4.2/dijit/themes/claro/claro.css">
  <link rel="stylesheet" href="https://js.arcgis.com/4.2/esri/css/main.css">
  <script src="https://js.arcgis.com/4.2/"></script>      

函數參數骨架

function(Map, FeatureLayer, MapView, geometryEngine){
    var map = new Map({...});
    var view = new MapView({...});
    
    var measureThisAction = {...};
    var template = {...};
    featureLayer =new FeatureLayer({...});
    map.add(featureLayer);
    
    function measureThis(){...};
    view.popup.on("trigger-action", function(event){...});
}      

套路依舊,map和view執行個體化。var的template是PopupTemplate對象賦給了featureLayer的popupTemplate屬性,再把featureLayer丢進map中。

好了,其餘陌生的是什麼?measureThisAction這個對象、measureThis這個方法、和view.popup.on方法?

别急,一個一個來。

measureThisAction出現的地方一一揪出來:

var measureThisAction = {
  title: "Measure Length",
  id: "measure-this",
  image: "Measure_Distance16.png"
};      
var template = {
  title: "Trail run",
  content: "{name}",
  actions: [measureThisAction]
};      

OK了,很明顯,measureThisAction是一個Object,有title、id和image,分别對應标題、ID和按鈕上的圖案。這個png檔案在html的同級别目錄下。

下面這個template是featureLayer的PopupTemplate屬性所需的對象,有title、content,第三個actions屬性就将上方的measureThisAction賦給了它(數組形式,因為是Collection類型)。

可以了解如下:

measureThisAction是一個“動作”的聲明,本身無功能,可以說是一個UI層上的描述。

————

那麼measureThis()這個方法呢?

function measureThis() {
  var geom = view.popup.selectedFeature.geometry;
  var distance = geometryEngine.geodesicLength(geom, "miles");
  distance = parseFloat(Math.round(distance * 100) / 100).toFixed(2);
  view.popup.content = view.popup.selectedFeature.attributes.name +
    "<div style='background-color:DarkGray;color:white'>" + distance +
    " miles.</div>";
}      

這裡出現了新玩意兒:popup類的selectedFeature屬性以及其子屬性geometry/attributes,geometryEngine類的geodesicLength方法。

查詢API,解讀一下:

selectedFeature屬性類别:Graphic類,目前選擇的要素。

是以Graphic的geometry和attributes屬性就容易查到:

geometry:幾何體,沒什麼好說的。類型:Geometry類。

attributes:要素的字段名和字段值的成對集合。

geodesicLength():計算傳入幾何體(Geometry)的長度。

于是這個方法就是:擷取幾何體,然後把長度用geodesicLength()計算出來,機關是miles。然後設定popup的content動态設定為擷取的要素長度值。

那麼view.popup.on()這個方法呢?

看看完整的方法體:

view.popup.on("trigger-action", function(event) {
  if (event.action.id === "measure-this") {
    measureThis();
  }
});      

已經灰常、灰常灰常明顯了,在popup這個類的“trigger-action”事件上,綁定一個事件方法體:如果“觸發”的“動作(action)”的id是“measure-this”,那麼執行measureThis()方法執行測距并輸出到popup的content上。

于是,這個trigger-action是什麼?

trigger-action是popup的事件的一種,和普通的click事件是一樣的,意義就是popup的“當動作(action)被觸發時”。click即“當滑鼠點選時”。

而Popup的triggerAction()方法則接受一個索引,去觸發trigger-action這個事件,然後執行索引對應的action。

現在明白了吧!

總結一下。

想要自己弄個按鈕在彈窗上,就要:告訴AJS我要建立一個按鈕,這個按鈕能做什麼事情。

第一步,建立一個按鈕,使用Object對象measureThisAction來建立,并添加到PopupTemplate的actions屬性中(數組形式)。

第二步,寫出這個按鈕的事件方法measureThis,然後把它綁定到trigger-action事件上。

【Part II 為按鈕定制更進階的獨特的功能】

節約篇幅,引用和函參骨架一起給出

require(
    [ 
        "esri/Map","esri/views/MapView","esri/layers/FeatureLayer","dojo/domReady!"
    ],
    function(Map,MapView,FeatureLayer){
        var map = new Map({});
        var view = new MapView({});
        var featureLayer = new FeatureLayer({
            ...
            definitionExpression: "country = 'United States'",
            popupTemplate: {
                  ...
                  actions: [{ id: "find-brewery", image: "beer.png",title: "Brewery Info"}]
            }
        });
        map.add(featureLayer);
        view.then(function(){
            var popup = view.popup;
            popup.viewModel.on("trigger-action", function(event){...})
        });
    }
)      

鑒于JavaScript的文法特性及面向對象的特性,Part I的很多對象、方法參數都直接用{}指派了。

可以看到仍然是map和view的執行個體化,用的也是featureLayer和actions,actions不是[對象名]而是[{}]這種寫法給定,不過并沒有什麼實質性的差別。

然後把featureLayer添加到map中去。

最後和Part I就有所不同了,Part I是view.popup.on("trigger-action", function(event){...})

而這裡Part II,則是在view的回調函數中先擷取popup,然後使用popup.viewModel.on("trigger-action", function(event){...})

我們暫時不看這有什麼差別,先看看這個function(event){...}做了什麼:

popup.viewModel.on("trigger-action", function(event) {
  if (event.action.id === "find-brewery") {
    var attributes = popup.viewModel.selectedFeature.attributes;
    var info = attributes.website;
    if (info !== null) {
      window.open(info.trim());
    } else {
      window.open("https://www.google.com/search?q=" +
        attributes.name);
    }
  }
});      

仍和Part I沒有什麼差別,同樣是擷取一些資訊,若info存在則直接打開,若不存在則到Google上搜尋這個關鍵詞,即如果選擇的啤酒店有網站連結,那麼就跳轉到這個網站;如果啤酒店沒有網站,就給出谷歌的搜尋頁面。

是以我覺得,popup.viewModel.on()的寫法和view.popup.on()的寫法沒什麼不同,多一層引用而已。

我們最後到官方的例子中看看有什麼遺漏的資訊:

沒有。說明這兩個寫法應該是通用的?留個标記,以後測試。

至此,第五章也結束了學習,這一章是我比較忙的時候寫的,可能讀起來比較費勁,各位看官還請見諒。

在下一章"Searching"即空間查詢中,我将轉換一下心情,寫出更好的筆記來學習AJS 4.2。

空間查詢的代碼量會激增,而AJS 4.3釋出也快了,是以學習AJS 4.2的時間也不多了,新版本出來必定有新特性,我也會保持跟進繼續更新4.3的新特性,并予以解讀、測試。

下一章再見!

繼續閱讀