<\/HEAD>
<\/P>
Před časem jsem napsal článek o tom, jak přijmout vaše AMD moduly<\/A>. Objevilo se několik otázek při pohledu na příklady, jak to udělat, s použitím nejlepších praktik a doporučených způsobů práce s moduly. Tak jsem si řekl, že dnes s tím zkusím pomoci.<\/P><\/P>
Vzpomněl jsem si na
příklad z dokumentace<\/A>, který jsem před časem aktualizoval o některé funkce<\/A>.<\/P><\/P>
Chtěl jsem přemýšlet o tom, jak bych to mohl rozdělit do více modulární aplikace pomocí AMD.<\/P>
<\/P>
Takže nejdřív se podívejme na kód, zatím ignorujme HTML.<\/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 \/\/_nastavte parametry blendRendereru\n var blendRendererParams = {\n \/\/_blendMode:"overlay" \/\/_Ve výchozím nastavení používá "source-over", odkomentujte pro zobrazení jiného režimu\n \/\/_Viz: <\/> https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/CanvasRenderingContext2D\/globalCompositeOperation<\/>\n symbol: new SimpleFillSymbol().setOutline(new SimpleLineSymbol().setWidth(0)),\n fields: [\n {\n field: "OWNER_CY",\n label: "Vlastníci obyvatelné",\n color: new Color([0, 0, 255])\n }, {\n field: "RENTER_CY",\n label: "Nájemci obyvatelné",\n color: new Color([255, 0, 0])\n }, {\n field: "VACANT_CY",\n label: "Volné",\n  .& nbsp.& nbsp;color: new Color([0, 255, 0])\n . & nbsp.& nbsp;} \n& nbsp;. & nbsp;} \n& nbsp;. & nbsp;} \n& nbsp;. & nbsp;} \n& nbsp;. & nbsp;} \n& nbsp;. & nbsp;} \n& nbsp;. & nbsp;} \n& nbsp;. & nbsp;} \n& nbsp;. & nbsp;} \n& nbsp;. & nbsp;} \n& nbsp;. & nbsp;} \n& nbsp;. & nbsp;} \n& nbsp;. & nbsp;} \n& nbsp;. & nbsp;} \n& nbsp;. & nbsp;} \n& nbsp;. & nbsp;} \n& nbsp;. & nbsp;} \nsymbol: new SimpleFillSymbol().setOutline(new SimpleLineSymbol().setWidth(0)),
fields: [
{
field: "OWNER_CY",
label: "Vlastníci obyvatelné",
color: new Color([0, 0, 255])
}, {
field: "RENTER_CY",
label: "Nájemci obyvatelné",
color: new Color([255, 0, 0])
}, {
field: "VACANT_CY",
label: "Volné",
color: new Color([0, 255, 0])
}
],
opacityStops: [
{
value: 0.1,
opacity: 0
},
{
value: 1,
opacity: 0.7
}
],
ormalizationField: "TOTHU_CY"
};
//Vytvořte PopupTemplate pro zobrazení demografických informací
var template = new PopupTemplate({
'title': 'Stav bydlení podle sčítacích bloků',
'tfieldInfos': [
'tfieldName': 'OWNER_CY',
'tlabel': 'Počet domů obývaných vlastníky',
'tvisible': true,
'tformat': {
'tplaces': 0,
'tdigitSeparator': true
}
't}, {
'tfieldName': 'RENTER_CY',
'tlabel': 'Počet domů obývaných nájemci',
'tvisible': true,
'tformat': {
'tplaces': 0,
'tdigitSeparator': true
}
't}, {
'tfieldName': 'VACANT_CY',
'tlabel': 'Počet volných domů',
'tvisible': true,
'tformat': {
'tplaces': 0,
'tdigitSeparator': true
}
't}, {
'tfieldName': 'TOTHU_CY',
'tlabel': 'Celkový počet bytových jednotek',
'tvisible': true,
'tformat': {
'tplaces': 0,
'tdigitSeparator': true
}
't}
't]
't});
tvar layerUrl = "http://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/Blocks%20near%20Wilshire%20enriched%20with%20Key%20Facts/FeatureServer/0";
vvar 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();
});
});
Toto není špatné pro malou aplikaci, ale rád přemýšlím o jak mohu svou aplikaci škálovat?< /EM> Kde ji mohu trochu rozdělit a co přesně se děje?< /EM>
< /P>
Varování - Toto je jen můj názor na to, jak byste mohli tuto aplikaci modularizovat, jiní mohou mít odlišné názory.< /EM>
< /P>
Krok za krokem
Nejdříve tato aplikace využívá BlendRenderer, o kterém jsem diskutoval zde. To mě přimělo přemýšlet, že bych mohl pravděpodobně oddělit veškerou tuto funkcionalitu. Také vytvářím PopupTemplate a BlendRenderer přímo v tomto jediném souboru. Když vidím takové věci – jednoduché parametry – mám tendenci je umisťovat do utilitních modulů, což znamená, že je lze snadno znovu použít v několika modulech a jsou součástí společného jádra mé aplikace.< /P>
< /P>
Mohu je tedy rozdělit do vlastních utilitních modulů, které můžete vidět v tomto ukázkovém repozitáři< /A>.< /P>< /P>
Dobře, to bylo docela jednoduché – jen kopírování/vložení do několika modulů. Teď přemýšlejme o tom, co moje aplikace dělá. Pro jednoduchost řekněme, že se zaměřuji na vizualizaci některých populačních informací. Zaměřme to do widgetu. Tento widget by mohl vypadat nějak takto:< /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', postCreate : function() { var layerOptions = this.get('layerOptions'); var renderer = this.get('renderer'); var url = this.get('url'); var map = this.get('map'); var layer = new FeatureLayer(url , layerOptions); layer.setRenderer(renderer); map.addLayer(layer); this.set('layer', layer); this.own( // udělejte to takto aby widget mohl uvolnit paměť pokud bude zničen
topic.subscribe('blend-select-update', hitch(this, 'updateBlendMode'))
);
},
updateBlendMode: function(mode) {
console.log('aktualizace režimu míchání pomocí dojo/topic');
var layer = this.get('layer');
var renderer = this.get('renderer');
renderer.blendMode = mode;
layer.setRenderer(renderer);
layer.refresh();
}
});
});Toto je docela jednoduchý widget. Používáme metodu postCreate k nastavení věcí.
V tomto videu mluvím o životním cyklu dijitu. Tato metoda je místo, kde nastavím svou vrstvu a přiřadím renderer, který byl předán v možnostech. Existuje jednoduchá HTML šablona, která je jen kopií HTML ze vzoru, který obsahuje popis dat. Také používám dojo/topic, o kterém jsem mluvil zde. Ukážu několik různých metod komunikace widgetů, jednu pomocí dojo/topic a druhou pomocí dojo/Evented.
Tento modul má také metodu updateBlendMode, která jednoduše zpracovává přihlášení k odběru dojo/topic a aktualizuje blendMode. To znamená, že jakýkoli modul ve vaší aplikaci může publikovat aktualizaci blendMode nebo z jakéhokoli důvodu. Podle mého názoru je to ideální metoda komunikace widgetů, protože jednotlivé widgety nemusí být navzájem informovány.
Druhou věcí, kterou vzor obsahoval, byla výběrová nabídka, která vám umožnila aktualizovat blendMode. Opět si myslím, že je to perfektní kandidát na další widget. Kód pro tento widget by mohl vypadat nějak takto:
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);
}
}
});
})
Tento widget používá dojo/Evented a dojo/topic. Všimnete si, že samotný widget rozšiřuje dojo/Evented pomocí dojo/_base/declare. To vám umožňuje použít this.emit() k vysílání událostí z vašeho widgetu. Tento widget má výběrové menu pro blendModes a také zaškrtávací políčko pro nastavení, zda chcete vyslat událost s novým blendMode nebo publikovat blendMode přes dojo/topic.
Šablona pro tento vypadá takto:
<div>
<div>
<input type="checkbox" data-dojo-attach-point="cboxNode"> Použít 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">kopie</option>
<option value="xor">xor</option>
<option value="overlay">>overlay</option>
  < option value= "normal"> normal< / option> ;
