Widgets 101: Customizing and Creating Widgets with the ArcGIS API for JavaScript JC Franco – @arfncode Matt Driscoll – @driskull
Widgets 101: Customizing and Creating
Widgets with the ArcGIS API for JavaScript
JC Franco – @arfncode
Matt Driscoll – @driskull
Welcome
AgendaShort URL: bit.ly/widgets101
About widgets
Building blocks
Building a widget
3.x
4.x
Tips & best practices
Resources
Q & A
WidgetsWhat?
Encapsulated
Cohesive
Single-purpose pieces of functionality
User interface
Why?
Reusable
Interchangeable
Modular
How?
Different frameworks are available
Focusing on Dijit
Dojo toolkitFoundation of ArcGIS JavaScript API
AMD support
Class-based inheritance
Internationalization
DijitDojo’s UI Library
Separate namespace (dijit)
Built on top of Dojo
Has themes
Asynchronous Module Definition (AMD)Asynchronous loading
Web-based solution
Lazy loading
Fewer globals
Dependency handling
AMD exampledefine
require
// moduleA.js
define(["moduleB"], function (moduleB) {
// module API
return {
_data: 100,
calculate: function () {
moduleB.calculate(this._data);
}
};
});
// main.js
require(["moduleA"], function (moduleA) {
moduleA.calculate();
});
AMD Plugins
text
"dojo/text!./templates/WikiWidget.html"
i18n
"dojo/i18n!./nls/WikiWidget",
Building blocks
dijit/_WidgetBase
What you get
Lifecycle
constructor
postMixInProperties
buildRendering
postCreate
startup
destroy
Events
Getters/Setters
Property watching
constructor
Called immediately when widget is created
Can be used for initialization
Can be used to manipulate widget parameters
constructor: function(params) {
// initialize private variables
this._activeTasks = [];
// manipulate user-provided params
if (params && params.oldProp {
params.newProp = params.oldProp;
delete params.oldProp;
}
}
postMixInProperties
Called after properties have been mixed into the instance
Can be used to access/alter properties after being mixed in, but before rendering
For example
postMixInProperties: function() {
this.get("inherited");
this._initialTitle = this.title;
}
var myWidget = new MyWidget({ title: "myTitle" }, "sample-node");
myWidget.toUppercase();
console.log(myWidget.title); // MYTITLE
console.log(myWidget._initialTitle); // myTitle
buildRendering
Widget template is parsed and its DOM is available
Not attached to the DOM tree
buildRendering: function() {
this.get("inherited");
if (this.editMode) {
// editor added before widget is displayed
this._attachEditor(this.domNode);
}
}
postCreate
Most widget DOM nodes are ready at this point
Widget not attached to the DOM yet
Most common point for adding custom logic
postCreate: function() {
this.get("inherited");
when destroyed
this._handleInput)
// set up event listeners
// `this.own` disposes handle
this.own(
on(this.inputNode, "input",
);
this._activeTasks.push(
this._initialize()
);
}
startup
Called manually or by dojo/parser, initializes all children
Recommended point for doing size calculations
Must always call when creating widgets programmatically
startup: function() {
this.get("inherited");
this._resize();
}
destroy
Used for teardown logic
By default, destroys top-level support widgets
Called manually to trigger widget disposal
All handles registered with this.own get removed
destroy: function() {
this.get("inherited");
this._activeTasks.forEach(function(process) {
process.cancel();
});
}
Events// widget function
startTimer: function() {
setInterval(function() {
this.emit("tick");
}.bind(this), 1000);
}
timerWidget.on("tick", function() {
console.log("timer ticked!");
});
timerWidget.startTimer();
// timer ticked!
// timer ticked!
// timer ticked!
Getters/Setterswidget.get("title"); // old
widget.set("title", "new");
widget.get("title"); // new
Watchwidget.watch("title", function(propName, oldValue, newValue) {
console.log(
propName + " changed from ", oldValue, " to ", newValue
);
});
widget.get("title"); // old
widget.set("title", "new"); // "title" changed from "old" to "new"
Code organizationKeep code modular and organized
Code organization: HTMLExtract HTML to separate file
Mix in dijit/_TemplatedMixin
Renders HTML based on a template string (use dojo/text plugin)
Create DOM node attachments
Code organization: CSSExtract widget-specific styles to separate stylesheet
@import widget stylesheet wherever applicable
Example: before/* ./MyWidget.js */
define([
"dijit/_WidgetBase",
"dijit/_TemplatedMixin"
],
function (
_WidgetBase, _TemplatedMixin
) {
return _WidgetBase.createSubclass([_TemplatedMixin], {
templateString: "<div style='background-color: chartreuse;'>" +
"<label style='font-weight: bolder;'>°˖✧◝(⁰▿⁰)◜✧"</div>";
});
});
Example: after
MyWidget.html
<div class="my-widget">
<label class="my-widget text">°˖✧◝(⁰▿⁰)◜✧˖°</label></div>
Example: after
MyWidget.css
.my-widget {
background-color: chartreuse
}
.my-widget text {
font-size: 1.5em;
}
Example: after
MyWidget.js
define([
"dijit/_WidgetBase",
"dijit/_TemplatedMixin",
"dojo/text!./templates/MyWidget.html"
],
function (
_WidgetBase, _TemplatedMixin,
template
) {
return _WidgetBase.createSubclass([_TemplatedMixin], {
templateString: template
});
});
CSSUse classes
<style>
.my-widget {
background-color: chartreuse;
}
</style>
<div class="my-widget">...</div>
and avoid inline styles
<div style="background-color: chartreuse">...</div>
Accessibility (a11y)Enable your application to be used by everyone
Consider other input devices besides the mouse
Keyboard
Touch
Screen reader
semantic markup, ARIA roles
dijit/a11yclick
Internationalization (i18n)Keep text separate from application logic
Support multiple languages
Helps ease translation
define({
root: ({
"button": "Home",
"title": "Default extent"
}),
"ar": 1,
...
"zh-cn": 1
});
DOM manipulationHere to help...
dojo/dom
dojo/dom-attr
dojo/dom-class
dojo/dom-construct
dojo/dom-style (used sparingly)
DOM manipulationHere to help...
// without `dojo/dom-class`
document.getElementById("container-id")
.classList.add("round-borders");
// with `dojo/dom-class`
domClass.add("container-id", "round-borders");
Let's build a widget!
WikiWidget (requirements)Use Wikipedia API to geosearch for entries
Display results in a list
List items should center on the map and display a popup
The popup should have a link for more info (wiki page)
Preview
Building WikiWidget the 3x waySteps
That's all for 3.x
4.x WidgetsWidget Pattern
View – the face
ViewModel – the brain
ViewUses ViewModel APIs to render the UI
View-specific logic resides here
ViewModelCore logic of widget resides here
Provides necessary APIs for the view to do it's thing
No DOM/UI concerns (think data)
BenefitsReusable
Testable
Logic without UI concerns
Framework compatibility
Let's update WikiWidgetSteps
WikiWidget + ReactDemo
Framework integrationUse ViewModels to create custom UIs in the framework of your choice
Angular 2 – demo
React – demo
Elm – demo
Ember
Rene Rubalcava - http://odoe.net/
Tips & best practices
Use a styleguideDefines rules for consistent code
Naming conventions
Whitespace
Common patterns
Etc...
Some options
Airbnb
idiomatic
jQuery
Dojo
Linting (code analysis)Highlight issues in your code based on predefined rules
JSLint
JSHint
ESLint
FormattingFormat your code based on predefined rules
ESLint
JS Beautifier
TestingAutomated testing helps you catch regressions as you move forward
Intern
Jasmine
QUnit
Karma
CSS preprocessorsFeatures
Variables
Mixins
@import & @extend
Allow us to
Restyle quickly
Theme
Write less code
Flavors
Sass
Stylus
Less
Demo
CSS methodologiesEstablish guidelines/rules for maintainable CSS
CSS & HTML best practices
Naming conventions
Ordering/grouping of CSS rules
Help dealing with specificity
Flavors
Block-Element-Modifier (BEM)
Scalable and Modular Architecture for CSS (SMACSS)
Object Oriented CSS (OOCSS)
SUIT CSS
Atomic OOBEMITSCSS
No silver bullet - choose what's best for your project/team
Use the source, Luke.Use GitHub to browse code and learn more about existing projects
dojo
dijit
dojox
etc...
Wrapping up
Suggested sessions
Wednesday
Operations Dashboard: Extending with Custom Widgets
Thursday
Web AppBuilder for ArcGIS: Customizing and Extending
Web AppBuilder for ArcGIS: Build your First Widget in 15 mins
Extending the Operations Dashboard for ArcGIS
ArcGIS API 4.0 for JavaScript: Patterns and Best Practices
Additional resourcesUnderstanding dijit/_WidgetBase
ArcGIS API for JavaScript 3.0 SDK
3.x
Calcite dijit theme (3.x)
4.x
ArcGIS API for JavaScript 4.0 SDK
Styling (4.0)
Please take our survey
Your feedback allows us to help maintain high
standards and to help presenters
Q & AQuestions?
Bonus: Haikuuser conference—
attended widget session
satisfaction now