Widgets 101: Customizing and Creating Widgets with the ArcGIS API for JavaScript

JC Franco – @arfncode Matt Driscoll – @driskull Welcome Agenda Short URL: bit.ly/widgets101 About widgets Building blocks Building a widget 3.x 4.x Tips & best practices Resources Q & A Widgets What? Encapsulated Cohesive Single-purpose pieces of functionality Why? Reusable Interchangeable Modular How? Different frameworks are available Focusing on Dijit Foundation of ArcGIS JavaScript API AMD support Class-based inheritance Internationalization Dijit Dojo’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 example define

// moduleA.js define(["moduleB"], function (moduleB) { // module API return { _data: 100, calculate: function () { moduleB.calculate(this._data); } }; }); require

// 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 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

postMixInProperties: function() { this.get("inherited");

this._initialTitle = this.title; }

For example

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");

// set up event listeners // `this.own` disposes handle when destroyed this.own( on(this.inputNode, "input", this._handleInput) );

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/Setters widget.get("title"); // old widget.set("title", "new"); widget.get("title"); // new Watch widget.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 organization Keep code modular and organized Code organization: HTML Extract 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: CSS Extract 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: "

" + "
"; });

}); Example: after MyWidget.html

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 }); }); CSS Use classes

...

and avoid inline styles

...
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 manipulation Here to help... dojo/dom dojo/dom-attr dojo/dom-class dojo/dom-construct dojo/dom-style (used sparingly) DOM manipulation Here 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 way Steps That's all for 3.x 4.x Widgets Widget Pattern View – the face ViewModel – the brain View Uses ViewModel APIs to render the UI View-specific logic resides here ViewModel Core logic of widget resides here Provides necessary APIs for the view to do it's thing No DOM/UI concerns (think data) Benefits Reusable Testable Logic without UI concerns Framework compatibility Let's update WikiWidget Steps WikiWidget + React Demo Framework integration Use 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 styleguide Defines 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 Formatting Format your code based on predefined rules ESLint JS Beautifier Task runners Automate all the things Grunt Gulp Testing Automated testing helps you catch regressions as you move forward Intern Jasmine QUnit Karma CSS preprocessors Features Variables Mixins @import & @extend Allow us to Restyle quickly Theme Write less code Flavors Sass Stylus Less Demo CSS methodologies Establish 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 resources Understanding 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) Get The Code bit.ly/widgets101 Please take our survey Your feedback allows us to help maintain high standards and to help presenters Q & A Questions? Bonus: Haiku user conference— attended widget session satisfaction now