|
BLOG
|
You write badass apps. Your users are happy, you're happy, everything is peachy. Someone says they want some new features. You're like rad dude, I got this. You start writing and refactoring, adding new modules, tweaking a couple of others and somewhere along the line your entire application comes crashing down. Something broke and you've made so many changes you can't tell what broke where. You are so screwed. Maybe you've never been in such a horrible scenario, but I bet you've had to do some ugly stuff in your code to make things work because you needed to work around some weird issue that popped up while adding new features. The shame As developers, we all feel like we should do it. Maybe we start a new project with enthusiasm doing it. We should be testing our code. I'm guilty of starting a project with lots of tests, but somewhere along the way I don't keep it up and I feel horrible about it. But it doesn't have to be that way. Unit tests are appropriate for large projects, trust me. Maybe it's TDD or BDD or you just write tests after the fact. However you do it, you will only thank yourself down the road as a project grows. Here is a repo on testing from previous Esri Dev Summits and a cool Karma example. I like Karma, it's a cool test runner, but recently I've revisited TheIntern for testing. Intern is really cool and you can read some really great posts on SitePen about it. Check out the one on InternRecorder which is awesome. Simplify your flow The only drawback on Intern from something like Karma that I could never get to work is have it run my tests in the terminal on every file change, but I've worked around that using live-reload in my development toolkit. Combine live-reload with Grunt and a few plugins and every time I make a change to my app, my app reloads in the browser and my test page also reloads so I can instantly see if I broke something. Green is a beautiful color. Now I'm not going to get into the whole red/green/refactor bit of tdd or unit testing. There are plenty of tutorials out there to help you out with that. There's also a whole world out there on spies and mocks for testing. I'll just say that I like SinonJS for this purpose. I know it and I'm used to it, but others may have different preferences. So what's a sample test look like? Like this maybe. define(function(require) {
var registerSuite = require('intern!object');
var expect = require('intern/chai!expect');
var View = require('app/components/map/WebMapView');
var topic = require('dojo/topic');
var config = require('app/config');
var utils = require('esri/arcgis/utils');
var chai = require('intern/chai!');
var sinon = require('sinon');
var sinonChai = require('sinon-chai');
chai.use(sinonChai);
var mapView;
registerSuite({
name: 'components: WebMapView',
setup: function() {
// set up test here
sinon.stub(utils, 'createMap').returns({
then: function(){}
});
},
beforeEach: function() {
// run before
},
afterEach: function() {
// run after
if (mapView && mapView.destroy) {
mapView.destroy();
}
},
teardown: function() {
// destroy widget
utils.createMap.restore();
},
'Component is valid': function() {
expect(View).to.not.be.undefined;
},
'View publishes a valid map given a webmapid': function() {
mapView = new View({
webmapid: config.webmap.webmapid
});
expect(mapView.webmapid).to.equal(config.webmap.webmapid);
}
});
}); All that noise basically boils down to this. 'View publishes a valid map given a webmapid': function() {
mapView = new View({
webmapid: config.webmap.webmapid
});
expect(mapView.webmapid).to.equal(config.webmap.webmapid);
} I'm basically saying that when I create this component, I expect it to have a webmap with the webmapid I gave the constructor. That's it. The implementation is left up to me, but the test is only concerned about the end result. Tells a story Tests are great documentation. I can't tell you how many times I've pulled up the tests for a library or framework to get a better idea of how to use it if I'm confused about something in the docs. They can be incredibly valuable resources. They don't replace documentation, but are fantastic companions. There are tons of tutorials out there on JavaScript unit testing, you can read up on. A lot are based on QUnit or Jasmine, but like I said, I've grown to really like Intern. Intern integrates well with Sauce Labs, but if you don't need the full platform, you can use the local selenium driver and chromedriver. And if using Grunt, I can use grunt-run to run the driver before executing the functional tests. run: {
options: {
wait: false
},
webdriver: {
cmd: 'java',
args: [
'-jar',
'tests/lib/selenium-server-standalone-2.46.0.jar',
'-Dwebdriver.chrome.driver=node_modules/chromedriver/bin/chromedriver'
]
}
} But these are probably details better left for a future blog post. Write some tests I'm currently working on a Yeoman generator to help simplify this for ArcGIS JS API development, which also includes testing. It's not quite done yet, but I'm hoping to finish it up soon. Now I'm not saying you have to test every little thing, but it is a good idea to test the behavior of your app. Two things to consider: Writing the tests before - testing helps to guide your development. Writing the tests after - can be done, writing the app can help you get the idea down, but you run the risk of writing tests just to pass. Remember I said that tests aren't concerned with the implementations of your code. You test that you pass in a and you expect result b. That's it. So it's a good way to quickly get down what you are trying to accomplish. If you write the tests after the fact, you run the risk of writing the tests to fit your code, which could be broken. BUT, if you rerun these tests as your write more code, you'll at least know if your broke something that worked earlier. There are lots of resources on JavaScript testing out there. Here are a couple I can think of . Introduction To JavaScript Unit Testing JavaScript Testing Recipes Intern Tutorial esri jsapi Here is a demo project that includes testing and grunt tooling to help you out. For more geodev tips and tricks, check out my blog.
... View more
09-02-2015
11:19 AM
|
2
|
0
|
1783
|
|
POST
|
You are having scope and order of execution issues. To do what you want, you need to do something like this. var pPoly;
function saySomething() {
console.log(pPoly);
}
featureLayer.on("selection-complete", function (result) {
pPoly = result.features[0].geometry;
saySomething();
}); dojo/on does not return a Promise, so you cannot use then with it. I'd recommend you look over this blog post Take your JavaScript Up a Notch And in particular look at closures.
... View more
09-02-2015
08:07 AM
|
2
|
0
|
527
|
|
POST
|
FeatureLayers work in 3D right now, but they just don't have the query functions built in yet. JS Bin - Collaborative JavaScript Debugging
... View more
08-28-2015
08:06 AM
|
1
|
0
|
1972
|
|
BLOG
|
r.js will get you about 75%+ of the way there depending on what you are using in your app. Main issue with r.js is that it attempts to execute loader plugins during compilation and will fail on most instances that try to access the DOM, since it's run in Node. Dojo gets around this by using plugin helpers during the build process that r.js does not have. So you can get close to a single-file build, but there are still some modules that will need to be lazy-loaded. Then there is i18n, which will always be lazy-loaded unless you include all the locales you would need into your build. The requirejs loader is also missing some methods that Dojo has, mostly for cross-domain loading.
... View more
08-27-2015
11:11 AM
|
0
|
0
|
626
|
|
POST
|
Are you using this clusterfeaturelayer? Each graphic in the cluster (cluster.graphics) should have a clusterCount attribute to let you know how many points make up the cluster. I think even the simple cluster layer has this. If you want the total count of all data in featurelayer, I think _clusterData.length is property you want.
... View more
08-26-2015
04:16 PM
|
0
|
0
|
715
|
|
BLOG
|
Every now and then, someone will ask me about how they should structure their ArcGIS JS API app. In my book, I recommend the following app structure: app/ |-- css/ |-- js/ |-- controllers/ |-- services/ |-- utils/ |-- widgets/ |-- main.js |-- run.js |-- index.html I've used this app structure for a lot of projects and it's worked fine. Typically my run.js looked like this: (function() {
'use strict';
var pathRX = new RegExp(/\/[^\/]+$/)
, locationPath = location.pathname.replace(pathRX, '');
require({
packages: [{
name: 'controllers',
location: locationPath + 'js/controllers'
}, {
name: 'widgets',
location: locationPath + 'js/widgets'
}, {
name: 'utils',
location: locationPath + 'js/utils'
}, {
name: 'services',
location: locationPath + 'js/services'
}, {
name: 'app',
location: locationPath + 'js',
main: 'main'
}]
}, ['app']);
})(); This works out pretty nice. But one thing it doesn't take into consideration is treating the app and all submodules as an app package. This is more concerned with treating my modules as packages when using the CDN. What's the difference? Glad you asked! Let's say you want to build your application. Maybe you're going to utilize the ArcGIS optimizer or esri-slurp, which has a great example here by the way. You're going to want to treat your application as it's own package. What do I mean by that? Well, let's take a look at how you use the ArcGIS JS API. require(['esri/map', 'dojo/declare'], function(Map, declare) {/*cool stuff*/}); In this case, esri is a package and dojo is a package. There are other packages included in the API, such as dgrid, dstore, and diijt. This because when you use the Dojo build system, it knows how to reference the files. So you can naturally bundle your application as it's own package, called app. You can call it Sally if you want, but let's assume app works just fine. So these days, the way I like to structure my app similar to this. index.html dojoConfig.js app/ |-- styles/ |-- models/ |-- services/ |-- utils/ |-- widgets/ |-- main.js |-- app.profile.js |-- package.json |-- config.json Here, I have dojoConfig file that does some basic setup. It could look like this: var dojoConfig = {
async: true,
parseOnLoad: true,
isDebug: true,
deps: ['app/main']
}
}; Ok, so this assumes I'm going to use esri-slurp to download the API and use bower to install other dependencies. If I were using this as a CDN, I would add packages property like this: packages: [{
name: 'app',
location: location.pathname.replace(/\/[^\/]+$/, '') + 'app'
}] That's it. Ok, bear with me a second. What is this app.profile.js nonsense? This file defines some resourceTags for our app package. This basically tells the Dojo build system my package is using AMD. A coworker told me about this, I didn't think I needed it, but the Dojo build system nags you if you don't have it. It looks like this: var profile = (function(){
return {
resourceTags: {
amd: function(filename, mid) {
return /\.js$/.test(filename);
}
}
};
})(); The pacakge.json let's the Dojo build system know where to find the app.profile to use. {
"name": "myapp",
"version": "1.0.0",
"main": "main",
"description": "Demo app.",
"homepage": "",
"dojoBuild": "app.profile.js"
} The config.json is something that i've been using for years in my ArcGIS JS API apps. It's basically settings or what the map looks like or configurations for widgets. I can use this file or call a web service to get this config data. You can see an example of what that might look like here. The rest of the application is pretty basic. Currently my application structure is heavily inspired by ember-cli as it's something I've been using a lot lately. However, if I'm using React for building my UI, I like to use a more Flux oriented structure. index.html dojoConfig.js app/ |-- styles/ |-- stores/ |-- actions/ |-- helpers/ |-- views/ |-- main.js |-- app.profile.js |-- package.json |-- config.json I would also do the same if I were using Angular, where I'd adopt a structure that uses directives instead of views or components. I'm currently working on a Yeoman generator for ArcGIS JS Apps, which you can see a demo app here. It's still pretty experimental, but could be useful to some. The cmv-app has an interesting app structure using configuration base similar to this starterkit I was working on. They key here, whether you like my advice or not, is pick a structure that works for you. You could have your main.js work as the application controller and just have a widget folder with all your UI stuff. Again, as long as it works for you, you're all set. I'll get to a follow-up blog post that talks about the next step here, which is creating a custom build of your application. That should be fun! For more geodev tips and tricks, check out my blog.
... View more
08-26-2015
09:06 AM
|
1
|
3
|
1523
|
|
POST
|
Austin Mulder do you have a basic sample app I can test this with? I'm pretty new to Windows Store Universal Apps and trying to debug this issue. You can email me if you like rrubalcava at odoe.net Thanks!
... View more
08-25-2015
08:45 AM
|
0
|
0
|
1680
|
|
POST
|
There will be some stuff coming up soon that will allow expanded RequireJS support. Dojo is not going anywhere, but the ability to use RequireJS loader will be easier. RequireJS has some limitations when it comes to cross-domain loading of files that Dojo can handle.
... View more
08-24-2015
03:51 PM
|
0
|
0
|
1546
|
|
POST
|
Dojo doesn't yet support this. Right now, it needs to be define(function (require) { require(['esri/map'], function(Map) { //do something with map }); }); This lazy loads the map. That being said, I did see some discussion at one point not long ago in Dojo IRC that this would be implemented, but I think it's going to be in Dojo2 as I don't see commit history for it in Dojo 1.x. As of right now, this only works if you have previously already loaded the module asynchronously somewhere else in your application.
... View more
08-24-2015
03:25 PM
|
0
|
2
|
1546
|
|
BLOG
|
So I've talked a lot about the ArcGIS API for JavaScript 4.0beta1 a lot recently. I've proclaimed my love of Accessors, and I think Promises are groovy. There's a the whole new concept of separating the map and the view. It's chock full of good stuff. It is however, beta. So not everything is 100% and some stuff just isn't quite ready yet. But you're impatient. You like living on the edge. You drink wine from the bottle. You want some stuff to work now! One of the things not in the 4.0beta1 API is editing. If you look at the docs for a FeatureLayer, it has no editing capabilities at the moment. It will, just not yet. That's ok though. You're a developer... you got this. What is editing after all? Editing is nothing more than a capability of a feature service in the ArcGIS REST API. I've told you before, learn to speak rest and everything else just comes together. For this case, let's assume you just want to add features to the service. There's a capability specifically for that. It even has a sample of what the request and response will look like. It can't get much simpler than that. So this sample editor is about as simple as you can get to add features to a Feature Service. var Editor = declare(null, {
constructor: function (params) {
this.map = params.map;
this.layer = params.layer;
},
add: function (graphic) {
var data = graphic.toJSON();
var map = this.map;
var layer = this.layer;
// http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#/Add_Features/02r30000010m000000/
var url = this.layer.url + "/addFeatures";
map.remove(layer);
esriRequest({
url: url,
content: {
features: JSON.stringify([data]),
}
}, {
usePost: true
}).then(function (response) {
map.add(layer);
}).otherwise(function () {
map.add(layer);
});
}
}); That's it. Provide a Map and FeatureLayer and you're all set. Notice that as of right now, you need to remove the layer from the map and then add it again to get the edits to show. Beta! I'm just using this on a shared service, but esri/request should be able to handle authentication for you if you need it. Don't hold me to this right now, I haven't tried it yet. Need to support deletes and updates? Look at the delete features and update endpoints. Or just use applyEdits and manage it how you want. Remember... It's all just REST man! Here is a demo of this module in action. So go on, give it a shot. Beta is beta, but sometimes you just gotta do whatcha gotta do. So go forth and hack away my friends! For more geodev tips and tricks, check out my blog.
... View more
08-19-2015
04:50 PM
|
0
|
0
|
2364
|
|
BLOG
|
A while ago I wrote an article on how to Embrace your AMD modules. A couple of questions popped up on seeing examples of how to do so, using best practices and recommended ways of working with modules. So I thought I would try to help out with that today. What I did was take a sample from the docs that I had updated to add some features a while ago. I wanted to think about how I could break this up into a more modular app using AMD. So first things, first, let's look at the code, ignoring the HTML for now. 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: "Owner Occupied",
color: new Color([0, 0, 255])
}, {
field: "RENTER_CY",
label: "Renter Occupied",
color: new Color([255, 0, 0])
}, {
field: "VACANT_CY",
label: "Vacant",
color: new Color([0, 255, 0])
}
],
opacityStops: [
{
value: 0.1,
opacity: 0
},
{
value: 1,
opacity: 0.7
}
],
normalizationField: "TOTHU_CY"
};
//Create the PopupTemplate to be used to display demographic info
var template = new PopupTemplate({
"title": "Housing Status by Census Block",
"fieldInfos": [
{
"fieldName": "OWNER_CY",
"label": "Number of Owner Occupied Houses",
"visible": true,
"format": {
"places": 0,
"digitSeparator": true
}
}, {
"fieldName": "RENTER_CY",
"label": "Number of Renter Occupied Houses",
"visible": true,
"format": {
"places": 0,
"digitSeparator": true
}
}, {
"fieldName": "VACANT_CY",
"label": "Number of Vacant Houses",
"visible": true,
"format": {
"places": 0,
"digitSeparator": true
}
}, {
"fieldName": "TOTHU_CY",
"label": "Total Housing Units",
"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();
});
}); This isn't so bad for a small app, but I like to think about how can I scale my app? Where can I break it up a bit and what exactly is happening? Warning - This is just my opinion on how you could modularize this app, others may have differing opinions. Step by step First off, this app makes use of the BlendRenderer that I discussed here. That has me thinking I could probably break all that functionality out. I'm also creating a PopupTemplate and the BlendRenderer right in this single file. When I see stuff like this, that is kind of simple parameters type stuff, I have tendency to drop them into utility modules, meaning they can be reused in multiple modules pretty easily and are part of a common core to my application. So I can break them out into their own utility modules which you can see in this sample repo. Ok, that was pretty simple, just a copy/paste into a couple of modules. Now let's think about what my app does. For simplicity sake, let's say I am focused on visualizing some population information. Let's focus this into a widget. This widget could look something like this: define([
'dojo/_base/declare',
'dojo/_base/lang',
'dojo/topic',
'dijit/_WidgetBase',
'dijit/_TemplatedMixin',
'esri/layers/FeatureLayer',
'dojo/text!./widget.html'
], function(
declare, lang, topic,
_WidgetBase, _TemplatedMixin,
FeatureLayer,
templateString
) {
var hitch = lang.hitch;
return declare([_WidgetBase, _TemplatedMixin], {
templateString: templateString,
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( // do this so the widget can clean up memory if it's destroyed
topic.subscribe('blend-select-update', hitch(this, 'updateBlendMode'))
);
},
updateBlendMode: function(mode) {
console.log('update blend mode with dojo/topic');
var layer = this.get('layer');
var renderer = this.get('renderer');
renderer.blendMode = mode;
layer.setRenderer(renderer);
layer.refresh();
}
});
}); This is a pretty simple widget. We use a postCreate method to set stuff up. I talk about the dijit lifecycle in this video. This method is where I set up my layer and assign the renderer that was passed in the options. There's a simple HTML template which is just a copy of the HTML from the sample the has a description of the data. I'm also using dojo/topic, which I talked about here. I'm going to demonstrate a couple of different methods of widget communication, one using dojo/topic and one using dojo/Evented. This module also has an updateBlendMode method that simply handle the dojo/topic subscribe and updates the blendMode. This means any module in your application publish an update to the blendMode or whatever reason. In my opinion, this is an ideal method of widget communication, because the individual widgets do not need to be aware of each other. The other thing the sample had was a select menu that allowed you to update the blendMode. Again, I think this is the perfect candidate for another widget. The code for this widget could look something like this: 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);
}
}
});
}) This widget uses the dojo/Evented and dojo/topic. You'll notice that the widget itself extends dojo/Evented using dojo/_base/declare. This allows you to use this.emit() to emit events from your widget. This widget has the select-menu for blendModes and also a checkbox to set whether or not you want to emit an event with the new blendMode or publish the blendMode via dojo/topic. The template for this looks like this: <div>
<div>
<input type="checkbox" data-dojo-attach-point="cboxNode"> Use 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">copy</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, so we have two widgets, one that creates the FeatureLayer with the blendRenderer and another widget that updates the blendMode of the renderer. Let's wire this up in a main.js module to get things started. 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');
// one way to do update the blendMode
blendSelectionWidget.on('blend-select-update', function(mode) {
console.log('update blend mode with events');
renderer.blendMode = mode;
populationWidget.get('layer').setRenderer(renderer);
populationWidget.get('layer').refresh();
});
});
}); Ok, so the purpose of the main file is to create the map and load the widgets, passing the required options while also using the utils we created earlier. You will also notice that I am using blendSelectionWidget.on('blend-select-update', function(mode){}) to listen for when an event is emitted and manually update the renderer for the layer in the population widget. This is another way you can do communication between widgets, where you can do it in a main file or maybe you'll have a WidgetController that handles all your applications widget communication, it really depends on what tickles your fancy. Normally, I would even break out the map creation as it's own widget as well. I'd also like to point out the dojoConfig for this application. var dojoConfig = {
isDebug: true,
deps: ['app/main'],
packages: [{
name: 'app',
location: location.pathname.replace(new RegExp(/\/[^\/]+$/), '') + 'app'
}]
}; That is dead simple. Doing it this way, I have defined an app package and all my modules live in this package. This means I don't have to create a widgets package or a utils package, I can just reference them as app/widgets and app/utils. Notice the deps property too. This will tell the Dojo loader to load this module when Dojo is loaded. You can check the docs here. This makes my actual index.html file really simple. <!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> You can read about other methods to initialize your application here. Just take it piece by piece You can find the entire modularized application in this github repo. I hope this provides a little more insight into how you might break up an application into modules and how you can start thinking about modularity in your own development. I tend to like breaking out features of an application into their own widgets, whether it be editing, searching, geocoding even just adding some behavior. You can see some other samples of modularity in stuff I worked on in the past in this starter-kit, which is fully configurable or my latest experiments in this yeoman generator, and sample app. Another app that is very modular is something like the cmv app. Again, this is simply my opinion on how I think you could break up an app into smaller modules that make it not only easier to maintain over time but to easily add new functionality as well. For more geodev tips and tricks, check out my blog.
... View more
08-12-2015
11:57 AM
|
0
|
0
|
2294
|
|
BLOG
|
Another neat feature that's part of the ArcGIS API for JavaScript 4.0beta1 is this concept of view padding. At first glance, you may be wondering exactly what view padding does. Here is a sample from the docs that is a good demonstration of what view padding actually does. Looking at this sample you see Liberty Island and a DOM element with some text on the right. What may not seem obvious is the center of the map was set as the location of Liberty Island, but by setting the view padding, the view will offset the center of the map by that padding amount. Remember, it's the view that controls how the map is drawn. This allows you to add sidebars, footers or headers to your map, but still be able to utilize as much of the map as possible. Think of it almost as a frame around the map where each side can be resized as needed. Maybe you've done side panels in your application that take up a lot of space, cover up the map in certain situations when they don't need to, or you resize the map to allow the sidebar to fit. Now you have another option to just adjust the padding the of the view to offset this for you. This also means you can adjust how the map and ui elements to interact when something is updated. Maybe you want to resize the side panel when not in use, so you probably want to adjust the view padding at the same time. You could do that by adjust the sample above a little bit. require([
"esri/Map",
"esri/views/SceneView",
"esri/widgets/Search",
"dojo/on",
"dojo/domReady!"
], function(
Map,
SceneView,
Search,
on
) {
//Create the map
var map = new Map({
basemap: "topo"
});
//Create the view set the view padding to be 320 px
var view = new SceneView({
container: "viewDiv",
map: map,
center: [-118, 34],
zoom: 9,
padding: {
right: 320
}
});
view.then(function() {
var searchWidget = new Search({
view: view
}, "searchDiv");
searchWidget.startup();
});
var resize = document.getElementById("resize");
on(resize, "click", function() {
if (view.padding.right === 320) {
view.padding = { right: 18 };
} else {
view.padding = { right: 320 };
}
});
//Using the view.padding to update the css
var updatePadding = function(padding) {
var right = padding.right + "px";
var paddingRight = document.querySelector("#padding").style;
paddingRight.width = right;
paddingRight.visibility = "visible";
};
updatePadding(view.padding);
// watch for view padding updates
view.watch('padding', function(val) {
updatePadding(val);
});
}); In this case, we are watching for a button click and adjusting the view padding, which in turn will resize the side panel. That's not too difficult to accomplish the in the EsriJS beta. You can view a demo of this application here. View padding in the current beta release is one of those nice little touches to the API that provides a lot of flexibility to you as a developer. It allows you to get creative with your user-interface and how that impacts your users interaction with the map. So have some fun with it. For more geodev tips and tricks, check out my blog.
... View more
08-05-2015
09:10 AM
|
0
|
0
|
1282
|
|
POST
|
It's not returning any results. The issue is JSBIN running in a mixed HTTP/HTTPS environment. Your server isn't set up for HTTPS, so you need to change all URLs to HTTP, including the JSBIN link like this JS Bin - Collaborative JavaScript Debugging
... View more
07-30-2015
10:28 AM
|
1
|
1
|
1234
|
|
POST
|
That's because it's not global, can't access it in the HTML Use dojo/on to handle events better JS Bin - Collaborative JavaScript Debugging
... View more
07-30-2015
08:49 AM
|
2
|
0
|
1051
|
|
POST
|
Looks like jsbin doesn't like http links, so change script tag to https <script src="https://js.arcgis.com/3.14"></script>
... View more
07-30-2015
08:10 AM
|
2
|
1
|
3149
|
| Title | Kudos | Posted |
|---|---|---|
| 2 | 2 weeks ago | |
| 1 | 07-17-2026 10:17 AM | |
| 2 | a month ago | |
| 2 | 05-19-2026 02:12 PM | |
| 1 | 04-24-2026 11:01 AM |
| Online Status |
Offline
|
| Date Last Visited |
yesterday
|