<\/HEAD>
<\/P>
Il y a quelque temps, j'ai écrit un article sur la façon de Adopter vos modules AMD<\/A>. Quelques questions sont apparues en voyant des exemples de la manière de procéder, en utilisant les meilleures pratiques et les méthodes recommandées pour travailler avec les modules. J'ai donc pensé que je pourrais aider à ce sujet aujourd'hui.<\/P><\/P>
Ce que j'ai fait, c'est prendre un
exemple tiré de la documentation<\/A> que j'avais mis à jour pour ajouter quelques fonctionnalités<\/A> il y a quelque temps.<\/P><\/P>
Je voulais réfléchir à la manière dont je pourrais découper cela en une application plus modulaire utilisant AMD.<\/P>
<\/P>
Alors, tout d'abord, regardons le code, en ignorant le HTML pour l'instant.<\/P>
require([
"esri\/Color", "esri\/dijit\/PopupTemplate", "esri\/layers\/FeatureLayer", "esri\/map", "esri\/renderers\/BlendRenderer",
"esri\/symbols\/SimpleFillSymbol", "esri\/symbols\/SimpleLineSymbol", "dojo\/on", "dojo\/domReady!"
], function (Color, PopupTemplate, FeatureLayer, Map, BlendRenderer, SimpleFillSymbol, SimpleLineSymbol, on){
map = new Map("map", {
basemap: "topo",
center: [-118.40, 34.06],
zoom: 15
});
\/\/_Set the blendRenderer's parameters
var blendRendererParams = {
\/\/_blendMode:"overlay" \/\/_By default, it uses "source-over", uncomment to display different mode
\/\/_See: <\/>
https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/CanvasRenderingContext2D\/globalCompositeOperation<\/><\/>
symbol: new SimpleFillSymbol().setOutline(new SimpleLineSymbol().setWidth(0)),
fields: [
{
field: "OWNER_CY",
label: "Propriétaire occupant",
color: new Color([0, 0, 255])
}, {
field: "RENTER_CY",
label: "Locataire occupant",
color: new Color([255, 0, 0])
}, {
 :& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:field: "VACANT_CY",
 :& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:label: "Vacant",
 :& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:& nbsp:color: new Color([0, 255, 0])
} ,
&nb sp ; opacityStops : [
&nb sp ; &nb sp ; {
&nb sp ; &nb sp ; &nb sp ; value : 0.1,
&nb sp ; &nb sp ; &nb sp ; opacity : 0
&nb sp ; &nb sp ; },
&nb sp ; &nb sp ; {
&nb sp ; &nb sp ; &nb sp ; value : 1,
&nb sp ; &nb sp ; &nb sp ; opacity : 0.7
&nb sp ; &nb sp ; }
&nb sp ; ],
&nb sp ; normalizationField : "TOTHU_CY"
&nb sp ; };
&nb sp ; \/\/_Create the PopupTemplate to be used to display demographic info
&nb sp ; var template = new PopupTemplate({
'title': 'Statut du logement par bloc de recensement',
'tfieldInfos': [
'tfieldName': 'OWNER_CY', 'label': 'Nombre de maisons occupées par le propriétaire', 'visible': true,'format': {'places': 0,'digitSeparator': true}}, {'fieldName': 'RENTER_CY','label': 'Nombre de maisons occupées par des locataires','visible': true,'format': {'places': 0,'digitSeparator': true}}, {'fieldName': 'VACANT_CY','label': 'Nombre de maisons vacantes','visible': true,'format': {'places': 0,'digitSeparator': true}}, {'fieldName': 'TOTHU_CY','label': 'Nombre total d'unités de logement','visible': true,'format': {'places': 0,'digitSeparator': true}}]});
var layerUrl = "http://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/Blocks%20near%20Wilshire%20enriched%20with%20Key%20Facts/FeatureServer/0";
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();
});
});
Cela n'est pas si mal pour une petite application, mais j'aime réfléchir à comment puis-je faire évoluer mon application ?< /EM> Où puis-je la découper un peu et que se passe-t-il exactement ?< /EM>
< /P>
Avertissement - Ceci n'est que mon avis sur la façon dont vous pourriez modulariser cette application, d'autres peuvent avoir des opinions différentes.< /EM>
< /P>
Étape par étape
Tout d'abord, cette application utilise le BlendRenderer dont j'ai parlé ici. Cela me fait penser que je pourrais probablement extraire toute cette fonctionnalité. Je crée également un PopupTemplate et le BlendRenderer directement dans ce fichier unique. Quand je vois ce genre de choses, qui sont plutôt des paramètres simples, j'ai tendance à les placer dans des modules utilitaires, ce qui signifie qu'ils peuvent être réutilisés dans plusieurs modules assez facilement et font partie d'un noyau commun à mon application.< /P>
< /P>
Je peux donc les extraire dans leurs propres modules utilitaires que vous pouvez voir dans ce dépôt d'exemple.< /P>
< /P>
D'accord, c'était assez simple, juste un copier/coller dans quelques modules. Maintenant réfléchissons à ce que fait mon application. Pour simplifier les choses, disons que je me concentre sur la visualisation de certaines informations démographiques. Concentrons cela dans un widget. Ce widget pourrait ressembler à ceci:< /P>
< /P>
define([
'tdojo/_base/declare',
'tdojo/_base/lang',
'tdojo/topic',
'tdijit/_WidgetBase',
'tdijit/_TemplatedMixin',
'tesri/layers/FeatureLayer',
'tdojo/text!./widget.html'
't], function(
declare, lang, topic,
't_WidgetBase, _TemplatedMixin,
'tFeatureLayer,
templateString
) {
v ar hitch = lang.hitch;
v return declare([_WidgetBase, _TemplatedMixin], {
templateString: templateString,
baseClass: 'population-info',
pos tCreate: function() {
v ar layerOptions = this.get('layerOptions');
v ar renderer = this.get('renderer');
v ar url = this.get('url');
v ar map = this.get('map');
v ar layer = new FeatureLayer(url, layerOptions);
layer.setRenderer(renderer);
map.addLayer(layer);
th is.set('layer', layer);
th is.own( // faire cela afin que le widget puisse libérer la mémoire s'il est détruit
topic.subscribe('blend-select-update', hitch(this, 'updateBlendMode'))
);
},
updateBlendMode: function(mode) {
console.log('mettre à jour le mode de fusion avec dojo/topic');
var layer = this.get('layer');
var renderer = this.get('renderer');
renderer.blendMode = mode;
layer.setRenderer(renderer);
layer.refresh();
}
});
});
C'est un widget assez simple. Nous utilisons une méthode postCreate pour configurer les choses. Je parle du cycle de vie dijit dans cette vidéo. C'est dans cette méthode que je configure ma couche et assigne le renderer qui a été passé dans les options. Il y a un template HTML simple qui est juste une copie du HTML de l'exemple avec une description des données. J'utilise aussi dojo/topic, dont j'ai parlé ici. Je vais démontrer quelques méthodes différentes de communication entre widgets, une utilisant dojo/topic et une autre utilisant dojo/Evented.
Ce module a aussi une méthode updateBlendMode qui gère simplement l'abonnement dojo/topic et met à jour le blendMode. Cela signifie que n'importe quel module dans votre application peut publier une mise à jour du blendMode ou pour toute autre raison. À mon avis, c'est la méthode idéale de communication entre widgets, car les widgets individuels n'ont pas besoin d'être conscients les uns des autres.
L'autre chose que l'exemple avait était un menu select qui vous permettait de mettre à jour le blendMode. Encore une fois, je pense que c'est le candidat parfait pour un autre widget. Le code pour ce widget pourrait ressembler à ceci :
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);
}
}
});
})
Ce widget utilise dojo/Evented et dojo/topic. Vous remarquerez que le widget lui-même étend dojo/Evented en utilisant dojo/_base/declare. Cela vous permet d'utiliser this.emit() pour émettre des événements depuis votre widget. Ce widget a le menu select pour les blendModes ainsi qu'une case à cocher pour définir si vous voulez émettre un événement avec le nouveau blendMode ou publier le blendMode via dojo/topic.
Le template ressemble à ceci :
<div>
<div>
<input type="checkbox" data-dojo-attach-point="cboxNode"> Utiliser 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">surimpression</option>
<option value="normal">nornal</option>
<option value="multiply">multiply</option>
<option value="screen">>screen</option>
< option value= "darken "> ; assombrir & lt / option & gt ;
& lt option value= "lighten "& gt ; éclaircir & lt / option & gt ;
& lt option value= "color-dodge "& gt ; color-dodge & lt / option & gt ;
& lt option value= "color-burn "& gt ; color-burn & lt / option & gt ;
& lt option value= "hard-light "& gt ; hard-light & lt / option & gt ;
& lt option value= "soft-light "& gt ; soft-light & lt / option & gt ;
& lt option value= "difference "& gt ; difference & lt / option & gt ;
& lt option value= "exclusion "& gt ; exclusion & lt / option & gt ;
& lt option value= "hue "& gt ; teinte (hue) & lt / option & gt ;
& lt option value= "saturation "& gt ; saturation (saturation) & lt / option & gt ;
& lt option value= "color "& gt ; couleur (color) & lt / option & gt ;
& lt option value= "luminosity "& gt ; luminosité (luminosity) & lt / option & gt ;
& lt / select >
</div>
D'accord, donc nous avons deux widgets, un qui crée la FeatureLayer avec le blendRenderer et un autre widget qui met à jour le blendMode du renderer.
Connectons cela dans un module main.js pour démarrer les choses.
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');
// une façon de mettre à jour le blendMode
blendSelectionWidget.on('blend-select-update', function(mode) {
console.log('mettre à jour le mode de fusion avec events');
renderer.blendMode = mode;
populationWidget.get('layer').setRenderer(renderer);
populationWidget.get('layer').refresh();
});
});
});
D'accord, donc le but du fichier main est de créer la carte et charger les widgets, en passant les options requises tout en utilisant aussi les utils que nous avons créés plus tôt. Vous remarquerez également que j'utilise blendSelectionWidget.on('blend-select-update', function(mode){}) pour écouter quand un événement est émis et mettre à jour manuellement le renderer pour la couche dans le widget population. C'est une autre façon de faire la communication entre widgets, où vous pouvez le faire dans un fichier principal ou peut-être aurez-vous un WidgetController qui gère toute la communication des widgets de votre application, cela dépend vraiment de ce qui vous plaît. Normalement, je découperais même la création de la carte en son propre widget également.
Je voudrais aussi souligner la configuration dojoConfig pour cette application.
var dojoConfig = {
isDebug: true,
deps: ['app/main'],
packages: [{
name: 'app',
location: location.pathname.replace(new RegExp(/\/[^\/]+$/), '') + 'app'
}]
};C'est très simple. En procédant ainsi, j'ai défini un package app et tous mes modules vivent dans ce package. Cela signifie que je n'ai pas besoin de créer un package widgets ou un package utils, je peux simplement les référencer comme app/widgets et app/utils. Remarquez aussi la propriété deps. Cela dira au chargeur Dojo de charger ce module lorsque Dojo est chargé. Vous pouvez consulter la documentation ici. Cela rend mon fichier index.html actuel vraiment 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>Démo d'application modulaire</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>Vous pouvez lire sur d'autres méthodes pour initialiser votre application ici.<\/P><\/P>Prenez-le morceau par morceau<\/H2>Vous pouvez trouver l'application modulaire complète dans ce dépôt github. J'espère que cela vous donnera un peu plus de compréhension sur la façon dont vous pourriez diviser une application en modules et comment vous pouvez commencer à penser à la modularité dans votre propre développement. J'aime généralement séparer les fonctionnalités d'une application en leurs propres widgets, que ce soit l'édition, la recherche, le géocodage ou même simplement ajouter un comportement.<\/P><\/P>Vous pouvez voir d'autres exemples de modularité dans des projets sur lesquels j'ai travaillé dans le passé dans ce starter-kit, qui est entièrement configurable, ou mes dernières expériences dans ce générateur yeoman, et application exemple. Une autre application très modulaire est quelque chose comme la application cmv.<\/P><\/P>Encore une fois, ceci est simplement mon avis sur la façon dont je pense que vous pourriez diviser une application en modules plus petits qui la rendent non seulement plus facile à maintenir au fil du temps mais aussi à ajouter facilement de nouvelles fonctionnalités.<\/P><\/P>Pour plus d'astuces et conseils geodev, consultez mon blog.<\/P><\/BODY><\/HTML>