【翻译】在Ext JS集成第三方库
原文地址:http://www.sencha.com/blog/integrating-ext-js-with-3rd-party-libraries/
作者:Kevin Kazmierczak
Kevin Kazmierczak is a Senior Technical Architect at Universal Mind, an innovative digital solutions agency that fuses the design capabilities of an interactive firm with the the technical expertise of a systems integrator. He specializes in building frontend applications using Objective-C, HTML/JS, and Flex. Kevin holds an MBA and a BA in Computer Science from Alfred University.
介绍
Ext JS提供了大量可高度自定义的直接就可以使用的内部组件。如果组件不在框架中,也可以容易的通过扩展类的方式,甚至通过浏览Sencha市场来获取所需要的组件。这工作可能需要花费大量的时间,有时候为了节省时间,会考虑使用没有包含在Ext JS组件系统的第三方库。针对这种情况,有许多解决方案,而最简单的方法就是创建一个自定义封装组件来处理库,这样就可以在应用程序中实现重用。
实现概述
封装组件的目的是通过将第三方库所需的逻辑封装起来以方便配置以及与Ext JS框架实现交互。对于在应用程序中使用多少第三方库的API,有很大的自由度。如果库相当简单,且希望对API的访问进行控制,那就可以将API的每一个方法都封装为对应的方法。这样就可以在想要引用额外的自定义逻辑的时候隐藏对不想公开的方法或拦截方法的调用。另一种方式是公开一些API中的根对象,以便其他控件可以自由的通过对象直接调用任何API方法。在大多数情况下,这可能是最终的解决办法,不过,不同的项目会有不同。
为了演示这一方式,将为Leaflet创建一个封装组件Leaflet是一个由Universal Mind的 Vladimir Agafonki创建的开源的Javascript地图库。在应用程序中将使用这个封装组件来显示一张地图,并提供一个按钮来将地图移动到一个指定位置。
Leaflet可以将来自许多不同的地图服务的地图块整合起来,这样就为地图的显示提供了极大的灵活性。在示例中,将使用CloudMade提供的地图块。可以到CloudMade的网站上注册一个免费账号,然后就可在后续的请求(后面的示例需要使用到)中使用获得的API密钥。要想了解更多的与地图块有关的信息,可访问Leaflet的网站。
添加库引用
在应用程序中,首先要做的是在HTML文件中添加库的引用,这样才可以使用库。在示例中,需要在HEAD元素内添加两行来引用Leaflet。在Leaflet快速入门指南的Leftlet安装文档中可获得更多的详细信息。
<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.Component扩展出Leaflet的封装组件。Ext.Component会使用一个空白的UI来搭建控件的骨架,不过,它提供了所有所需的框架方法以便让它在任何布局中都能运行得很好。
在集成第三方库的时候,通常都需要配置和初始化它来满足需求。对于示例,需要重写afterRender方法来处理Leftlet的初始化。这个方法会在自定义组件渲染到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());
这里将传递Ext JS的组件id来创建Leftlet地图对象。默认情况下,由Ext.Component创建的HTML标记是DIV,而这正是Leaflet用来初始化地图组件所需的。在这里使用的是在渲染控件时Ext框架生成的id,而不是使用硬编码的id来引用DIV。这样的好处就是可以在应用程序中创建多个实例。
map.setView([42.3583, -71.0603], 13);
这句将地图的位置设置为马萨诸塞州的波士顿的纬度和经度,地图的缩放等级为13。有许多在线工具可以用来查找不同地点的纬度和经度。
this.setMap(map);
将地图引用设置给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中。不用去担心那个混乱的网址,当移动地图的时候,Leaflet会动态去加载地图块。如果需要了解更多的信息,建议查看Leaflet的API文档。
到目前为止,已经创建好基本的地图控件了,可以在应用程序中使用了。不过,实际上这还没有完全实现。如果就这样去使用它,会发现它并不是所期望的效果。地图的尺寸并没有填满布局。Leaflet需要随时调用名为invalidateSize()的方法来调整地图的尺寸,并让它渲染成这个尺寸。这个问题在封装组件中很容易解决。可以重写onResize方法来实现,这样,布局的尺寸变化就可以随时调用地图的invalidateSize方法。
添加以下代码到控件:
onResize: function(w, h, oW, oH){ this.callParent(arguments); var map = this.getMap(); if (map){ map.invalidateSize(); } }
这样,布局的改变且有一个有效的地图引用就可以随时进行调用。如果调整了尺寸,就会告诉Leaflet去执行invalidateSize方法。
现在,就可以在布局中使用组件了,而且会看到地图将在提供的布局尺寸内显示。如果布局因浏览器调整尺寸或滑块而改变,将会看到地图应用了新的尺寸。在自定义的封装组件中,通过了几行代码就可以让第三方库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文档,这需要调用map对象的setView方法,并纬度和经度的坐标数组以及缩放等级传递给它。所要做的,就是公开封装组件在afterRender方法中创建的map对象,然后在按钮中访问对象并调用它的方法。
在viewport的items数组中地图组件的上面添加以下代码:
{ 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应用程序中,有许多方式来引用组件,包括控制器的refs、Ext.ComponentQuery()等等。对于当前示例来说,最方便的是使用组件查询来找到在viewport中的地图组件。一旦获得引用,就可以调用getMap方法来获得Leaflet的地图实例并直接调用任何Leaflet的APi方法。
从这里开始,就可以根据自己的需要来实现组件。可以针对所有的安装参数添加配置属性,这样就可以使用Ext JS的配置参数代替第三方库的约定来自定义组件的每一个实例。还可以添加新属性来切换库功能。例如,可以添加一个属性来启用Leaflet的定位功能,这样就可通过浏览器的Geolocation API来找到你的位置。可以在我的GitHub库找到更完整的示例。
小结
所有的库都会有不同,且会出现更多的挑战,不过,这个概念将有助于在Ext JS或Sencha Touch应用程序中整合他们。在Sencha市场或GitHUB已经有许多封装组件,因此可能不需要创建你自己的封装组件。如果找不到所需要的库,那就要创建自己的封装组件,并将它分享到Sencha开发社区。