ArcGIS API For JavaScrip入门 (7)-写一个类

已被阅读 973 次 | 文章分类:ArcGIS API For Javascript | 2022-01-03 23:01

这篇教程主要介绍ArcGIS API For JavaScrip如何通过创建类,解耦我们的代码,便于维护

1 使用模块和类来管理代码

大多数在 Web 工作很长时间的开发人员在管理 JavaScript 代码的方式上有所改进。刚开始,通常将所有内容(HTML、CSS 和 JavaScript)放在一个文件中。最终这变得很麻烦,并且 JavaScript(以及 CSS)被移动到一个或多个单独的文件中。随着 JS 代码量的不断增长,JS 文件的数量也在不断增长。随着时间的推移,管理引用在其他地方定义的变量和对象的多个不同文件变得难以维护。

此问题的一种解决方案是使用面向对象 (OO) 编程中的模式来处理大型 JavaScript 代码库。通过使用面向对象的风格可以避免应用程序中的意大利面条式代码,并增加代码被重用的可能性。应用程序维护也得到简化,从而可以更快地修复错误并在更短的时间内实现新功能。

本教程的主要目标是让开发人员免于自己解决这个问题,提供一些示例说明如何使用 Dojo 提供的工具编写类,如何使用 Dojo 和 ArcGIS API 提供的类JavaScript 在自己的类中,以及如何将类打包为符合异步模块定义 (AMD) 规范的模块。

本教程将介绍编写一个类以通过他们的API搜索Seat Geek,该API接受纬度、经度坐标和搜索半径。Seat Geek API 返回属于指定感兴趣区域内的事件(音乐会、棒球比赛等)的信息。

搜索 Seat Geek 的类将被称为“SeatGeekSearch”,并将在“extras”文件夹中。该类所在的模块是“extras/SeatGeekSearch”,并且模块名称不是硬编码该名称,而是派生自 SeatGeekSearch.js 的路径。

在使用有助于实现 OO JS 的各种 Dojo 组件之前,花一点时间阅读一些 Dojo 教程:定义模块创建类

2 创建模块

在编写一个类之前,我们需要创建一个模块来存放一个类。define()让我们知道如何做到这一点,并加载类所需的任何依赖项。define 函数是 Dojo 模块加载器的一部分,类似于其他 AMD 加载器提供的定义,例如 require.js。

请注意,模块和类不是一回事。在此处显示的示例中,创建的模块包含一个类。这是建议遵循的模式。一个模块可以包含多个类,但不鼓励这样做。

下面的代码位于 SeatGeekSearch.js 的顶部并调用define():

                                    
define(
  ["dojo/_base/declare", "dojo/_base/lang", "esri/request"],
  function(declare, lang, esriRequest) { ... }
);
                                    
                                

要定义的第一个参数是一个模块标识符数组。模块标识符数组中列出的模块将被加载并传递给回调函数(要定义的第二个参数)。首选参数别名用于命名回调函数的参数。这些参数名称可以是你喜欢的任何名称,但建议分别为 Dojo 和 Esri 模块使用Dojo 的首选参数别名和Esri 的首选参数别名

SeatGeekSearch 是一个简单的模块,具有三个依赖项: dojo/_base/declaredojo/_base/langesri/request。Declare 用于创建可以实例化的类,引入 lang 模块以便其hitch函数可用于为来自 SeatGeek API 的响应提供适当的上下文,并且 esri/request 用于与 SeatGeek API 对话。

3 创建一个class

一旦进入传递给定义的回调函数,我们就知道所有依赖项都已加载,因此我们可以创建一个类:

                                    
declare(null, { ... });
                                    
                                

在这里使用时,declare接受两个参数:超类和一个具有属性和方法的对象。有关每个参数的更多信息,请参阅Dojo 的“创建类”教程。这里使用了两个参数,以便创建一个匿名类。创建匿名类的优点是该类不绑定到特定的模块路径,也不会创建全局变量。

                                    
{
  distance: null,
  lastSearchResult: null,
  perPage: null,
  queryParams: null,
  seatGeekUrl: null,

  constructor: function(options){
    // specify class defaults
    this.distance = options.distance || "20mi"; // default seat geek range is 20mi
    this.perPage = options.perPage || 50; // default to 50 results per page
    this.seatGeekUrl = "http://api.seatgeek.com/2/events";

    // returnEvents is called by an external function, esriRequest
    // hitch() is used to provide the proper context so that returnEvents
    // will have access to the instance of this class
    this.returnEvents = lang.hitch(this, this.returnEvents);
  },

  searchByLoc: function(geopoint) {
    var eventsResponse;

    this.queryParams = {
      "lat": geopoint.y,
      "lon": geopoint.x,
      "page": 1,
      "per_page": this.perPage,
      "range": this.distance
    }

    // seat geek endpoints:
    // petco park search using lat, lon:
    // http://api.seatgeek.com/2/events?lat=32.7078&lon=-117.157&range=20mi&callback=c
    // lat, lon for petco park:  32.7078, -117.157
    eventsResponse = esriRequest({
      "url": this.seatGeekUrl,
      "callbackParamName": "callback",
      "content": this.queryParams
    });
    return eventsResponse.then(this.returnEvents, this.err);
  },

  getMore: function() {
    var eventsResponse;

    // increment the page number
    this.queryParams.page++;

    eventsResponse = esriRequest({
      "url": this.seatGeekUrl,
      "callbackParamName": "callback",
      "content": this.queryParams
    });
    return eventsResponse.then(this.returnEvents, this.err);
  },

  returnEvents: function(response) {
    // check number of results
    if ( response.meta.total == 0 ) {
      // console.log("Seat Geek returned zero events: ", response);
      return null;
    }

    // save search result
    this.lastSearchResult = response;
    // console.log("set last search result: ", response, this);

    return response;
  },

  err: function(err) {
    console.log("Failed to get results from Seat Geek due to an error: ", err);
  }
}
                                    
                                

