<\/HEAD>
<\/P>
Hace un tiempo escribí un artículo sobre cómo Adoptar tus módulos AMD<\/A>. Surgieron un par de preguntas al ver ejemplos de cómo hacerlo, usando las mejores prácticas y formas recomendadas de trabajar con módulos. Así que pensé en ayudar con eso hoy.<\/P><\/P>
Lo que hice fue tomar un
ejemplo de la documentación<\/A> que había actualizado para agregar algunas funciones<\/A> hace un tiempo.<\/P><\/P>
Quería pensar en cómo podría dividir esto en una aplicación más modular usando AMD.<\/P>
<\/P>
Así que primero lo primero, veamos el código, ignorando el HTML por ahora.<\/P>
require([\n "esri\/Color", "esri\/dijit\/PopupTemplate", "esri\/layers\/FeatureLayer", "esri\/map", "esri\/renderers\/BlendRenderer",\n "esri\/symbols\/SimpleFillSymbol", "esri\/symbols\/SimpleLineSymbol", "dojo\/on", "dojo\/domReady!"\n], function (Color, PopupTemplate, FeatureLayer, Map, BlendRenderer, SimpleFillSymbol, SimpleLineSymbol, on){\n map = new Map("map", {\n basemap: "topo",\n center: [-118.40, 34.06],\n zoom: 15\n });\n \/\/-Establecer los parámetros del blendRenderer\n var blendRendererParams = {\n \/\/-blendMode:"overlay" \/\/-Por defecto, usa "source-over", descomenta para mostrar otro modo\n \/\/-Ver: <\/SPAN>https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/CanvasRenderingContext2D\/globalCompositeOperation<\/A>\n symbol: new SimpleFillSymbol().setOutline(new SimpleLineSymbol().setWidth(0)),\n fields: [\n {\n field: "OWNER_CY",\n label: "Ocupado por propietario",\n color: new Color([0, 0, 255])\n }, {\n field: "RENTER_CY",\n label: "Ocupado por inquilino",\n color: new Color([255, 0, 0])\n }, {\n field: "VACANT_CY",\n label: "Vacante",\n color: new Color([0, 255, 0])\n }\n ],\n opacityStops: [\n {\n :& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:value: 0.1,\n :& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:opacity: 0\n :& nbsp:& nbsp:& nbsp:& nbsp;} , \n & nbsp : & nbsp : & nbsp : & nbsp : & nbsp : & nbsp : { \ n & nbsp : & nbsp : & nbsp : & nbsp : & nbsp : & nbsp : value : 1 , \ n & nbsp : & nbsp : & nbsp : & nbsp : & nbsp : & nbsp : opacity : 0.7 \ n & nbsp ; & nb sp ; & nb sp ; & nb sp ; } \ n & nb sp ; ] , \ n & nb sp ; normalizationField : "TOTHU_CY" \ n } ; \ n \/ \/Crear la PopupTemplate para mostrar información demográfica \ n var template = new PopupTemplate ( { \ n & quot ; title & quot ; : & quot ; Estado de vivienda por bloque censal & quot ; , \ n & quot ; fieldInfos & quot ; : [ \ n { \ n & quot ; fieldName & quot ; : & quot ; OWNER_CY & quot ; , \ n & quot ; label & quot ; : & quot ; Número de casas ocupadas por propietarios & quot ; , \ n & quot ; visible & quot ; : true , \ n & quot ; format & quot ; : { \ n places : 0 , \ n digitSeparator : true \ n } \ n } , { \ n fieldName : "RENTER_CY" , \ n label : "Número de casas ocupadas por inquilinos" , \ n visible : true , \ n format : { \ n places : 0 , \ n digitSeparator : true \ n } \ n } , { \ n fieldName : "VACANT_CY" , \ n label : "Número de casas vacantes" , \ n visible : true , \ n format : { \ n places : 0 , \ n digitSeparator : true \ n } \ n } , { \ n fieldName : "TOTHU_CY" , \ n label : "Total de unidades habitacionales" , \ n visible : true , \ n format : { \ n places : 0 , \ n digitSeparator : true \ n } \ n } ] }); & nb sp ; var layerUrl = "< / SPAN >< A class = "jive-link-external-small" href = "https://community.esri.com/external-link.jspa?url=http://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/Blocks near Wilshire enriched with Key Facts/FeatureServer/0" target = "_blank">http://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/Blocks near Wilshire enriched with Key Facts/FeatureServer/0";< /SPAN>
var renderer = new BlendRenderer(blendRendererParams);
layer = new FeatureLayer(layerUrl, {
id: "blendedLayer",
outFields: ["TOTHU_CY", "RENTER_CY", "OWNER_CY", "VACANT_CY"],
opacity: 1,
definitionExpression: "TOTHU_CY > 0",
infoTemplate: template
});
layer.setRenderer(renderer);
map.addLayer(layer);
on(document.getElementById("blendSelect"), "change", function(e) {
renderer.setBlendMode(e.target.value);
layer.redraw();
});
});Esto no está tan mal para una aplicación pequeña, pero me gusta pensar en ¿cómo puedo escalar mi aplicación?< /EM> ¿Dónde puedo dividirla un poco y qué está pasando exactamente?< /EM>
< /P>
Advertencia - Esta es solo mi opinión sobre cómo podrías modularizar esta aplicación, otros pueden tener opiniones diferentes.< /EM>
< /P>
Paso a paso< /H2>Primero que nada, esta aplicación utiliza el BlendRenderer que discutí aquí< /A>. Eso me hace pensar que probablemente podría separar toda esa funcionalidad. También estoy creando un PopupTemplate< /A> y el BlendRenderer< /A> directamente en este único archivo. Cuando veo cosas así, que son parámetros simples, tiendo a colocarlos en módulos utilitarios, lo que significa que pueden reutilizarse fácilmente en múltiples módulos y forman parte del núcleo común de mi aplicación.< /P>< /P>
Así que puedo separarlos en sus propios módulos utilitarios que puedes
ver en este repositorio de ejemplo< /A>.< /P>< /P>
Ok, eso fue bastante simple, solo copiar y pegar en un par de módulos. Ahora pensemos en lo que hace mi aplicación. Para simplificar, digamos que me enfoco en visualizar información poblacional. Centremos esto en un widget. Este widget podría verse algo así:< /P>
< /P>
define([\n 'dojo\/_base\declare',\n 'dojo\/_base\lang',\n 'dojo/topic',\n 'dijit\/_WidgetBase',\n 'dijit\/_TemplatedMixin',\n 'esri/layers/FeatureLayer',\n 'dojo/text!./widget.html'\n], function(\n declare, lang, topic,\n _WidgetBase, _TemplatedMixin,\n FeatureLayer,\n templateString\n) {\n var hitch = lang.hitch;\n return declare([_WidgetBase, _TemplatedMixin], {\n templateString: templateString,\n baseClass: 'population-info',\n postCreate: function() {\n var layerOptions = this.get('layerOptions');\n var renderer = this.get('renderer');\n var url = this.get('url');\n var map = this.get('map');\n var layer = new FeatureLayer(url, layerOptions);\n layer.setRenderer(renderer);\n map.addLayer(layer);\n this.set('layer', layer);\n this.own( \/ hacer esto para que el widget pueda limpiar la memoria si se destruye
topic.subscribe('blend-select-update', hitch(this, 'updateBlendMode'))
);
},
updateBlendMode: function(mode) {
console.log('actualizar modo de mezcla con dojo/topic');
var layer = this.get('layer');
var renderer = this.get('renderer');
renderer.blendMode = mode;
layer.setRenderer(renderer);
layer.refresh();
}
});
});Este es un widget bastante simple. Usamos un método postCreate para configurar las cosas.
Hablo sobre el ciclo de vida del dijit en este video. Este método es donde configuro mi capa y asigno el renderer que se pasó en las opciones. Hay una plantilla HTML simple que es solo una copia del HTML del ejemplo que tiene una descripción de los datos. También estoy usando dojo/topic, sobre lo que hablé aquí. Voy a demostrar un par de métodos diferentes de comunicación entre widgets, uno usando dojo/topic y otro usando dojo/Evented.Este módulo también tiene un método updateBlendMode que simplemente maneja la suscripción a dojo/topic y actualiza el blendMode. Esto significa que cualquier módulo en tu aplicación puede publicar una actualización al blendMode o por cualquier otra razón. En mi opinión, este es un método ideal de comunicación entre widgets, porque los widgets individuales no necesitan estar conscientes unos de otros.Otra cosa que tenía el ejemplo era un menú select que te permitía actualizar el blendMode. De nuevo, creo que este es el candidato perfecto para otro widget. El código para este widget podría verse algo así:define([
'dojo/_base/declare',
'dojo/Evented',
'dojo/topic',
'dijit/_WidgetBase',
'dijit/_TemplatedMixin',
'dojo/text!./widget.html'
], function(
declare, Evented, topic,
_WidgetBase, _TemplatedMixin,
templateString
) {
return declare([_WidgetBase, _TemplatedMixin, Evented], {
templateString: templateString,
baseClass: 'blend-select',
onChange: function(e) {
var val = e.target.value;
if (this.cboxNode.checked) {
topic.publish('blend-select-update', val);
} else {
this.emit('blend-select-update', val);
}
}
});
})Este widget usa dojo/Evented y dojo/topic. Notarás que el widget en sí extiende dojo/Evented usando dojo/_base/declare. Esto te permite usar this.emit() para emitir eventos desde tu widget. Este widget tiene el menú select para blendModes y también una casilla de verificación para establecer si quieres emitir un evento con el nuevo blendMode o publicar el blendMode vía dojo/topic.La plantilla para esto se ve así:<div>
<div>
<input type="checkbox" data-dojo-attach-point="cboxNode"> Usar dojo/topic
</div>
<select data-dojo-attach-event="change:onChange">
<option value="source-over">source-over</option>
<option value="source-in">source-in</option>
<option value="source-out">source-out</option>
<option value="source-atop">source-atop</option>
<option value="destination-over">destination-over</option>
<option value="destination-in">destination-in</option>
<option value="destination-out">destination-out</option>
<option value="destination-atop">destination-atop</option>
<option value="lighter">lighter</option>
<option value="copy">copia</option>
<option value="xor">xor</option>
<option value="overlay">sobreponer</option>
<option value="normal">complexión normal</option>
<option value="multiply">multiplicar</option>
<option value="screen">pantalla</option>
<option value="darken">>oscurecer</option>
< option value= "lighten">aclarar< / option>
< option value= "color-dodge">esquivar color< / option>
< option value= "color-burn">quemar color< / option>
< option value= "hard-light">luz dura< / option>
< option value= "soft-light">luz suave< / option>
< option value= "difference">diferencia< / option>
< option value= "exclusion">exclusión< / option>
< option value= "hue">matiz< / option>
< option value= "saturation">saturación< / option>
< option value= "color">color< / option>
< option value= "luminosity">luminosidad< / option>
Ok, entonces tenemos dos widgets, uno que crea la FeatureLayer con el blendRenderer y otro widget que actualiza el blendMode del renderer.Vamos a conectar esto en un módulo main.js para comenzar.define([
'esri/map',
'esri/layers/ArcGISTiledMapServiceLayer',
'app/widgets/population/widget',
'app/widgets/blendselection/widget',
'app/utils/popupUtil',
'app/utils/rendererUtil'
], function(
Map, ArcGISTiledMapServiceLayer,
PopulationWidget, BlendSelectionWidget,
popup,
renderer
) {
var map = new Map('map', {
center: [-100, 38],
zoom: 5
});
var tileLayer = new ArcGISTiledMapServiceLayer('http://tiles.arcgis.com/tiles/nzS0F0zdNLvs7nc8/arcgis/rest/services/US_Counties_basemap/MapServer');
map.addLayer(tileLayer);
map.on('load', function() {
var populationWidget = new PopulationWidget({
layerOptions: {
outFields: ['WHITE', 'POP2012', 'AMERI_ES', 'HISPANIC', 'BLACK', 'ASIAN', 'POP12_SQMI', 'NAME', 'STATE_NAME'],
opacity: 1,
infoTemplate: popup
},
renderer: renderer,
map: map,
url: 'http://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/USA_Counties_Generalized/FeatureServer/0'
}, 'population-container');
var blendSelectionWidget = new BlendSelectionWidget({}, 'blend-selection-container');
// una forma de actualizar el blendMode
blendSelectionWidget.on('blend-select-update', function(mode) {
console.log('actualizar modo de mezcla con eventos');
renderer.blendMode = mode;
populationWidget.get('layer').setRenderer(renderer);
populationWidget.get('layer').refresh();
});
});
});Ok, entonces el propósito del archivo main es crear el mapa y cargar los widgets, pasando las opciones requeridas mientras también usas las utilidades (utils) que creamos antes. También notarás que estoy usando blendSelectionWidget.on('blend-select-update', function(mode){}) para escuchar cuando se emite un evento y actualizar manualmente el renderer para la capa en el widget de población. Esta es otra forma en la que puedes hacer comunicación entre widgets, donde puedes hacerlo en un archivo principal o tal vez tendrás un WidgetController que maneje toda la comunicación entre widgets de tus aplicaciones, realmente depende de lo que prefieras. Normalmente, incluso separaría la creación del mapa como su propio widget también.También me gustaría señalar la configuración dojoConfig para esta aplicación.var dojoConfig = {
isDebug: true,
deps: ['app/main'],
packages: [{
name: 'app',
location: location.pathname.replace(new RegExp(/\/[^\/]+$/), '') + 'app'
}]
};Eso es muy simple. Haciéndolo de esta manera, he definido un paquete app y todos mis módulos viven en este paquete. Esto significa que no tengo que crear un paquete widgets o un paquete utils, puedo simplemente referenciarlos como app/widgets y app/utils. Nota también la propiedad deps. Esto le indicará al cargador Dojo que cargue este módulo cuando se carga Dojo. Puedes consultar la documentación aquí. Esto hace que mi archivo real index.html sea realmente simple.<\/P><!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no" />
<title>Demostración de Aplicación Modular</title>
<link rel="stylesheet" href="http://js.arcgis.com/3.14/esri/css/esri.css"><\/SPAN>
<link rel="stylesheet" href="css/main.css">
<script src="dojoConfig.js"></script>
</head>
<body>
<div id="blend-selection-container"></div>
<div id="population-container"></div>
<div id="map"></div>
<script src="http://js.arcgis.com/3.14/"></script><\/SPAN>
</body>
</html><\/PRE>Puedes leer sobre otros métodos para inicializar tu aplicación aquí.<\/P><\/P>Tómalo paso a paso<\/H2>Puedes encontrar la aplicación modularizada completa en este repositorio de github. Espero que esto proporcione un poco más de perspectiva sobre cómo podrías dividir una aplicación en módulos y cómo puedes empezar a pensar en modularidad en tu propio desarrollo. Tiende a gustarme separar las características de una aplicación en sus propios widgets, ya sea edición, búsqueda, geocodificación o incluso simplemente agregar algún comportamiento.<\/P><\/P>Puedes ver algunos otros ejemplos de modularidad en cosas en las que trabajé en el pasado en este starter-kit, que es totalmente configurable o mis últimos experimentos en este generador yeoman, y aplicación de ejemplo. Otra aplicación que es muy modular es algo como la aplicación cmv.<\/P><\/P>Nuevamente, esta es simplemente mi opinión sobre cómo creo que podrías dividir una aplicación en módulos más pequeños que no solo faciliten su mantenimiento con el tiempo sino también agregar fácilmente nueva funcionalidad.<\/P><\/P>Para más consejos y trucos geodev, visita mi blog.<\/P><\/BODY><\/HTML>