odoe

Mixin it up

Blog Post created by odoe on May 27, 2015

esrijs-mixins.jpg

Have you ever started working on an application with some custom widgets and you begin to notice that maybe your widget is starting to grow a little unruly? Maybe it's gone from 50 lines of code to 100, maybe 200, 1000? Don't be ashamed, depending on your needs, some widgets can require a lot of stuff to get the job done, and that's what we're all about right? Getting the job done.

 

Take a step back and start looking at how maybe you can break that widget up. Are you doing stuff like adding graphics to the map based on certain widget events? Are you listening for map activity to do some analysis using the current map extent and another service that may not be loaded in the map? Maybe you just need to send the users current location to a FeatureService every time a widget is activated, but you'd like to port this functionality across all widgets? I don't know your use case, but if you can step back and start breaking your widget down into smaller pieces, maybe we can start making things easier to manage.

 

Break up the behavior

A good way of looking at widget composition is looking at the behavior you need in your widget. For example, let's look at the ever famous dijit/_WidgetBase that you probably have used to build your own widgets. When you extend a _WidgetBase, you are given a set of behaviors most notably the dijit lifecycle, which is a set of methods you can override to build your widget during it's creation. Fore more details about custom widgets and the dijit lifecycle, you can check out this video on my blog. You also get another set of behavior, which is the set/get methods for attributes from dojo/Stateful, that allows you to watch for changes.

 

Now if you want this widget to use a templated HTML string to define it's user interface, you can extend the dijit/_TemplatedMixin that adds one simple property for you, the templateString. That one some property allows you to build an entire UI around your widget. That's pretty powerful when you think about it.

 

Be on your best behavior

So how exactly could you start incorporating mixins into your own projects? Let's assume you notice that in your widgets you are constantly adding the ability to recenter the map when something happens. One way you can accomplish this is to break that behavior out into a mixin. I don't know if it's convention, but I notice in the dijit library that mixins are prefixed with an underscore, so let's make a mixin called _RecenterMixin.

 

That could look something like this:

define([
  'dojo/_base/declare'
], function(declare) {
  return declare(null, {
    // this mixin works under the assumption
    // that you have a map assigned to your widget.
    // it also assumes the map was initialized with
    // a center and zoom
    recenter: function() {
      var map = this.get('map');
      var params = map._mapParams;
      if (params.center) {
        map.centerAt(params.center);
      }
    }
  });
});

 

So now you can make a widget that extends this mixin and just call the recenter method when you need it. I can use it in a widget that wraps BasemapGallery and recenter the map when the base map is changed.

define([
  'dojo/_base/declare',
  'dojo/_base/lang',
  'dojo/dom',
  'dojo/dom-construct',
  'esri/domUtils',
  'esri/dijit/BasemapGallery',
  'dijit/_WidgetBase',
  'dijit/_TemplatedMixin',
  './_RecenterMixin',
  'dojo/text!./templates/BasemapSwitcher.html'
], function(
  declare, lang, dom, domConstruct,
  esriDomUtils, BasemapGallery,
  _WidgetBase, _TemplatedMixin,
  _RecenterMixin,
  template
) {
  return declare([_WidgetBase, _TemplatedMixin, _RecenterMixin], {
    templateString: template,
    postCreate: function() {
      var node = dom.byId('map_root');
      esriDomUtils.hide(this.domNode);
      domConstruct.place(this.domNode, node);
      var map = this.get('map');
      this.gallery = new BasemapGallery({
        map: map,
        showArcGISBasemaps: true
      }, this.bmNode);
      this.gallery.startup();
      this.gallery.on('selection-change', lang.hitch(this, function() {
        this.recenter(); // MIXIN MAGIC
      }));
    },
    hide: function() {
      esriDomUtils.hide(this.domNode);
    },
    show: function() {
      esriDomUtils.show(this.domNode);
    }
  });
});

Looking at this sample, I could even move the hide/show methods to a mixin and reuse it elsewhere

 

Wait a second

You might be asking yourself what is the difference between something like _WidgetBase and a mixin? Typically, a mixin on it's own is pretty useless. In the example above, if you just extended _RecenterMixin alone, you wouldn't have the postCreate method or a templateString to work with. A mixin may even depend on something like _WidgetBase, hooking into lifecycle methods. At the core of it, you are still extending modules, extending the behavior or widgets.

 

I put a demo repo of this project up on github for you to play with.

 

So go out there and see if you can start breaking out mixins in your project and maybe simplify the maintenance of your application. You might find you really like the ability to reuse behavior among different widgets, get more use out of all the hard work you put in to your code.

 

For more geodev tips and tricks, check out my blog.

Outcomes