<\/HEAD>
<\/P>
Há algum tempo escrevi um artigo sobre como Abraçar seus módulos AMD<\/A>. Surgiram algumas perguntas ao ver exemplos de como fazer isso, usando as melhores práticas e formas recomendadas de trabalhar com módulos. Então pensei em tentar ajudar com isso hoje.<\/P><\/P>
O que fiz foi pegar um
exemplo da documentação<\/A> que eu tinha atualizado para adicionar alguns recursos<\/A> há algum tempo.<\/P><\/P>
Queria pensar em como poderia dividir isso em um aplicativo mais modular usando AMD.<\/P>
<\/P>
Então, primeiro de tudo, vamos olhar o código, ignorando o HTML por enquanto.<\/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 \/\/_Set the blendRenderer's parameters_\n var blendRendererParams = {\n \/\/_blendMode:"overlay" \/\/_Por padrão, usa "source-over", descomente para exibir modo diferente\n \/\/_Veja: <\/_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: "Proprietário Ocupado",\n color: new Color([0, 0, 255])\n }, {\n field: "RENTER_CY",\n label: "Inquilino Ocupado",\n color: new Color([255, 0, 0])\n }, {\n field: "VACANT_CY",\n label: "Vago",\n color: new Color([0, 255, 0])\n }\n ],\n opacityStops: [\n :& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:{ \ n & nbsp:& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:value : 0.1 , \ n & nbsp:& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:opacity : 0 \ n & nbsp:& nbsp:& nbsp:& nbsp:& nbsp:}, \ n & nbsp:& nbsp:& nbsp:& nbsp:& nbsp:{ \ n & nbsp:& nbsp:& nbsp:& nbsp:& nbsp:value : 1 , \ n & nbsp:& nbsp:& nbsp:& nbsp:& nbsp:opacity : 0.7 \ n & nbsp:& nbsp:& nbsp:& nbsp;} \ n & nbsp :& nb sp ;], \ n & nb sp ;normalizationField : "TOTHU_CY" \ n }; \ n \/Create the PopupTemplate to be used to display demographic info \ n var template = new PopupTemplate({ \ n & nb sp ;"title": "Status de Moradia por Bloco do Censo", \ n & nb sp ;"fieldInfos": [ \ n & nb sp ;& nb sp ;{ \ n & nb sp ;& nb sp ;& nb sp ;"fieldName": "OWNER_CY", \ n & nb sp ;& nb sp ;& nb sp ;"label": "Número de Casas Ocupadas por Proprietários", \ n & nb sp ;& nb sp ;& nb sp ;"visible": true, \ n & nb sp ;& nb sp ;& nb sp ;"format": { \ n & nb sp ;& nb sp ;& nb sp ;& nb sp ;"places": 0, \ n & nb sp ;& nb sp ;& nb sp ;& nb sp ;"digitSeparator": true \ n & nb sp ;& nb sp ;& nb sp;} \ n & nb sp ;& nb sp ;}, { \ n & nb sp ;& nb sp ;& nb sp ;"fieldName": "RENTER_CY", \ n & nb sp ;& nb sp ;& nb sp ;"label": "Número de Casas Ocupadas por Inquilinos", \ n & nb sp ;& nb sp ;& nb sp ;"visible": true, \ n & nb sp ;& nb sp ;& nb sp ;"format": { \ n & nb sp ;& nb sp ;& nb sp ;& nb sp ;"places": 0, \ n & nb sp ;& nb sp ;& nb sp ;& nb sp ;"digitSeparator": true \ n & nb sp ;& nb sp ;& nb sp;} \ n & nb sp ;& nb sp ;}, { \ n & nb sp ;& nb sp ;& nb sp ;"fieldName": "VACANT_CY", \ n & nb sp ;& nb sp ;& nb sp ;"label": "Número de Casas Vagas", \ n & nb sp ;& nb sp ;& nb sp ;"visible": true, \ n & nb sp ;& nb sp ;& nb sp ;"format": { \ n ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;& ;&&nbp... topic.subscribe('blend-select-update', hitch(this, 'updateBlendMode'))
);
},
updateBlendMode: function(mode) {
console.log('atualizar modo de mesclagem com dojo/topic');
var layer = this.get('layer');
var renderer = this.get('renderer');
renderer.blendMode = mode;
layer.setRenderer(renderer);
layer.refresh();
}
});
});Este é um widget bem simples. Usamos um método postCreate para configurar as coisas.
Eu falo sobre o ciclo de vida do dijit neste vídeo. Este método é onde eu configuro minha camada e atribuo o renderer que foi passado nas opções. Há um template HTML simples que é apenas uma cópia do HTML do exemplo que tem uma descrição dos dados. Também estou usando dojo/topic, sobre o qual falei aqui. Vou demonstrar alguns métodos diferentes de comunicação entre widgets, um usando dojo/topic e outro usando dojo/Evented.
Este módulo também tem um método updateBlendMode que simplesmente lida com a inscrição no dojo/topic e atualiza o blendMode. Isso significa que qualquer módulo em sua aplicação pode publicar uma atualização para o blendMode ou qualquer outro motivo. Na minha opinião, este é um método ideal de comunicação entre widgets, porque os widgets individuais não precisam estar cientes uns dos outros.
A outra coisa que o exemplo tinha era um menu select que permitia atualizar o blendMode. Novamente, acho que este é o candidato perfeito para outro widget. O código para este widget poderia ser algo assim:
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 o dojo/Evented e dojo/topic. Você notará que o próprio widget estende dojo/Evented usando dojo/_base/declare. Isso permite usar this.emit() para emitir eventos do seu widget. Este widget tem o menu select para blendModes e também uma checkbox para definir se você quer emitir um evento com o novo blendMode ou publicar o blendMode via dojo/topic.
O template para isso é assim:
<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">cópia</option>
<option value="xor">'xor'</option>
  < option value='overlay'>'overlay'< /option>