从顶部开始,各种类属性被初始化为 null。当类的构造函数运行时(每当new SeatGeekSearch()调用时都会发生这种情况),类属性将填充通过传递给类构造函数的对象提供的值或默认值。构造函数中的代码还使用lang.hitch为 returnEvents 方法提供适当的上下文,以便在从另一个函数调用时能够访问正确的实例属性。如果省略 lang.hitch 调用,则当作为 esriRequest 的回调调用时,returnEvents 将无法访问类的属性

这就是创建定义类的自定义模块。

4 加载自定义模块

创建自定义模块后,你需要加载它。这归结为告诉 Dojo 的模块加载器如何解析模块的路径,这意味着将模块标识符映射到 Web 服务器上的文件。

Dojo 为此提供了多种选项,其中最简单的是 dojoConfig.paths。在 SitePen 博客上,有一篇文章讨论了别名、路径和包之间的差异,值得一读。您可以使用 dojoConfig.paths 或 dojoConfig.packages 来告诉 Dojo 在哪里可以找到您的自定义模块,但路径具有最简单的语法。

以下是使用 esri cdn 中的 JSAPI 时加载 SeatGeekSearch 模块的方法:

                                    
var dojoConfig = {
  paths: { extras: location.pathname.replace(/\/[^/]+$/, "") + "/extras" }
};
                                    
                                

extras 的值看起来像乱码,但对于从当前 .html 页面的位置导出 extras 文件夹的绝对路径是必要的

5 使用自定义类

既然 Dojo 知道在 extras 文件夹中的何处可以找到模块,就可以使用require将其与应用程序使用的其他模块一起加载。 这是一个加载 extras/SeatGeekSearch 模块以及其他几个 Dojo 和 Esri 模块的 require 块:

                                    
require([
  "dojo/_base/array",
  "extras/SeatGeekSearch",
  "esri/map",
  "esri/geometry/webMercatorUtils"
], function(
  arrayUtils, SeatGeekSearch, Map, webMercatorUtils
) {
  // SeatGeekSearch, as well other modules, are available here
});
                                    
                                

下一步是使用JavaScript 的 new 关键字创建 SeatGeekSearch 类的实例:

                                    
// instantiate the Seat Geek Search class
var sg = new SeatGeekSearch({
  distance: "20mi",
  perPage: 10
});
                                    
                                

最后,连接一个事件监听器,在点击地图时调用 SeatGeekSearch.searchByLoc:

                                    
// search Seat Geek for events when the map is clicked
map.on("click", function(e) {
  // Seat Geek expects latitude, longitude
  var geographic = webMercatorUtils.webMercatorToGeographic(e.mapPoint);
  // searchByLoc returns a deferred
  // once the deferred is resolved,
  // pass the results to a callback function
  var sgResults = sg.searchByLoc(geographic);
  sgResults.then(searchSucceeded, searchFailed);
});
                                    
                                

如果对 Seat Geek API 的请求成功完成,searchSucceeded 将运行。否则,searchFailed 将运行并记录发生的错误。

还提供了一个完整的示例: SeatGeekSearch 示例。单击地图后,来自 Seat Geek 的结果会记录到浏览器的控制台

6 从 Esri 类继承

有时,需要扩展 Esri 提供的类以创建行为与 Esri 版本不同的新类。为此,应用与从头开始创建自定义模块和类相同的模式。但有几个不同之处。具体来说:

  • 加载要作为依赖项继承的类。
  • 在声明中指定要作为超类继承的一个或多个类
  • 覆盖方法或属性以实现自定义功能

例如,如果开发人员想要创建自定义渲染器,工作流程将是加载适当的渲染器模块,通过声明和覆盖getSymbol将渲染器类指定为超类以创建并返回适当的符号。

7 其他示例

SDK 中有几个示例可以创建自定义类:

  • 创建自定义图层
  • 自定义撤销/重做操作
  • 信息窗口 - 自定义
  • 点聚类

QQ群:713985494 | 技术问题解答

Copyright ©2017 xiaobaigis.com . 版权所有 鲁ICP备17027716号