Select to view content in your preferred language

Directions Widget Modification?

2844
11
Jump to solution
08-03-2017 09:55 AM
deleted-user-iwYtitJHBIyn
Deactivated User

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 Emeritus

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

11 Replies
RobertScheitlin__GISP
MVP Emeritus

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
Deactivated User

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 Emeritus

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");
        }
      }
    });
  });
deleted-user-iwYtitJHBIyn
Deactivated User

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 Emeritus

Jeff,

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

0 Kudos
deleted-user-iwYtitJHBIyn
Deactivated User

Thank you so much, Robert!

Perfect.

Jeff

0 Kudos
JoshV
by
Regular Contributor

Hi Robert, this is great to show us how to place addresses labels next to each stop in the route.  Maybe I didn't copy the code correctly but I notice I can't use the print button in the Directions widget after pasting your code in Widget.js.  Besides that, my real issue is that popup type html page that out of the box directions widget uses.  For the life of me, I cannot access/change any of the logo's or text on that special html page from print button in Directions widget.  here is my post: Directions Widget, modify Logo at top of Print Res... - Esri Community

0 Kudos
RVG296
by
Regular 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 Emeritus

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