< option value='normal'>'normal'< /option>
< option value='multiply'>'multiply'< /option>
< option value='screen'>'screen'< /option>
< option value='darken'>'darken'< /option>
< option value='lighten'>'lighten'< /option>
< option value='color-dodge'>'color-dodge'< /option>
< option value='color-burn'>'color-burn'< /option>
< option value='hard-light'>'hard-light'< /option>
< option value='soft-light'>'soft-light'< /option>
< option value='difference'>'difference'< /option>
< option value='exclusion'>'exclusion'< /option>
< option value='hue'>'hue'< /option>
< option value='saturation'>'saturation'< /option>
< option value='color'>'color'< /option>
< option value='luminosity'>'luminosity'< /option>
</select>
</div>
Ok, então temos dois widgets, um que cria a FeatureLayer com o blendRenderer e outro widget que atualiza o blendMode do renderer.
Vamos conectar isso em um módulo main.js para começar.
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');
// uma forma de atualizar o blendMode
blendSelectionWidget.on('blend-select-update', function(mode) {
console.log('atualizar modo de mesclagem com eventos');
renderer.blendMode = mode;
populationWidget.get('layer').setRenderer(renderer);
populationWidget.get('layer').refresh();
});
});
});
Ok, então o propósito do arquivo main é criar o mapa e carregar os widgets, passando as opções necessárias enquanto também usa os utils que criamos anteriormente. Você também notará que estou usando blendSelectionWidget.on('blend-select-update', function(mode){}) para escutar quando um evento é emitido e atualizar manualmente o renderer da camada no widget de população. Esta é outra forma de fazer comunicação entre widgets, onde você pode fazer isso em um arquivo principal ou talvez tenha um WidgetController que gerencia toda a comunicação dos widgets da sua aplicação, realmente depende do que você preferir. Normalmente, eu até separaria a criação do mapa como seu próprio widget também.
Também gostaria de destacar a dojoConfig para esta aplicação.
var dojoConfig = {
isDebug: true,
deps: ['app/main'],
packages: [{
name: 'app',
location: location.pathname.replace(new RegExp(/\/[^\/]+$/), '') + 'app'
}]
};Isto é muito simples. Fazendo assim, defini um pacote app e todos os meus módulos vivem neste pacote. Isso significa que não preciso criar um pacote widgets ou um pacote utils, posso apenas referenciá-los como app/widgets e app/utils. Note também a propriedade deps. Isso dirá ao carregador Dojo para carregar este módulo quando o Dojo é carregado. Você pode consultar a documentação aqui. Isso torna meu arquivo index.html realmente simples.<\/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>Demonstração de Aplicativo 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>Você pode ler sobre outros métodos para inicializar sua aplicação aqui.<\/P><\/P>Pegue pedaço por pedaço<\/H2>Você pode encontrar a aplicação modularizada completa neste repositório do github. Espero que isso forneça um pouco mais de entendimento sobre como você pode dividir uma aplicação em módulos e como você pode começar a pensar sobre modularidade no seu próprio desenvolvimento. Eu costumo gostar de separar funcionalidades de uma aplicação em seus próprios widgets, seja edição, busca, geocodificação ou até mesmo adicionando algum comportamento.<\/P><\/P>Você pode ver alguns outros exemplos de modularidade em trabalhos que fiz no passado neste starter-kit, que é totalmente configurável, ou em meus experimentos mais recentes neste gerador yeoman, e aplicativo de exemplo. Outro aplicativo que é muito modular é algo como o app cmv.<\/P><\/P>Novamente, esta é simplesmente minha opinião sobre como eu acho que você poderia dividir um app em módulos menores que tornam não apenas mais fácil de manter ao longo do tempo, mas também para adicionar novas funcionalidades facilmente.<\/P><\/P>Para mais dicas e truques de geodev, confira meu blog.<\/P><\/BODY><\/HTML>