& lt option value= "multiply"& gt multiply< / option> ;
& lt option value= "screen"& gt screen< / option> ;
& lt option value= "darken"& gt darken< / option> ;
& lt option value= "lighten"& gt lighten< / option> ;
& lt option value= "color-dodge"& gt color-dodge< / option> ;
& lt option value= "color-burn"& gt color-burn< / option> ;
& lt option value= "hard-light"& gt hard-light< / option> ;
& lt option value= "soft-light"& gt soft-light< / option> ;
& lt option value= "difference"& gt difference< / option> ;
& lt option value= "exclusion"& gt exclusion< / option> ;
& lt option value= "hue"& gt hue< / option> ;
& lt option value= "saturation"& gt saturation< / option> ;
& lt option value= "color"& gt color< / option> ;
& lt option value= "luminosity"& gt luminosity< / option> ;
& lt / select> ; \\n</div>
Dobře, máme tedy dva widgety, jeden vytváří FeatureLayer s blendRenderer a druhý widget aktualizuje blendMode rendereru.
Pojďme to propojit v modulu main.js, abychom začali.
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');
// jeden způsob jak aktualizovat blendMode
blendSelectionWidget.on('blend-select-update', function(mode) {
console.log('aktualizace režimu míchání pomocí událostí');
renderer.blendMode = mode;
populationWidget.get('layer').setRenderer(renderer);
populationWidget.get('layer').refresh();
});
});
});
Dobře, účelem souboru main je vytvořit mapu a načíst widgety, předávající požadované možnosti a zároveň používat utils, které jsme vytvořili dříve. Také si všimnete, že používám blendSelectionWidget.on('blend-select-update', function(mode){}), abych poslouchal na vysílání události a ručně aktualizoval renderer pro vrstvu v population widgetu. Toto je další způsob komunikace mezi widgety, kdy to můžete dělat v hlavním souboru nebo možná budete mít WidgetController, který zpracovává veškerou komunikaci vašich aplikací mezi widgety, opravdu záleží na tom, co vám vyhovuje. Obvykle bych dokonce oddělil vytvoření mapy jako vlastní widget.
Chtěl bych také upozornit na dojoConfig pro tuto aplikaci.
var dojoConfig = {
isDebug: true,
deps: ['app/main'],
packages: [{
name: 'app',
location: location.pathname.replace(new RegExp(/\/[^\/]+$/), '') + 'app'
}]
};To je velmi jednoduché. Tímto způsobem jsem definoval balíček app a všechny mé moduly žijí v tomto balíčku. To znamená, že nemusím vytvářet balíček widgets nebo balíček utils, mohu je jednoduše odkazovat jako app/widgets a app/utils. Všimněte si také vlastnosti deps. Ta řekne Dojo loaderu načíst tento modul když je Dojo načteno. Dokumentaci můžete zkontrolovat zde. To dělá můj aktuální soubor index.html opravdu jednoduchým.<\/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>Modular App Demo</title>
<link rel="stylesheet" href="http://js.arcgis.com/3.14/esri/css/esri.css">
<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>
</body>
</html><\/PRE>Můžete si přečíst o dalších metodách inicializace vaší aplikace zde.<\/P><\/P>Vezměte to kousek po kousku<\/H2>Celou modularizovanou aplikaci najdete v tomto github repozitáři. Doufám, že to poskytne trochu více náhledu do toho, jak můžete rozdělit aplikaci na moduly a jak můžete začít přemýšlet o modularitě ve svém vlastním vývoji. Mám tendenci rozdělovat funkce aplikace do jejich vlastních widgetů, ať už jde o editaci, vyhledávání, geokódování nebo jen přidání nějakého chování.<\/P><\/P>Můžete vidět další příklady modularity v projektech, na kterých jsem pracoval v minulosti, v tomto starter-kit, který je plně konfigurovatelný, nebo mé poslední experimenty v tomto yeoman generatoru, a ukázkové aplikaci. Další aplikací, která je velmi modulární, je například cmv app.<\/P><\/P>Opět, toto je pouze můj názor na to, jak si myslím, že byste mohli rozdělit aplikaci na menší moduly, které nejen usnadní údržbu v průběhu času, ale také snadné přidávání nové funkčnosti.<\/P><\/P>Pro více tipů a triků z geodev sledujte můj blog.<\/P><\/BODY><\/HTML>