Directions Widget Modification?

1076
10
Jump to solution
08-03-2017 09:55 AM
deleted-user-iwYtitJHBIyn
New Contributor II

Hello Everyone,

I've added the AGO Directions Widget to an application for routing my building inspectors.  The widget works very nicely but I am wondering if it can be modified, and if so, what the code modification would be to achieve the following.

Have the stops show not only the stop number but also the address of the stop as well?

Thank you.

Jeff

0 Kudos
1 Solution

Accepted Solutions
RobertScheitlin__GISP
MVP Esteemed Contributor

Jeff,

   The labels are added after the second address. Adjust the label properties in the _onDirectionsStart function.

Widget.js

///////////////////////////////////////////////////////////////////////////
// Copyright © 2014 - 2016 Esri. All Rights Reserved.
//
// Licensed under the Apache License Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///////////////////////////////////////////////////////////////////////////

define([
    'dojo/_base/declare',
    'jimu/BaseWidget',
    'esri/dijit/Directions',
    'esri/tasks/locator',
    'esri/tasks/RouteParameters',
    'esri/request',
    'esri/graphicsUtils',
    'esri/layers/ArcGISDynamicMapServiceLayer',
    'dojo/on',
    'dojo/_base/lang',
    'dojo/_base/html',
    'dojo/_base/array',
    'dojo/_base/config',
    'dojo/Deferred',
    'dojo/promise/all',
    'jimu/portalUtils',
    'esri/layers/GraphicsLayer',
    'esri/symbols/TextSymbol',
    'esri/symbols/Font',
    'esri/graphic',
    'esri/Color'
  ],
  function(declare, BaseWidget, Directions, Locator, RouteParameters, esriRequest, graphicsUtils,
    ArcGISDynamicMapServiceLayer, on, lang, html, array, dojoConfig, Deferred, all, portalUtils,
    GraphicsLayer, TextSymbol, Font, Graphic, Color) {

    return declare([BaseWidget], {
      name: 'Directions',
      baseClass: 'jimu-widget-directions',
      _dijitDirections:null,
      _routeTaskUrl: "//route.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World",
      _locatorUrl: "//geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer",
      _active: false,//save last active state
      _dijitDef: null,
      _trafficLayer: null,
      _isInitPresetStopsFlag: false,
      _stopsLabelGL: null,

      onOpen: function () {
        this._toggleDartStyleByAppConfig();
        this.widgetManager.activateWidget(this);
      },

      onClose: function(){
        this._hide();
      },

      onNormalize: function(){
        this._show();
      },

      onMinimize: function(){
        this._hide();
      },

      onMaximize: function(){
        this._show();
      },

      onDeActive: function(){
        this._deactivateDirections();
        this._enableWebMapPopup();
      },

      setStartStop: function(stop){
        console.info("stop added", stop);
        this.getDirectionsDijit().then(lang.hitch(this, function(directionsDijit){
          directionsDijit.reset().then(lang.hitch(this, function(){
            directionsDijit.addStop(stop);
          }), lang.hitch(this, function(err){
            console.error(err);
          }));
        }), lang.hitch(this, function(err){
          console.error(err);
        }));
      },

      addStop: function(stop){
        console.info("stop added", stop);
        this.getDirectionsDijit().then(lang.hitch(this, function(directionsDijit) {
          directionsDijit.addStop(stop);
        }), lang.hitch(this, function(err) {
          console.error(err);
        }));
      },

      getDirectionsDijit: function(){
        if(!this._dijitDef){
          this._dijitDef = new Deferred();
        }
        if(this._dijitDef.isFulfilled()){
          this._dijitDef = new Deferred();
        }
        if(this._dijitDirections){
          this._dijitDef.resolve(this._dijitDirections);
        }
        return this._dijitDef;
      },

      _handlePopup: function(){
        if(this.map.activeDirectionsWidget && this.map.activeDirectionsWidget.mapClickActive){
          this._disableWebMapPopup();
        }else{
          this._enableWebMapPopup();
        }
      },

      _disableWebMapPopup:function(){
        if(this.map){
          this.map.setInfoWindowOnClick(false);
        }
      },

      _enableWebMapPopup:function(){
        if(this.map){
          this.map.setInfoWindowOnClick(true);
        }
      },

      destroy: function(){
        if(this.map.activeDirectionsWidget === this._dijitDirections){
          this.map.activeDirectionsWidget = null;
        }
        if(this._trafficLayer){
          this.map.removeLayer(this._trafficLayer);
          this._trafficLayer = null;
        }
        this._handlePopup();
        this.inherited(arguments);
      },

      startup: function(){
        this.inherited(arguments);
        this.portal = portalUtils.getPortal(this.appConfig.portalUrl);

        this._stopsLabelGL = new GraphicsLayer();
        this.map.addLayer(this._stopsLabelGL);

        this._preProcessConfig().then(lang.hitch(this, function(){
          var routeParams = new RouteParameters();
          var routeOptions = this.config.routeOptions;
          if(routeOptions){
            if(routeOptions.directionsLanguage){
              routeParams.directionsLanguage = routeOptions.directionsLanguage;
            }
            else{
              routeParams.directionsLanguage = dojoConfig.locale || "en_us";
            }
            routeParams.directionsLengthUnits = routeOptions.directionsLengthUnits;
            routeParams.directionsOutputType = routeOptions.directionsOutputType;
            if(routeOptions.impedanceAttribute){
              routeParams.impedanceAttribute = routeOptions.impedanceAttribute;
            }
          }

          var options = {
            map: this.map,
            searchOptions: this.config.searchOptions,
            routeParams: routeParams,
            routeTaskUrl: this.config.routeTaskUrl,
            dragging: true,
            showClearButton: true,
            mapClickActive: true
          };

          //set units on root for API-3.20
          if(this.config.routeOptions && this.config.routeOptions.directionsLengthUnits) {
            options.directionsLengthUnits = this.config.routeOptions.directionsLengthUnits;
          }
          if(this.config.trafficLayerUrl){
            this._trafficLayer = new ArcGISDynamicMapServiceLayer(this.config.trafficLayerUrl);
            options.trafficLayer = this._trafficLayer;
            options.traffic = true;
          }else{
            options.traffic = false;
            options.showTrafficOption = false;
          }

          this.setDoNotFetchTravelModes(options).then(lang.hitch(this, function() {
            html.empty(this.directionController);
            var directionContainer = html.create('div', {}, this.directionController);
            //Only init Directions dijit when we can access the route task url.
            esriRequest({
              url: options.routeTaskUrl,
              content: {
                f: 'json'
              },
              handleAs: 'json',
              callbackParamName: 'callback'
            }).then(lang.hitch(this, function() {
              this._dijitDirections = new Directions(options, directionContainer);
              //html.place(this._dijitDirections.domNode, this.directionController);
              this._dijitDirections.startup();

              this.own(on(this._dijitDirections, 'load', lang.hitch(this, this._onDirectionsActivate)));
              this.own(on(this._dijitDirections, 'directions-start', lang.hitch(this, this._onDirectionsStart)));
              this.own(on(this._dijitDirections, 'directions-clear', lang.hitch(this, this._onDirectionsClear)));

              this.own(on(this._dijitDirections,
                'directions-finish',
                lang.hitch(this, this._onDirectionsFinish)));

              this.own(on(this._dijitDirections,
                'map-click-active',
                lang.hitch(this, this._handlePopup)));

              this._activateDirections();
              this._storeLastActiveState();

              if (this._dijitDef && !this._dijitDef.isFulfilled()) {
                this._dijitDef.resolve(this._dijitDirections);
              }
            }), lang.hitch(this, function(err) {
              console.log("Can't access " + options.routeTaskUrl, err);
            }));
          }), lang.hitch(this, function(err) {
            console.error(err);
          }));
        }));
      },

      onAppConfigChanged: function(appConfig){
        this.appConfig = appConfig;
        this._toggleDartStyleByAppConfig();
      },

      _onDirectionsClear: function(evt){
        if(this._stopsLabelGL){
          this._stopsLabelGL.clear();
        }
      },

      _onDirectionsStart: function(evt){
        if(evt.target.stops && evt.target.stops.length > 0 && this._stopsLabelGL){
          this._stopsLabelGL.clear();
          var stops = evt.target.stops;
          var font = new Font("12px", Font.STYLE_NORMAL, Font.VARIANT_NORMAL, Font.WEIGHT_BOLD);
          array.map(stops, lang.hitch(this, function(stop){
            if ("undefined" !== typeof stop.name && "" !== stop.name){
              var textSymbol = new TextSymbol(stop.name, font, new Color([0, 0, 0]));
              textSymbol.horizontalAlignment = "left";
              textSymbol.setOffset(12, 10);
              var labelPointGraphic = new Graphic(stop.feature.geometry, textSymbol);
              this._stopsLabelGL.add(labelPointGraphic);
            }
          }));
        }
      },

      _onDirectionsFinish: function(evt){
        if(evt && evt.result){
          var routeResults = evt.result.routeResults;
          if(lang.isArrayLike(routeResults) && routeResults.length > 0){
            var routes = [];
            array.forEach(routeResults, function(routeResult){
              if(routeResult.route){
                routes.push(routeResult.route);
              }
            });
            if(routes.length > 0){
              var ext = null;
              try{
                ext = graphicsUtils.graphicsExtent(routes);
                if(ext){
                  ext = ext.expand(1.3);
                }
              }catch(e){
                console.log(e);
              }
              if(ext){
                this.map.setExtent(ext);
              }
            }
          }
        }
      },

      _preProcessConfig:function(){
        if(!this.config.geocoderOptions){
          this.config.geocoderOptions = {};
        }
        if(!(this.config.geocoderOptions.geocoders &&
         this.config.geocoderOptions.geocoders.length > 0)){
          this.config.geocoderOptions.geocoders = [{
            url: '',
            placeholder: ''
          }];
        }

        var placeholder = this.config.geocoderOptions.geocoders[0].placeholder;

        if(!placeholder){
          if(!this.config.routeTaskUrl){
            //user doesn't open the setting page, we use the default placeholder
            placeholder = this.nls.searchPlaceholder;
          }
        }

        this.config.searchOptions = {
          enableSuggestions: this.config.geocoderOptions.autoComplete,
          maxSuggestions: this.config.geocoderOptions.maxLocations,
          minCharacters: this.config.geocoderOptions.minCharacters,
          suggestionDelay: this.config.geocoderOptions.searchDelay,
          sources: [{
            locator: null,
            name: '',
            singleLineFieldName: '',
            outFields: ["*"],
            placeholder: placeholder
          }]
        };

        var def = new Deferred();
        all([this._getRouteTaskUrl(), this._getLocatorUrl()]).then(
          lang.hitch(this, function(results){
          this.config.routeTaskUrl = results[0];

          this.config.routeTaskUrl = this._replaceRouteTaskUrlWithAppProxy(this.config.routeTaskUrl);

          var locatorUrl = results[1];
          esriRequest({
            url: locatorUrl,
            hanleAs:'json',
            content:{
              f:'json'
            },
            callbackParamName:'callback'
          }).then(lang.hitch(this, function(geocodeMeta){
            this.config.searchOptions.sources[0].locator = new Locator(locatorUrl);
            this.config.searchOptions.sources[0].name = geocodeMeta.serviceDescription || '';
            this.config.searchOptions.sources[0].singleLineFieldName =
             geocodeMeta.singleLineAddressField && geocodeMeta.singleLineAddressField.name || '';
            def.resolve();
          }), lang.hitch(this, function(err){
            console.error(err);
            def.reject();
          }));
        }), lang.hitch(this, function(err){
          console.error(err);
          def.reject();
        }));
        return def;
      },

      _replaceRouteTaskUrlWithAppProxy: function(routeTaskUrl){
        // Use proxies to replace the routeTaskUrl
        var ret = routeTaskUrl;
        if(!window.isBuilder && !this.appConfig.mode &&
            this.appConfig.appProxies && this.appConfig.appProxies.length > 0) {
          array.some(this.appConfig.appProxies, function(proxyItem) {
            if(routeTaskUrl === proxyItem.sourceUrl) {
              ret = proxyItem.proxyUrl;
              return true;
            }
          });
        }
        return ret;
      },

      _getRouteTaskUrl: function(){
        var def = new Deferred();
        if(this.config.routeTaskUrl){
          def.resolve(this.config.routeTaskUrl);
        }
        else{
          this.portal.loadSelfInfo().then(lang.hitch(this, function(response){
            if(response && response.helperServices && response.helperServices.route){
              def.resolve(response.helperServices.route.url);
            }
            else{
              def.resolve(this._routeTaskUrl);
            }
          }), lang.hitch(this, function(err){
            console.error(err);
            def.resolve(this._routeTaskUrl);
          }));
        }
        return def;
      },

      _getLocatorUrl: function(){
        var def = new Deferred();
        var geocodeArgs = this.config.geocoderOptions &&
         this.config.geocoderOptions.geocoders &&
          this.config.geocoderOptions.geocoders[0];
        var url = geocodeArgs && geocodeArgs.url;
        if(url){
          def.resolve(url);
        }
        else{
          this.portal.loadSelfInfo().then(lang.hitch(this, function(response){
            if(response && response.helperServices &&
             response.helperServices.geocode &&
              response.helperServices.geocode.length > 0){
              var geocode = response.helperServices.geocode[0];
              def.resolve(geocode.url);
            }
            else{
              def.resolve(this._locatorUrl);
            }
          }), lang.hitch(this, function(err){
            console.error(err);
            def.resolve(this._locatorUrl);
          }));
        }
        return def;
      },

      setDoNotFetchTravelModes: function(options) {
        var def = new Deferred();
        if (this.config.travelModesUrl) {
          options.travelModesServiceUrl = this.config.travelModesUrl;
          options.doNotFetchTravelModesFromOwningSystem = false;
          def.resolve();
        } else {
          this._getTravelModesUrlVersion().then(lang.hitch(this, function(version) {
            if (version && version >= 10.4) {
              options.doNotFetchTravelModesFromOwningSystem = false;
            } else {
              options.doNotFetchTravelModesFromOwningSystem = true;
            }
            def.resolve();
          }), lang.hitch(this, function(err){
            def.reject(err);
          }));
        }
        return def;
      },
      _getTravelModesUrlVersion: function() {
        var def = new Deferred();
        esriRequest({
          url: this.config.routeTaskUrl,
          content: {
            f: 'json'
          },
          handleAs: 'json',
          callbackParamName: 'callback'
        }).then(lang.hitch(this, function(results) {
          def.resolve(results.currentVersion);
        }), lang.hitch(this, function(err) {
          console.log("Can't access " + this.config.routeTaskUrl, err);
          def.reject(err);
        }));

        return def;
      },

      _hide: function(){
        if(this._dijitDirections){
          this._storeLastActiveState();
          this._deactivateDirections();
        }
      },

      _show: function(){
        if(this._dijitDirections){
          this._resetByLastActiveState();
        }
      },

      _storeLastActiveState: function(){
        if(this._dijitDirections){
          this._active = this._dijitDirections.mapClickActive;
        }
      },

      _resetByLastActiveState: function(){
        if(this._dijitDirections){
          if(this._active){
            this._activateDirections();
          }
          else{
            this._deactivateDirections();
          }
          this._storeLastActiveState();
        }
      },

      _activateDirections: function() {
        if (this._dijitDirections) {
          if (typeof this._dijitDirections.activate === 'function') {
            this._dijitDirections.activate();//Deprecated at v3.13
          }
          if (typeof this._dijitDirections.mapClickActive !== "undefined") {
            this._dijitDirections.set("mapClickActive", true);
          }
          this._disableWebMapPopup();
        }
      },

      _deactivateDirections: function() {
        if (this._dijitDirections) {
          if (typeof this._dijitDirections.deactivate === 'function') {
            this._dijitDirections.deactivate();//Deprecated at v3.13
          }
          if (typeof this._dijitDirections.mapClickActive !== "undefined") {
            this._dijitDirections.set("mapClickActive", false);
          }
          this._enableWebMapPopup();
        }
      },
      _onDirectionsActivate: function () {
        if (this.config.defaultLocations &&
          this.config.defaultLocations.length && this.config.defaultLocations.length > 0 &&
          false === this._isInitPresetStopsFlag) {

          this._isInitPresetStopsFlag = true;
          this._dijitDirections.addStops(this.config.defaultLocations);
        }
      },

      isHasFrom: function () {
        var hasFrom = false;
        if (this._dijitDirections) {
          var stops = this._dijitDirections.stops;
          if (stops && stops.length >= 0) {
            var from = stops[0];
            if (("undefined" !== typeof from.name && "" !== from.name) ||
              ("undefined" !== typeof from.feature) ||
              ("undefined" !== typeof from.extent)) {
              hasFrom = true;
            }
          }
        }
        return hasFrom;
      },
      // isHasTo: function () {
      //   var hasTo = false;
      //   if (this._dijitDirections) {
      //     var stops = this._dijitDirections.stops;
      //     if (stops && stops.length === 1) {
      //       var to = stops[1];
      //       if (("undefined" !== typeof to.name && "" !== to.name) ||
      //         ("undefined" !== typeof to.feature) ||
      //         ("undefined" !== typeof to.extent)) {
      //         hasTo = true;
      //       }
      //     } else if (stops && stops.length > 1) {
      //       hasTo = true;
      //     }
      //   }
      //   return hasTo;
      // },
      // actionTo: function (geometry) {
      //   this.getDirectionsDijit().then(lang.hitch(this, function (directionsDijit) {
      //     //var stops = this._dijitDirections.stops;
      //     if (false === this.isHasFrom()) {
      //       directionsDijit.addStops(["", geometry]);
      //     } else {
      //       directionsDijit.addStop(geometry);
      //     }
      //   }), lang.hitch(this, function (err) {
      //     console.error(err);
      //   }));
      // },
      // actionFrom: function (geometry) {
      //   this.setStartStop(geometry);
      // }
      actionTo: function (geometry) {
        this.getDirectionsDijit().then(lang.hitch(this, function (directionsDijit) {
          var stops = this._getReplaceStops(directionsDijit, geometry, "last");
          directionsDijit.reset().then(lang.hitch(this, function () {
            directionsDijit.addStops(stops);
          }), lang.hitch(this, function (err) {
            console.error(err);
          }));
        }), lang.hitch(this, function (err) {
          console.error(err);
        }));
      },
      actionFrom: function (geometry) {
        this.getDirectionsDijit().then(lang.hitch(this, function (directionsDijit) {
          var stops = this._getReplaceStops(directionsDijit, geometry, "first");
          directionsDijit.reset().then(lang.hitch(this, function () {
            directionsDijit.addStops(stops);
          }), lang.hitch(this, function (err) {
            console.error(err);
          }));
        }), lang.hitch(this, function (err) {
          console.error(err);
        }));
      },
      _getReplaceStops: function (dijitDirections, geometry, place) {
        var stops = [];
        if (dijitDirections && dijitDirections.stops) {
          stops = lang.clone(dijitDirections.stops);//directionsDijit.reset() will clean stops now, so clone
          console.info("stops added", stops);
          if (place === "first") {
            //replace first stop ,as "From"
            stops[0] = geometry;
          } else if (place === "last") {
            if (false === this.isHasFrom()) {
              //if without start stop, leave a blank
              stops = ["", geometry];
            } else {
              //append a "To" stop
              stops.push(geometry);
            }
          }
        }
        return stops;
      },
      _toggleDartStyleByAppConfig: function () {
        var themeName = this.appConfig.theme.name;
        if ((themeName === "DashboardTheme" &&
          (this.appConfig.theme.styles[0] === 'default' || this.appConfig.theme.styles[0] === 'style3')) ||
          themeName === "DartTheme") {
          html.addClass(this.domNode, "dart-theme");
        } else {
          html.removeClass(this.domNode, "dart-theme");
        }
      }
    });
  });

