ArcGIS API for JavaScript ArcGIS API for JavaScript Programming Patterns and API Fundamentals Programming Patterns and API Fundamentals Kelly Hutchins | René Rubalcava | slides: @kellyhutchins @odoenet https://git.io/fhNcV
ArcGIS API for JavaScriptArcGIS API for JavaScriptProgramming Patterns and API FundamentalsProgramming Patterns and API Fundamentals
Kelly Hutchins | René Rubalcava
|
slides:
@kellyhutchins @odoenet
https://git.io/fhNcV
What do I get with the 4x JSAPI?What do I get with the 4x JSAPI?Simpli�ed and consistent APIWrite apps in ES6 or TypeScriptModern browser support (IE11+)Supported in 30+ locales
What are my options?What are my options?Needs?Resources?Time?Customizations?
Why start from scratch?Why start from scratch?App startersWidgets
Widgets!Widgets!We'll look at a few widgets
Widgets!Widgets!We'll look at a few ~30 Widgets out of the box
widgets
Widgets!Widgets!We'll look at a few ~30 Widgets out of the boxWidgets help make great apps
widgets
Widgets!Widgets!We'll look at a few ~30 Widgets out of the boxWidgets help make great appsLess code for you to write
widgets
Widgets!Widgets!We'll look at a few ~30 Widgets out of the boxWidgets help make great appsLess code for you to writeDesigned with responsive apps in mind
widgets
Widgets - ExpandWidgets - Expand
IconsGroupMode
Clickable button to open container
Widgets - Use Portal ContentWidgets - Use Portal Content
Basemap GallerySearch
Widgets - PopupWidgets - PopupMenu actionsDock
Widgets - Popup TemplateWidgets - Popup TemplateDe�ne �elds, charts, custom html content
PromisesArcade
Using a function
Widgets - Author popup in onlineWidgets - Author popup in online
Layer.fromPortalItemWeb Map or Web Scene
App Demo
Widgets - FeatureWidgets - FeatureDisplay popup template contentHover
Widgets - ArchitectureWidgets - ArchitectureView + View Model
View ModelsView ModelsCustom ViewUse the view model
Additional Examples
GeocodingGeocodingconstconst locator locator == newnew LocatorLocator(({{ url url:: url url }}));;
locatorlocator..addressToLocationsaddressToLocations(({{
address address:: {{
"singleLine""singleLine":: "380 New York St, Redlands, CA 92373""380 New York St, Redlands, CA 92373"
}}
}}));;
locatorlocator..locationToAddresslocationToAddress(({{ location location:: point point }}));;
GeocodingGeocodingconstconst searchVM searchVM == newnew SearchVMSearchVM(());;
searchVMsearchVM..searchsearch(("380 New York St, Redlands, CA 92373""380 New York St, Redlands, CA 92373"));;
searchVMsearchVM..searchsearch((locationlocation));;
GeocodingGeocodingconstconst portal portal == newnew PortalPortal((......));;
awaitawait portal portal..loadload(());;
portalportal..helperServiceshelperServices..geocodegeocode..mapmap((geocoderServicegeocoderService =>=> {{
// objects with details on// objects with details on
// geocode services for your portal// geocode services for your portal
}}));;
Widgets - StylingWidgets - StylingAvailable Themes
Theme TestingA PEN BY kellyhutchins
Run Pen
Widgets - StylingWidgets - StylingCSS Extension languageSASSTheme Utility
Map and ViewMap and View
Map and ViewMap and Viewconstconst map map == newnew MapMap(({{
basemap basemap:: "topo""topo"
}}));;
constconst mView mView == newnew MapViewMapView(({{
map map:: map map,,
container container:: "viewDiv""viewDiv"
}}));;
constconst sView sView == newnew SceneViewSceneView(({{
map map:: map map,,
container container:: "viewDiv""viewDiv"
}}));;
Basemaps and GroundBasemaps and GroundConvenience Strings
constconst map map == newnew MapMap(({{
/*/*
streets, satellite, hybrid, terrain, topo, gray, streets, satellite, hybrid, terrain, topo, gray,
dark-gray, oceans, national-geographic, osm, dark-gray, oceans, national-geographic, osm,
dark-gray-vector, gray-vector, streets-vector, topo-vector, dark-gray-vector, gray-vector, streets-vector, topo-vector,
streets-night-vector, streets-relief-vector, streets-navigation-vector streets-night-vector, streets-relief-vector, streets-navigation-vector
*/ */
basemap basemap:: "streets""streets"
/*/*
world-elevation world-elevation
*/ */
ground ground:: "world-elevation""world-elevation"
}}));;
Basemaps and GroundBasemaps and Groundconstconst map map == newnew MapMap(({{
basemap basemap:: {{
// Layers drawn at the bottom// Layers drawn at the bottom
baseLayers baseLayers:: [[
newnew TileLayerTileLayer(({{ url url:: baselayer baselayer }}))
]],,
// Layers drawn on top// Layers drawn on top
referenceLayers referenceLayers:: [[
newnew TileLayerTileLayer(({{ url url:: refUrl refUrl }}))
]],,
}},,
ground ground:: {{
layers layers:: [[
newnew ElevationLayerElevationLayer(({{ url url:: elevationUrl elevationUrl }}))
]]
Basemap and GroundBasemap and Ground
VT BasemapsA PEN BY odoe
Run Pen
CollectionsCollections
CollectionA PEN BY odoe
Run Pen
esri/core/Collection
Working with AccessorWorking with AccessorObjects are have properties that can be:
read and setor read-onlyconstructor argumentswatchable
Accessor - property accessAccessor - property access
layerlayer..opacity opacity == 0.50.5;;
layerlayer..title title == "My test layer""My test layer";;
// setting multiple values// setting multiple values
layerlayer..setset(({{
opacity opacity:: 0.50.5,,
title title:: "My test layer""My test layer"
}}));;
// accessing the value of a deep property// accessing the value of a deep property
viewview..getget(("map.basemap.title""map.basemap.title"));;
viewview..setset(("map.basemap.title""map.basemap.title",, "new title""new title"));;
Accessor - property watchingAccessor - property watching
mapViewmapView..watchwatch(("scale""scale",, ((newValuenewValue,, oldValue oldValue,, property property,, target target)) =>=> {{
consoleconsole..loglog((`scale changed: `scale changed: ${${newValuenewValue}}`̀));;
}}));;
mapViewmapView..watchwatch(("map.basemap.title""map.basemap.title",, ((newValuenewValue,, oldValue oldValue,, property property,, target target)) =>=> {{
consoleconsole..loglog((`new basemap title: `new basemap title: ${${newValuenewValue}}`̀));;
}}));;
mapViewmapView..watchwatch(("ready, stationary""ready, stationary",, ((newValuenewValue,, oldValue oldValue,, property property,, target target)) =>=> {{
consoleconsole..loglog((`property `property ${${propertyproperty}}: : ${${newValuenewValue}}`̀));;
}}));;
watchUtilswatchUtils..whenTruewhenTrue((viewview,, "stationary""stationary",, (()) =>=> {{
watchUtils
Accessor - autocasting and single constructorAccessor - autocasting and single constructor
// 4.x// 4.x
{{
type type:: "simple-marker""simple-marker",,
style style:: 'square''square',,
color color:: 'red''red',,
size size:: 1010,,
outline outline:: {{
color color:: 'rgba(255, 255, 255, 0.5)''rgba(255, 255, 255, 0.5)'
width width:: 44
}}
}}));;
// 3.x// 3.x
newnew SimpleMarkerSymbolSimpleMarkerSymbol((SimpleMarkerSymbolSimpleMarkerSymbol..STYLE_SQUARESTYLE_SQUARE,, 1010,,
newnew SimpleLineSymbolSimpleLineSymbol((SimpleLineSymbolSimpleLineSymbol..STYLE_SOLIDSTYLE_SOLID,,
PromisesPromises
PromisesPromisesAll asynchronous methods return a promise, no more The basic pattern looks like this:
events
layerlayer..queryFeaturesqueryFeatures((queryquery))..thenthen((handleResulthandleResult))..catchcatch((handleErrorhandleError));;
Promises with async/awaitPromises with async/awaitwork with native promises
constconst doQuerydoQuery == asyncasync ((queryquery)) =>=> {{
constconst results results == awaitawait layer layer..queryFeaturesqueryFeatures((queryquery));;
constconst transformedResults transformedResults == results results..mapmap((transformDatatransformData));;
returnreturn transformedResults transformedResults;;
}}
PromisesPromisesLoad resourcesAsychronously initialized Layer, WebMap, WebScene, View
constconst map map == newnew MapMap(({{......}}))
view view == newnew SceneViewSceneView(({{
map map:: map map,,
//...//...
}}));;
viewview..whenwhen(((()) =>=> {{
// the view is ready to go// the view is ready to go
}}));;
PromisesPromisesviewview..whenwhen(((()) =>=> {{
returnreturn view view..whenLayerViewwhenLayerView((mapmap..findLayerByIdfindLayerById(("awesomeLayer""awesomeLayer"))));;
}}))
..thenthen((layerViewlayerView =>=> {{
returnreturn watchUtils watchUtils..whenFalseOncewhenFalseOnce((layerViewlayerView,, "updating""updating"));;
}}))
..thenthen((resultresult =>=> {{
constconst layerView layerView == result result..targettarget;;
returnreturn layerView layerView..queryFeaturesqueryFeatures(());;
}}))
..thenthen((doSomethingWithFeaturesdoSomethingWithFeatures))
..catchcatch((errorHandlererrorHandler));;
API sample
async/awaitasync/awaitconstconst initinit == asyncasync ((doSomethingWithFeaturesdoSomethingWithFeatures)) =>=> {{
awaitawait view view..whenwhen(());;
constconst layerView layerView == awaitawait view view..whenLayerViewwhenLayerView((mapmap..findLayerByIdfindLayerById(("awesomeLayer""awesomeLayer"))));;
constconst {{ target target asas layerView layerView }} == awaitawait watchUtils watchUtils..whenFalseOncewhenFalseOnce((layerViewlayerView,, "updating""updating"));;
constconst features features == awaitawait layerView layerView..queryFeaturesqueryFeatures(());;
doSomethingWithFeaturesdoSomethingWithFeatures((featuresfeatures));;
}};;
trytry {{
initinit(());;
}}
catchcatch((errorerror)) {{
errorHandlererrorHandler((errorerror));;
}}
PatternsPatterns
Interactivity with view eventsInteractivity with view eventsUse view events to interact with the view
You can stop the propagation of the event to prevent the defaultbehavior
List of events
viewview..onon(("drag""drag",, eventevent =>=> {{
// user won't be able to drag// user won't be able to drag
event event..stopPropagationstopPropagation(());;
}}))
Interactivity with view eventsInteractivity with view eventsAccess the features on click
viewview..onon(("click""click",, (({{ x x,, y y }})) =>=> {{
constconst screenPoint screenPoint == {{xx,, y y}};;
view view..hitTesthitTest((screenPointscreenPoint))
..thenthen((responseresponse =>=> {{
// do something with the result graphic// do something with the result graphic
constconst graphic graphic == response response..resultsresults[[00]]..graphicgraphic;;
}}));;
}}));;
API Sample
goTo() with ViewgoTo() with ViewSets the view to a given target.
Navigate to a geometry/feature/locationAPI Sample
LoadablesLoadablesbrings better control, and scheduling of loading resources.extension of esri/core/Promisein 3.x, instanciating a layer loads it. in 4.0, it's an explicit callthe views automatically loads the map and its layers
LoadablesLoadablesWebMap / WebScene need to load:
the portal itemthe layer modulethe layer's item
MapView / SceneView need to load:the mapthe layers
//In a single page application, get a feature from a FeatureLayer from a WebMap without displaying i//In a single page application, get a feature from a FeatureLayer from a WebMap without displaying i
constconst webmap webmap == newnew WebMapWebMap(({{
portalItem portalItem:: {{
id id:: 'affa021c51944b5694132b2d61fe1057''affa021c51944b5694132b2d61fe1057'
}}
}}));;
webmap webmap..loadload(())
..thenthen(((()) =>=> {{
returnreturn webmap webmap..getLayergetLayer(('myFeatureLayerId''myFeatureLayerId'))..loadload(());;
}}))
..thenthen((featureLayerfeatureLayer =>=> {{
returnreturn featureLayer featureLayer..queryFeaturesqueryFeatures(({{
where where:: 'OBJECTID = 1''OBJECTID = 1'
}}));;
Zoom or ScaleZoom or Scale
Zoom = LOD (Level of Details)Not all LODs are created equal
constconst view view == newnew MapViewMapView(({{
container container:: "viewDiv""viewDiv",,
map map:: map map,,
center center:: [[--116.5116.5,, 33.8033.80]],,
zoom zoom:: 1414 // what does that really mean?// what does that really mean?
}}));;
Zoom is not ScaleZoom is not Scale
Scale is portableScale has meaningWe still snap to closest LOD/zoom
constconst view view == newnew MapViewMapView(({{
container container:: "viewDiv""viewDiv",,
map map:: map map,,
center center:: [[--116.5116.5,, 33.8033.80]],,
scale scale:: 5000050000 // I know what that means!// I know what that means!
}}));;
WebMap is still a MapWebMap is still a Map
Still acts like a regular MapHas some advantages
constconst map map == newnew WebMapWebMap(({{
basemap basemap:: {{ ...... }},,
layers layers:: [[ ...... ]]
}}));;
WebMap is still a MapWebMap is still a Map
Local bookmarksA PEN BY odoe
Run Pen
Sublayer to FeatureLayerSublayer to FeatureLayerYou can extract a FeatureLayer from MapImageLayer Sublayersublayer.createFeatureLayer()Can use capabilities not normally available with Sublayer
Sublayer to FeatureLayerSublayer to FeatureLayer
createFeatureLayerA PEN BY odoe
Run Pen
createQuerycreateQueryWhen you can do layer.createQuery()query object will already have the layers �lters and layerde�nitionsmore consistent
Use new Query() when you don't want prede�ned �lters to beapplied
createQuerycreateQuery
createQueryA PEN BY odoe
Run Pen
MapImageLayerMapImageLayerIf you want to modify Sublayers, do it after you load the layerDe�ning them upfront overrides the defaults
May not be what you want
MapImageLayerMapImageLayer
MapImageLayer - Load SublayersA PEN BY odoe
Run Pen
LayerViewsLayerViewsRenders the LayerWhen is it done though?
hotly debated topic!When can you actually use it!!Behavior different with optimized FeatureLayer
LayerViewsLayerViews
LayerView - ReadyA PEN BY odoe
Run Pen