Building Your own Widget with ArcGIS API for JavaScript Matt Driscoll @driskull JC Franco @arfncode
Agenda About Widgets Prerequisites Widget framework Theming DO IT! Tips & tricks
About Widgets What? Encapsulated Cohesive Single-purpose pieces of functionality Why? Reusable Interchangeable How? Different frameworks are available
Prerequisites Accessor (esri/core/accessor) TypeScript
Accessor JavaScript API Foundation Consistent developer experience TypeScript support
Accessor - Unified Object Constructor var view = new MapView({ container: "viewdiv", map: map }); var symbol = new SimpleMarkerSymbol({ style: "square", color: "blue" }); var widget = new BasemapToggle({ view: view, nextbasemap: "hybrid" });
Accessor - Defining Properties (getters + setters) var Foo = Accessor.createSubclass({ properties: { // read-only foo: { readonly: true, value: new // aliased bar: { aliasof: "foo" }, } }); // autocast baz: { type: SomeClass }
Accessor - Property watching // watch for changes using a property view.watch("map.basemap.title", handle // watch for changes to multiple prope view.watch("stationary, interacting",
TypeScript Superset of JavaScript Compiled to JavaScript Statically type-checked Syntactic sugar... sweet! Use ES6 syntax while targeting ES5 environments
Type safety let view: MapView SceneView; //... /* * TS2322: Type '"not-a-view"' is not * to type 'MapView SceneView'. */ view = "not-a-view";
Typings Help describe what things are: type PresenterName = "Alan" "Matt" interface Person { name: string; age: number; } interface Presenter extends Person { name: PresenterName; }
JS of the future, now Fat arrow functions const somefn = () => { /*... */ }; // instead of const somefn = function () { /*... */
JS of the future, now Template strings const text = `Hello. Nice to meet you, // instead of const text = "Hello. Nice to meet you,
JS of the future, now Destructuring const { map, zoom, scale } = view; // instead of const map = view.map; const zoom = view.zoom; const scale = view.scale;
JS of the future, now Rest Parameters function ignorefirst(first,...therest console.log(therest); } // instead of function ignorefirst() { var therest = Array.prototype.slice. } console.log(therest);
JS of the future, now Decorators @log() foo = "foo";
TypeScript IDE Support Visual Studio: 2015/2013, Code WebStorm Sublime Text Atom Eclipse Brackets Emacs Vim
TypeScript + JS API 4 Install TypeScript Install JavaScript API typings Start writing code! TypeScript setup
Let's see some widget decorators
Creating a class @subclass + declared @subclass("example.foo") class Foo extends declared(accessor) { //... }
Creating a class: multiple inheritance pattern @subclass + declared interface Foo extends Bar, Baz {} @subclass("example.foo") class Foo extends declared(accessor, B //... }
Defining a property @property @property() foo = new Foo();
Custom setter @property @property() set myproperty(value: string) { // note internal `_set` this._set("myproperty", value); this._ensurevalidity(value); }
Computed properties @property @property({ dependson: ["firstname, lastname"] }) get fullname() { return `${this.firstname} ${this.las }
Read-only value @property @property({ readonly: true }) myproperty = "I'm read-only";
Autocast @property @property({ type: MyClass }) myproperty; instance.myproperty = { /* params */ } console.log(instance.myproperty instan
Alias a property @property @property({ aliasof: "bar.baz" }) foo;
Alias a property @aliasof @aliasof("bar.baz") foo;
Handle click and key events @accessiblehandler @accessiblehandler private function _dosomething() { //... }
Rendering when properties change @renderable @property() @renderable() title = "hello"; @property() @renderable([ "viewmodel.foo", "viewmodel.bar" ]) viewmodel = new ViewModel();
More details in the SDK Implementing Accessor Widget Development
Widget framework JSX Lifecycle Properties Methods Events
Widget Framework: About esri/widgets/widget: Our new widget framework Accessor-based Built with TypeScript
Widget Framework: JSX JavaScript extension syntax adds XML syntax to JavaScript Looks similar to HTML Can use JS inline! <div class={classlookup.hello} onclick={this._handleclick} tabindex={0}> Hello World </div>
Widget Framework: Lifecycle constructor() postinitialize() render() destroy()
constructor() constructor(params?: any) { super(); // Do some stuff! }
postinitialize() postinitialize() { this.own( watchutils.on(this, "property", => ); }
render() Return JSX Virtual DOM render() { return ( <button>{this.title}</button> ); } Widget rendering (SDK)
destroy() destroy() { // cleanup listeners // destroy other widgets // dereference variables // etc. }
Framework: Getting/Setting Properties // normal setting of a prop mywidget.property = value; // normal getting of a prop console.log(mywidget.property); // internal set property // will not trigger setter this._set("property", propertyvalue); // internal get property // will not trigger getter this._get("property");
Framework: ViewModels (The brain) Core logic of widget resides here Provides necessary APIs for the view to do it's thing No DOM/UI concerns (think business logic)
ViewModels: Why? Framework integration Reusability Separates concerns
Widget Framework: Views (The face) esri/widgets/widget Uses ViewModel APIs to render the UI View-specific logic resides here
Views: Why? Separates concerns Framework compatibility
Views: Defining ViewModel @property({ type: MyViewModel }) viewmodel: MyViewModel = new MyViewMod
Widget Framework: Method Convention Public Methods public mymethod() {} Private Methods private _mymethod() {}
Widget Framework: Events Views have ability to emit() an event. ViewModel needs to import dojo/evented in order to emit() Views can alias an event with vmevent decorator.
Widget Framework: View Events Widget views extend dojo/evented this.emit("my-event", {...});
Widget Framework: ViewModel Events 1. Import evented import Evented = require("dojo/evented 2. Extend Evented interface MyViewModel extends Evented... } 3. Emit event when necessary this.emit("my-event", {...});
Widget Framework: Aliased View Events @vmevent("my-event") @property({ type: MyViewModel }) viewmodel: MyViewModel = new MyViewMod
Widget Theming Out of the box themes SDK: Styling topic Sass BEM
Widget Theming: Guide SDK Guide: Styles
Widget Theming: Out of the box Themes Demo
Widget Theming: Sass CSS preprocessor Variables @mixin (group statements) @include - (use mixins) @import - (split up files) @extend - (inheritance) More power!
Sass makes it easier to... Restyle Theme Modular / DRY Be organized Write less code :)
Sass Install Installing Sass Themes source on Github
Widget Theming: BEM BEM: Block Element Modifier Methodology to create reusable components Uses delimiters to separate block, element, modifiers Provides semantics (albeit verbose) Keeps specificity low Scopes styles to blocks /* block */.example-widget {} /* block element */.example-widget input {}.example-widget submit {} /* block--modifier */.example-widget--loading {} /* block element--modifier */.example-widget submit--disabled {}
DO IT! Build a widget! Demo
Setup HTML & CSS with Sass HTML Setup Steps Sass Setup Steps
Tips & Tricks Collections Accessibility i18n
Rethinking Widget APIs Using esri/core/collection instead of Arrays Using esri/core/accessor instead of plain objects Adding properties to manage widget state Hiding nodes with Widget framework JSX instead of CSS setting JSX node to null when necessary Private/protected variables
Accessibility tabindex aria roles keyboard events
i18n Import language file import * as i18n from "dojo/i18n!./myw Setup root language file with languages // "/nls/mywidget" define({ root: ({ helloworld: "Hello World" }), "es": 1 }); Setup other languages. (\es) // "/nls/es/mywidget" define({ helloworld: "Hola Mundo" });
Suggested Sessions Building Classes Using Accessor and the ArcGIS API for JavaScript Using TypeScript with ArcGIS API for Javascript Deep Dive on How ArcGIS API for JavaScript Widgets Were Built
Additional Resources Styling Implementing Accessor Setting up TypeScript Widget Development JavaScript Sessions at DevSummit Documentation 4.3
Use the source luke esriurl.com/buildwidgets2017
Please Take Our Survey! 1. Download the Esri Events app and go to DevSummit 2. Select the session you attended 3. Scroll down to the "Feedback" section 4. Complete Answers, add a Comment, and Select "Submit"
Questions?
Thank you!