View solution in original post

10 Replies
RobertScheitlin__GISP
MVP Esteemed Contributor

Jeff,

Have the stops show not only the stop number but also the address of the stop as well

What exactly do you mean? You want a label on the map to show the address?

0 Kudos
deleted-user-iwYtitJHBIyn
New Contributor II

Hi Robert,

That's correct.  By default the stops are labeled on the map 1...2...3...and so on in sequence.  I was wondering if it was possible to have the address that the building inspectors type in to create the stop could be also represented as a label next to the location as well.

Thanks.

Jeff 

0 Kudos
RobertScheitlin__GISP
MVP Esteemed Contributor

Jeff,

   The labels are added after the second address. Adjust the label properties in the _onDirectionsStart function.

Widget.js

///////////////////////////////////////////////////////////////////////////
// Copyright © 2014 - 2016 Esri. All Rights Reserved.
//
// Licensed under the Apache License Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///////////////////////////////////////////////////////////////////////////

define([
    'dojo/_base/declare',
    'jimu/BaseWidget',
    'esri/dijit/Directions',
    'esri/tasks/locator',
    'esri/tasks/RouteParameters',
    'esri/request',
    'esri/graphicsUtils',
    'esri/layers/ArcGISDynamicMapServiceLayer',
    'dojo/on',
    'dojo/_base/lang',
    'dojo/_base/html',
    'dojo/_base/array',
    'dojo/_base/config',
    'dojo/Deferred',
    'dojo/promise/all',
    'jimu/portalUtils',
    'esri/layers/GraphicsLayer',
    'esri/symbols/TextSymbol',
    'esri/symbols/Font',
    'esri/graphic',
    'esri/Color'
  ],
  function(declare, BaseWidget, Directions, Locator, RouteParameters, esriRequest, graphicsUtils,
    ArcGISDynamicMapServiceLayer, on, lang, html, array, dojoConfig, Deferred, all, portalUtils,
    GraphicsLayer, TextSymbol, Font, Graphic, Color) {

    return declare([BaseWidget], {
      name: 'Directions',
      baseClass: 'jimu-widget-directions',
      _dijitDirections:null,
      _routeTaskUrl: "//route.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World",
      _locatorUrl: "//geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer",
      _active: false,//save last active state
      _dijitDef: null,
      _trafficLayer: null,
      _isInitPresetStopsFlag: false,
      _stopsLabelGL: null,

      onOpen: function () {
        this._toggleDartStyleByAppConfig();
        this.widgetManager.activateWidget(this);
      },

      onClose: function(){
        this._hide();
      },

      onNormalize: function(){
        this._show();
      },

      onMinimize: function(){
        this._hide();
      },

      onMaximize: function(){
        this._show();
      },

      onDeActive: function(){
        this._deactivateDirections();
        this._enableWebMapPopup();
      },

      setStartStop: function(stop){
        console.info("stop added", stop);
        this.getDirectionsDijit().then(lang.hitch(this, function(directionsDijit){
          directionsDijit.reset().then(lang.hitch(this, function(){
            directionsDijit.addStop(stop);
          }), lang.hitch(this, function(err){
            console.error(err);
          }));
        }), lang.hitch(this, function(err){
          console.error(err);
        }));
      },

      addStop: function(stop){
        console.info("stop added", stop);
        this.getDirectionsDijit().then(lang.hitch(this, function(directionsDijit) {
          directionsDijit.addStop(stop);
        }), lang.hitch(this, function(err) {
          console.error(err);
        }));
      },

      getDirectionsDijit: function(){
        if(!this._dijitDef){
          this._dijitDef = new Deferred();
        }
        if(this._dijitDef.isFulfilled()){
          this._dijitDef = new Deferred();
        }
        if(this._dijitDirections){
          this._dijitDef.resolve(this._dijitDirections);
        }
        return this._dijitDef;
      },

      _handlePopup: function(){
        if(this.map.activeDirectionsWidget && this.map.activeDirectionsWidget.mapClickActive){
          this._disableWebMapPopup();
        }else{
          this._enableWebMapPopup();
        }
      },

      _disableWebMapPopup:function(){
        if(this.map){
          this.map.setInfoWindowOnClick(false);
        }
      },

      _enableWebMapPopup:function(){
        if(this.map){
          this.map.setInfoWindowOnClick(true);
        }
      },

      destroy: function(){
        if(this.map.activeDirectionsWidget === this._dijitDirections){
          this.map.activeDirectionsWidget = null;
        }
        if(this._trafficLayer){
          this.map.removeLayer(this._trafficLayer);
          this._trafficLayer = null;
        }
        this._handlePopup();
        this.inherited(arguments);
      },

      startup: function(){
        this.inherited(arguments);
        this.portal = portalUtils.getPortal(this.appConfig.portalUrl);

        this._stopsLabelGL = new GraphicsLayer();
        this.map.addLayer(this._stopsLabelGL);

        this._preProcessConfig().then(lang.hitch(this, function(){
          var routeParams = new RouteParameters();
          var routeOptions = this.config.routeOptions;
          if(routeOptions){
            if(routeOptions.directionsLanguage){
              routeParams.directionsLanguage = routeOptions.directionsLanguage;
            }
            else{
              routeParams.directionsLanguage = dojoConfig.locale || "en_us";
            }
            routeParams.directionsLengthUnits = routeOptions.directionsLengthUnits;
            routeParams.directionsOutputType = routeOptions.directionsOutputType;
            if(routeOptions.impedanceAttribute){
              routeParams.impedanceAttribute = routeOptions.impedanceAttribute;
            }
          }

          var options = {
            map: this.map,
            searchOptions: this.config.searchOptions,
            routeParams: routeParams,
            routeTaskUrl: this.config.routeTaskUrl,
            dragging: true,
            showClearButton: true,
            mapClickActive: true
          };

          //set units on root for API-3.20
          if(this.config.routeOptions && this.config.routeOptions.directionsLengthUnits) {
            options.directionsLengthUnits = this.config.routeOptions.directionsLengthUnits;
          }
          if(this.config.trafficLayerUrl){
            this._trafficLayer = new ArcGISDynamicMapServiceLayer(this.config.trafficLayerUrl);
            options.trafficLayer = this._trafficLayer;
            options.traffic = true;
          }else{
            options.traffic = false;
            options.showTrafficOption = false;
          }

          this.setDoNotFetchTravelModes(options).then(lang.hitch(this, function() {
            html.empty(this.directionController);
            var directionContainer = html.create('div', {}, this.directionController);
            //Only init Directions dijit when we can access the route task url.
            esriRequest({
              url: options.routeTaskUrl,
              content: {
                f: 'json'
              },
              handleAs: 'json',
              callbackParamName: 'callback'
            }).then(lang.hitch(this, function() {
              this._dijitDirections = new Directions(options, directionContainer);
              //html.place(this._dijitDirections.domNode, this.directionController);
              this._dijitDirections.startup();

              this.own(on(this._dijitDirections, 'load', lang.hitch(this, this._onDirectionsActivate)));
              this.own(on(this._dijitDirections, 'directions-start', lang.hitch(this, this._onDirectionsStart)));
              this.own(on(this._dijitDirections, 'directions-clear', lang.hitch(this, this._onDirectionsClear)));

              this.own(on(this._dijitDirections,
                'directions-finish',
                lang.hitch(this, this._onDirectionsFinish)));

              this.own(on(this._dijitDirections,
                'map-click-active',
                lang.hitch(this, this._handlePopup)));

              this._activateDirections();
              this._storeLastActiveState();

              if (this._dijitDef && !this._dijitDef.isFulfilled()) {
                this._dijitDef.resolve(this._dijitDirections);
              }
            }), lang.hitch(this, function(err) {
              console.log("Can't access " + options.routeTaskUrl, err);
            }));
          }), lang.hitch(this, function(err) {
            console.error(err);
          }));
        }));
      },

      onAppConfigChanged: function(appConfig){
        this.appConfig = appConfig;
        this._toggleDartStyleByAppConfig();
      },

      _onDirectionsClear: function(evt){
        if(this._stopsLabelGL){
          this._stopsLabelGL.clear();
        }
      },

      _onDirectionsStart: function(evt){
        if(evt.target.stops && evt.target.stops.length > 0 && this._stopsLabelGL){
          this._stopsLabelGL.clear();
          var stops = evt.target.stops;
          var font = new Font("12px", Font.STYLE_NORMAL, Font.VARIANT_NORMAL, Font.WEIGHT_BOLD);
          array.map(stops, lang.hitch(this, function(stop){
            if ("undefined" !== typeof stop.name && "" !== stop.name){
              var textSymbol = new TextSymbol(stop.name, font, new Color([0, 0, 0]));
              textSymbol.horizontalAlignment = "left";
              textSymbol.setOffset(12, 10);
              var labelPointGraphic = new Graphic(stop.feature.geometry, textSymbol);
              this._stopsLabelGL.add(labelPointGraphic);
            }
          }));
        }
      },

      _onDirectionsFinish: function(evt){
        if(evt && evt.result){
          var routeResults = evt.result.routeResults;
          if(lang.isArrayLike(routeResults) && routeResults.length > 0){
            var routes = [];
            array.forEach(routeResults, function(routeResult){
              if(routeResult.route){
                routes.push(routeResult.route);
              }
            });
            if(routes.length > 0){
              var ext = null;
              try{
                ext = graphicsUtils.graphicsExtent(routes);
                if(ext){
                  ext = ext.expand(1.3);
                }
              }catch(e){
                console.log(e);
              }
              if(ext){
                this.map.setExtent(ext);
              }
            }
          }
        }
      },

      _preProcessConfig:function(){
        if(!this.config.geocoderOptions){
          this.config.geocoderOptions = {};
        }
        if(!(this.config.geocoderOptions.geocoders &&
         this.config.geocoderOptions.geocoders.length > 0)){
          this.config.geocoderOptions.geocoders = [{
            url: '',
            placeholder: ''
          }];
        }

        var placeholder = this.config.geocoderOptions.geocoders[0].placeholder;

        if(!placeholder){
          if(!this.config.routeTaskUrl){
            //user doesn't open the setting page, we use the default placeholder
            placeholder = this.nls.searchPlaceholder;
          }
        }

        this.config.searchOptions = {
          enableSuggestions: this.config.geocoderOptions.autoComplete,
          maxSuggestions: this.config.geocoderOptions.maxLocations,
          minCharacters: this.config.geocoderOptions.minCharacters,
          suggestionDelay: this.config.geocoderOptions.searchDelay,
          sources: [{
            locator: null,
            name: '',
            singleLineFieldName: '',
            outFields: ["*"],
            placeholder: placeholder
          }]
        };

        var def = new Deferred();
        all([this._getRouteTaskUrl(), this._getLocatorUrl()]).then(
          lang.hitch(this, function(results){
          this.config.routeTaskUrl = results[0];

          this.config.routeTaskUrl = this._replaceRouteTaskUrlWithAppProxy(this.config.routeTaskUrl);

          var locatorUrl = results[1];
          esriRequest({
            url: locatorUrl,
            hanleAs:'json',
            content:{
              f:'json'
            },
            callbackParamName:'callback'
          }).then(lang.hitch(this, function(geocodeMeta){
            this.config.searchOptions.sources[0].locator = new Locator(locatorUrl);
            this.config.searchOptions.sources[0].name = geocodeMeta.serviceDescription || '';
            this.config.searchOptions.sources[0].singleLineFieldName =
             geocodeMeta.singleLineAddressField && geocodeMeta.singleLineAddressField.name || '';
            def.resolve();
          }), lang.hitch(this, function(err){
            console.error(err);
            def.reject();
          }));
        }), lang.hitch(this, function(err){
          console.error(err);
          def.reject();
        }));
        return def;
      },

      _replaceRouteTaskUrlWithAppProxy: function(routeTaskUrl){
        // Use proxies to replace the routeTaskUrl
        var ret = routeTaskUrl;
        if(!window.isBuilder && !this.appConfig.mode &&
            this.appConfig.appProxies && this.appConfig.appProxies.length > 0) {
          array.some(this.appConfig.appProxies, function(proxyItem) {
            if(routeTaskUrl === proxyItem.sourceUrl) {
              ret = proxyItem.proxyUrl;
              return true;
            }
          });
        }
        return ret;
      },

      _getRouteTaskUrl: function(){
        var def = new Deferred();
        if(this.config.routeTaskUrl){
          def.resolve(this.config.routeTaskUrl);
        }
        else{
          this.portal.loadSelfInfo().then(lang.hitch(this, function(response){
            if(response && response.helperServices && response.helperServices.route){
              def.resolve(response.helperServices.route.url);
            }
            else{
              def.resolve(this._routeTaskUrl);
            }
          }), lang.hitch(this, function(err){
            console.error(err);
            def.resolve(this._routeTaskUrl);
          }));
        }
        return def;
      },

      _getLocatorUrl: function(){
        var def = new Deferred();
        var geocodeArgs = this.config.geocoderOptions &&
         this.config.geocoderOptions.geocoders &&
          this.config.geocoderOptions.geocoders[0];
        var url = geocodeArgs && geocodeArgs.url;
        if(url){
          def.resolve(url);
        }
        else{
          this.portal.loadSelfInfo().then(lang.hitch(this, function(response){
            if(response && response.helperServices &&
             response.helperServices.geocode &&
              response.helperServices.geocode.length > 0){
              var geocode = response.helperServices.geocode[0];
              def.resolve(geocode.url);
            }
            else{
              def.resolve(this._locatorUrl);
            }
          }), lang.hitch(this, function(err){
            console.error(err);
            def.resolve(this._locatorUrl);
          }));
        }
        return def;
      },

      setDoNotFetchTravelModes: function(options) {
        var def = new Deferred();
        if (this.config.travelModesUrl) {
          options.travelModesServiceUrl = this.config.travelModesUrl;
          options.doNotFetchTravelModesFromOwningSystem = false;
          def.resolve();
        } else {
          this._getTravelModesUrlVersion().then(lang.hitch(this, function(version) {
            if (version && version >= 10.4) {
              options.doNotFetchTravelModesFromOwningSystem = false;
            } else {
              options.doNotFetchTravelModesFromOwningSystem = true;
            }
            def.resolve();
          }), lang.hitch(this, function(err){
            def.reject(err);
          }));
        }
        return def;
      },
      _getTravelModesUrlVersion: function() {
        var def = new Deferred();
        esriRequest({
          url: this.config.routeTaskUrl,
          content: {
            f: 'json'
          },
          handleAs: 'json',
          callbackParamName: 'callback'
        }).then(lang.hitch(this, function(results) {
          def.resolve(results.currentVersion);
        }), lang.hitch(this, function(err) {
          console.log("Can't access " + this.config.routeTaskUrl, err);
          def.reject(err);
        }));

        return def;
      },

      _hide: function(){
        if(this._dijitDirections){
          this._storeLastActiveState();
          this._deactivateDirections();
        }
      },

      _show: function(){
        if(this._dijitDirections){
          this._resetByLastActiveState();
        }
      },

      _storeLastActiveState: function(){
        if(this._dijitDirections){
          this._active = this._dijitDirections.mapClickActive;
        }
      },

      _resetByLastActiveState: function(){
        if(this._dijitDirections){
          if(this._active){
            this._activateDirections();
          }
          else{
            this._deactivateDirections();
          }
          this._storeLastActiveState();
        }
      },

      _activateDirections: function() {
        if (this._dijitDirections) {
          if (typeof this._dijitDirections.activate === 'function') {
            this._dijitDirections.activate();//Deprecated at v3.13
          }
          if (typeof this._dijitDirections.mapClickActive !== "undefined") {
            this._dijitDirections.set("mapClickActive", true);
          }
          this._disableWebMapPopup();
        }
      },

      _deactivateDirections: function() {
        if (this._dijitDirections) {
          if (typeof this._dijitDirections.deactivate === 'function') {
            this._dijitDirections.deactivate();//Deprecated at v3.13
          }
          if (typeof this._dijitDirections.mapClickActive !== "undefined") {
            this._dijitDirections.set("mapClickActive", false);
          }
          this._enableWebMapPopup();
        }
      },
      _onDirectionsActivate: function () {
        if (this.config.defaultLocations &&
          this.config.defaultLocations.length && this.config.defaultLocations.length > 0 &&
          false === this._isInitPresetStopsFlag) {

          this._isInitPresetStopsFlag = true;
          this._dijitDirections.addStops(this.config.defaultLocations);
        }
      },

      isHasFrom: function () {
        var hasFrom = false;
        if (this._dijitDirections) {
          var stops = this._dijitDirections.stops;
          if (stops && stops.length >= 0) {
            var from = stops[0];
            if (("undefined" !== typeof from.name && "" !== from.name) ||
              ("undefined" !== typeof from.feature) ||
              ("undefined" !== typeof from.extent)) {
              hasFrom = true;
            }
          }
        }
        return hasFrom;
      },
      // isHasTo: function () {
      //   var hasTo = false;
      //   if (this._dijitDirections) {
      //     var stops = this._dijitDirections.stops;
      //     if (stops && stops.length === 1) {
      //       var to = stops[1];
      //       if (("undefined" !== typeof to.name && "" !== to.name) ||
      //         ("undefined" !== typeof to.feature) ||
      //         ("undefined" !== typeof to.extent)) {
      //         hasTo = true;
      //       }
      //     } else if (stops && stops.length > 1) {
      //       hasTo = true;
      //     }
      //   }
      //   return hasTo;
      // },
      // actionTo: function (geometry) {
      //   this.getDirectionsDijit().then(lang.hitch(this, function (directionsDijit) {
      //     //var stops = this._dijitDirections.stops;
      //     if (false === this.isHasFrom()) {
      //       directionsDijit.addStops(["", geometry]);
      //     } else {
      //       directionsDijit.addStop(geometry);
      //     }
      //   }), lang.hitch(this, function (err) {
      //     console.error(err);
      //   }));
      // },
      // actionFrom: function (geometry) {
      //   this.setStartStop(geometry);
      // }
      actionTo: function (geometry) {
        this.getDirectionsDijit().then(lang.hitch(this, function (directionsDijit) {
          var stops = this._getReplaceStops(directionsDijit, geometry, "last");
          directionsDijit.reset().then(lang.hitch(this, function () {
            directionsDijit.addStops(stops);
          }), lang.hitch(this, function (err) {
            console.error(err);
          }));
        }), lang.hitch(this, function (err) {
          console.error(err);
        }));
      },
      actionFrom: function (geometry) {
        this.getDirectionsDijit().then(lang.hitch(this, function (directionsDijit) {
          var stops = this._getReplaceStops(directionsDijit, geometry, "first");
          directionsDijit.reset().then(lang.hitch(this, function () {
            directionsDijit.addStops(stops);
          }), lang.hitch(this, function (err) {
            console.error(err);
          }));
        }), lang.hitch(this, function (err) {
          console.error(err);
        }));
      },
      _getReplaceStops: function (dijitDirections, geometry, place) {
        var stops = [];
        if (dijitDirections && dijitDirections.stops) {
          stops = lang.clone(dijitDirections.stops);//directionsDijit.reset() will clean stops now, so clone
          console.info("stops added", stops);
          if (place === "first") {
            //replace first stop ,as "From"
            stops[0] = geometry;
          } else if (place === "last") {
            if (false === this.isHasFrom()) {
              //if without start stop, leave a blank
              stops = ["", geometry];
            } else {
              //append a "To" stop
              stops.push(geometry);
            }
          }
        }
        return stops;
      },
      _toggleDartStyleByAppConfig: function () {
        var themeName = this.appConfig.theme.name;
        if ((themeName === "DashboardTheme" &&
          (this.appConfig.theme.styles[0] === 'default' || this.appConfig.theme.styles[0] === 'style3')) ||
          themeName === "DartTheme") {
          html.addClass(this.domNode, "dart-theme");
        } else {
          html.removeClass(this.domNode, "dart-theme");
        }
      }
    });
  });

