- A+
介绍
ExtJS提供了许多高度可定制化内置组件。如果它不在框架(framework)里面,你可以很容易的扩展这些类,或者浏览Sencha市场(Sencha Market)寻找你可能需要的任何东西。那些都在大部分的时间里运行得很好,但是有时候你兴许想要使用一些不是使用ExtJS的组件系统构建的第三方库。有很多方法可以帮助你解决这个问题,而最简单的一种方法是创建一个定制的封装组件,用来处理你的这个库,这样它就可以在你的应用中重用了。
实现概述
封装组件的目标就是封装第三方库在自我安装和与Ext JS交互时的逻辑要求。你能很随意地设定你的应用程序能使用的第三方库提供的API。在这个问题上,你有以下几个选项。如果第三方的库相对比较简单,而你想控制对API的访问,那么你可以封装每一个API的方法,在你的封装器里,你可以封装成相应的方法。这种方法能允许你隐藏不想暴露的方法,还允许你拦截你引进的附加定制的逻辑函数调用。用。另外一种选择则是暴露一些在API中的根对象,这样其他的控件就能在对象上直接自由地调用任何API方法。在大多数情形下,这将可能是你最后的方法,但是所有的项目都不同。
为了阐述这个观点,我们将围绕Leaflet库创建一个封装组件。Leaflet是一个开源的地图Javascript库,它由Universal Mind的Vladimir Agafonkin创建的。我们将在一个应用程序中使用这个封装组件。该应用程序给我们展示了一个地图并提供了一个可以移动到地图中指定位置的按钮。
Leaflet能在不同地图服务中整合地图配置文件,给你在地图外观选择上提供最大的柔韧度。在这个例子中,我们将使用由CloudMap提供的滴入配置文件。你可以免费注册一个账号,获取API密钥在你的请求中使用(我们将在这个例子的后面使用)。对于更多的地图配置文件的信息,请访问Leaflet
添加库引用
在你应用程序中你要做的第一件事就是添加库对HTML文件的引用, 这样库就能生效并使用。在我们的例子中,我们将添加两行代码到每个Leaflet文档的头部。你可以在 Leaflet Quick Start Guide中获得更详细的安装细节。
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.5/leaflet.css" /> <script src="http://cdn.leafletjs.com/leaflet-0.5/leaflet.js"></script>
组建定制控件
我们下一步要做的就是拓展Ext组件,为Leaflet创建封装组件。Ext组件将提供一个大致的,带有空UI界面的控件,但是包含所有框架需要的在任何布局中都表现出色的方法
当整合第三方库的时候,我们通常需要配置和初始化库来与我们的需求相配。对于我们的例子,在重载'afterRender'方法时,我们将操作Leaflet的初始化。这个方法的运行发生在,我们定制的组件已经被渲染到DOM并准备开始交互的时候 。我们同样添加一个类别名并为我们的地图引用配置变量。
Ext.define('Ext.ux.LeafletMapView', { extend: 'Ext.Component', alias: 'widget.leafletmapview', config:{ map: null }, afterRender: function(t, eOpts){ this.callParent(arguments); var leafletRef = window.L; if (leafletRef == null){ this.update('No leaflet library loaded'); } else { var map = L.map(this.getId()); map.setView([42.3583, -71.0603], 13); this.setMap(map); L.tileLayer('http://{s}.tile.cloudmade.com/{key}/{styleId}/256/{z}/{x}/{y}.png', { key: 'YOUR_API_KEY', styleId: 997, maxZoom: 18 }).addTo(map); } } });
单步进入 ‘afterRender’ :
var leafletRef = window.L; if (leafletRef == null){ this.update('No leaflet library loaded'); } else { .... }
我们尝试访问加载在window.L命名空间的Leaflet库。如果我们不能获得库的引用,我们将更新组件的html文件,显示一则声明存在加载库文件错误的消息。
var map = L.map(this.getId());
在这里我们创建一个Leaflet地图对象,把Ext JS组件的ID号传递过去。缺省情况下,HTML的标签由Ext创建。 组件将会变为一个div,这是Leaflet初始化地图控件要求做到的。相比硬编码一个ID给div的引用,我们将使用Ext框架在渲染控件时生成的ID号。这允许我们在application.map.setView([42.3583, -71.0603], 13)函数调用上拥有多个控件的实例。
这一步就把地图的纬度/经度设置为麻省州波士顿的位置,并设置放大级别为13。对于不同的locations.this.setMap(map)的函数调用,还有很多的在线工具供查阅纬度和经度。
设置地图对地图变量的引用,我们在随后就会访问它。
L.tileLayer('http://{s}.tile.cloudmade.com/{key}/{styleId}/256/{z}/{x}/{y}.png', { key: 'YOUR_API_KEY', styleId: 997, maxZoom: 18 }).addTo(map);
这将会配置Leaflet时用CloudMade的地图配置文件。假设你创建一个账号并注册你的应用程序,你将获得一个API密钥,这个密钥是你要在‘YOUR_API_KEY’里要提供的。不要担心迷惑性的URL网址,这是当你移动地图时,Leaflet将怎样动态加载你的配置文件。我建议你去看一看Leaflet API文档。
此时此刻,你有一个基本的地图控件可以在你的应用中使用。然而,我们完全没有这样做。如果你按照现状来使用,你不会看见你所期待的。尺寸和你的布局不搭配。Leaflet需要我们去调用一个叫做'invalidateSize()'这个方法在每次地图尺寸发生变化的时候,然后Leaflet就会渲染那个尺寸。这在我们的封装组件中是个很容易解决的问题。我们可以重载'onResize'方法,这个方法会在布局中尺寸发生改变时调用,并在地图中调用'invalidteSize'方法。
我们要把如下代码添加到我们的控件中:
onResize: function(w, h, oW, oH){ this.callParent(arguments); var map = this.getMap(); if (map){ map.invalidateSize(); } }
这将会在布局发生改变时被调用,如果我们有一个合法的地图引用,那么我们将看到这一点。如果没有,我们将通过‘invalidateSize’通知Leaflet。
现在我们可以在布局中使用组件,你可以看到它会响应我们提供给它的布局维度。如果布局因为浏览器尺寸改变或者滑动而改变,你将会看到新尺寸会被应用。在我们定制的封装组件,我们用了行数不多的代码,是第三方库Leaflet控件完美地搭载Ext JS布局系统。
使用示例
我们使用这个新的封装组件,来建立一个简单的Ext JS应用。
Ext.Loader.setConfig({ enabled: true, paths:{ 'Ext.ux':'ux' } }); Ext.require(['Ext.ux.LeafletMapView']); Ext.onReady(function() { Ext.create('Ext.container.Viewport', { layout: 'vbox', items: [ { xtype: 'leafletmapview', flex: 1, width: '100%' } ] }) });
这将创建一个使用浏览器全屏的viewport,并使用全景渲染我们的地图。你使用一些简单的缩放控件,将会看到一个较大的波士顿地区的地图。
下一步要做的就是操作地图和外部控件的交互。我们在地图上添加一个按钮,在它被点击的时候让地图缩放到一个位置。根据Leaflet文档,你需要做的就是在地图对象上调用'setView',并传递经纬度的坐标数组和缩放级别。我们要用我们的封装组件在我们创建的'afterRender'函数中显示暴露地图对象,然后使这个按钮能够被对象和调用在之上的方法所访问。
在视区项目数组中把这个控件放到我们地图控件之上:
{ xtype: 'button', text: 'Show Buffalo', listeners:{ click: function(){ var map = Ext.ComponentQuery.query("viewport leafletmapview")[0]; map.getMap().setView([42.8864, -78.8786], 13); } } }
以上代码将会显示一个按钮。当它被点击的时候,代码将会获得一个对地图对象的引用,并更新它的视窗到新的位置。有很多方法引用一个在Ext JS应用程序中的组件,包括Controler refs,Ext.ComponentQuery()等等。在这个例子中,为了方便,我们将使用一个组件查询来找到在视窗中的地图组件。一旦我们获得了那个引用,我们可以调用'getMap'来获取Leaflet地图实例并在其上直接调用任何Leaflet的API函数。
此后,你可以让你的组件变得巧妙无比。你能为所有必需的启动参数添加配置属性,你就能定制每一个使用Ext Js配置参数的实例,而不是第三方库的传统做法。你可以添加新的属性来切换库特性。举个例子,你可以添加一个属性来启用Leaflet的定位功能,这将会试图调用你浏览器的地理API来查找你的位置。你可以我的GitHub上,看到更加复杂的例子(仓库地址)
结论
所有的库都不同,并可能会带来附加的挑战,但是这个概念将会帮助你让你的Ext JS或者Sencha Touch应用集整合到一起。在Sencha市场和GitHub已经有很多的封装组件可以获得,所以你可能不需要自己创建。但是如果没有一个你所需要的库,你现在就知道如何创建你自己的库并把它分享给其他Sencha社区的其他开发者。