View solution in original post

deleted-user-iwYtitJHBIyn
New Contributor II

Thank you, Robert.

As a follow-up question...

This application was not created within WAB Developers Edition.  Instead I created it within AGO.

I've modified code in server/app folders by locating the respective Widget.js file but never with one created on AGO.

Where would that Widget.js for the Direction widget be located?

Jeff

0 Kudos
RobertScheitlin__GISP
MVP Esteemed Contributor

Jeff,

   The WAB directory structure is pretty easy to navigate. It would be /widgets/Directions.

0 Kudos
deleted-user-iwYtitJHBIyn
New Contributor II

Thank you so much, Robert!

Perfect.

Jeff

0 Kudos
Rohit_Venkat_GandhiMendadhala
Occasional Contributor

Robert, My apologies for involving in this discussion. Since my query is also related to the same widget and similar query, I would like to ask here.

Is there a way we can add different place holders for the start and stop in the directions widget here.

I tried modifying the placeholders in this folder

Application\widgets\Directions\nls\strings.js

0 Kudos
RobertScheitlin__GISP
MVP Esteemed Contributor

Rohit,

   You can do one of two things.

  1. In the widgets config.json file (i.e. configs/Directions/config__*.json) delete the geocoders > placeholder property
  2. Or in the config.json change the geocoders > placeholder property.
0 Kudos
Rohit_Venkat_GandhiMendadhala
Occasional Contributor

Thanks Robert, but how do I keep two different place holders like "Start" in one place and "Stop" in the other.

For example in Google Maps I have 

The 3rd add destination is available only after we fill in the first two.

0 Kudos