Currently, we want to create a custom widget to draw polygon on the map(arcgis web appbuilder 1.3). But it shows that the Uncaught TypeError: Can not read property 'activate' of undefined. (In Widget.js file )
The related line is drawToolbar.activate(esri.toolbars.Draw.POLYGON);
Here is my code:
define(['dojo/_base/declare',
'jimu/BaseWidget',
'esri/urlUtils',
'esri/config',
'esri/map',
'esri/graphic',
'esri/tasks/RouteTask',
'esri/tasks/RouteParameters',
'esri/tasks/FeatureSet',
'esri/symbols/SimpleMarkerSymbol',
'esri/symbols/SimpleLineSymbol',
'esri/Color',
'dojo/_base/array',
'dojo/on',
'dojo/dom',
'dojo/domReady!',
'dijit/registry',
'dijit/layout/BorderContainer',
'dijit/layout/ContentPane',
'dijit/form/HorizontalSlider',
'dijit/form/HorizontalRuleLabels',
'esri/toolbars/draw',
'esri/tasks/GeometryService',
'esri/symbols/SimpleFillSymbol',
'esri/tasks/DataFile',
'dojo/_base/connect'
],
function(declare, BaseWidget,urlUtils, esriConfig, Map, Graphic, RouteTask, RouteParameters,
FeatureSet, SimpleMarkerSymbol, SimpleLineSymbol,
Color, array, on, dom, registry,
Draw, GeometryService,SimpleFillSymbol,DataFile,connect
) {
return declare([BaseWidget], {
baseClass: 'jimu-widget-MyWidget',
name: 'MyWidget',
startup: function(){
var map, Draw, routeTask, routeParams, routes= [];
var drawToolbar;
var geometryService;
var stopSymbol, barrierSymbol, polygonBarrierSymbol,routeSymbols;
var mapOnClick_addStops_connect, mapOnClick_addBarriers_connect,mapOnClick_addPolygonBarriers_connect;
geometryService = new GeometryService("https://utility.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer");
map = this.map;
routeTask = new RouteTask("https://route.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World");
routeParams = new RouteParameters();
routeParams.polygonBarriers = new FeatureSet();
routeParams.outSpatialReference = {"wkid":102100};
var polygonBarrierSymbol= new esri.symbol.SimpleFillSymbol();
routeSymbols = {
"Route 1": new SimpleLineSymbol().setColor(new Color([0,0,255,0.5])).setWidth(5),
"Route 2": new SimpleLineSymbol().setColor(new Color([0,255,0,0.5])).setWidth(5),
"Route 3": new SimpleLineSymbol().setColor(new Color([255,0,255,0.5])).setWidth(5)
};
on(dom.byId("addpolygonBarriersBtn"), "click", addpolygonBarriers);
on(dom.byId("clearpolygonBarriersBtn"), "click", clearpolygonBarriers);
function createToolBar(){
drawToolbar = new esri.toolbars.Draw(map);
drawToolbar.on("draw-end", addToMap);
}
function activateTool(){
drawToolbar.activate(esri.toolbars.Draw.POLYGON);
map.hideZoomSlider();
}
function addToMap(evt) {
drawToolbar.deactivate();
routeParams.polygonBarriers.features.push(
map.graphics.add(new esri.Graphic(evt.geometry, polygonBarrierSymbol)) );
}
var node = dojo.byId("addpolygonBarriersBtn");
dojo.connect(node, "click", activateTool);
map.on("load", createToolBar);
//Begins listening for click events to add polygonbarriers
function addpolygonBarriers() {
removeEventHandlers();
mapOnClick_addPolygonBarriers_connect = on(map, "onclick", addPolygonBarrier);
}
//Clears all polybarriers
function clearpolygonBarriers() {
removeEventHandlers();
for (var i=routeParams.polygonBarriers.features.length-1; i>=0; i--) {
map.graphics.remove(routeParams.polygonBarriers.features.splice(i, 1)[0]);
}
}
// Add Polygon
function addPolygonBarrier(evt) {
drawToolbar.activate(esri.toolbars.Draw.POLYGON);
var drawEnd_connect = dojo.connect(drawToolbar, "onDrawEnd", function(geometry) {
routeParams.polygonBarriers.features.push( map.graphics.add(new esri.Graphic(geometry, polygonBarrierSymbol)) );});
}
function removeEventHandlers() {
if (mapOnClick_addPolygonBarriers_connect) {
mapOnClick_addPolygonBarriers_connect.remove();
}
}
}
});
});
Solved! Go to Solution.
Juxuan,
OK here is the edited and fully working version of your widget:
Junxuan,
Your biggest issue is that you have your requires and their subsequent parameters out of order. You should review this blog:
The abc’s of AMD | ArcGIS Blog
Each require has a parameter that the require is called by
i.e. "esri/map", "esri/toolbars/draw"],function(Map, Draw){
notice Map is first then Draw and the parameters match the same order.
dojo/domReady! should always be last in your requires and layout elements like 'dijit/layout/BorderContainer' that are used in your trmplate html but not in your js code should be right before dojo/domReady!
Your next issue is mixing Legacy and AMD Style coding (i.e. esri.toolbar.draw is Legacy). The parameter Draw is all you need to use when coding against the require for esri/toolbars/draw.
When building widgets you reference dom elements in the template by using data-dojo-attach-point="blah" and not id="blah". So there is no need for dom.byId("blah") instead you use this.blah.
If you are going to use dojo layout or form dijits in your template html then you need to have the require for 'dijit/_WidgetsInTemplateMixin' and it needs to be added to your declare too.
return declare([BaseWidget, _WidgetsInTemplateMixin], {
Here is a version of what you had using esri standard widget development practices:
Hi Robert,
Thanks for your detail explanation and example.
Our final goal is to find the routes but avoid certain area or specific points. Currently, the widget works well except the routes can not be showed on the map. The error is can not read property 'push' of undefined in showRoute function(this.routes.push).
Attached is our code.
Thanks in advanced!
Junxuan
Junxuan,
This issue is that your array loop is out of scope and does not know what this.routes is. Adding lang.hitch as shown in line 4 will fix that.
showRoute:function(evt) { this.clearRoutes(); array.forEach(evt.result.routeResults, lang.hitch(this, function(routeResult, i) { this.routes.push( this.map.graphics.add( routeResult.route.setSymbol(this.routeSymbols[routeResult.routeName]) ) ); }));
Don't forget to mark this thread as answered by clicking on the correct answer link on the thread that answered your question.
Hi Robert:
Thanks for your information. I modified the showRoutes function code, but it still can not show the routes, even though it seems that there is no error.
showRoute:function(evt) {
this.clearRoutes();
array.forEach(evt.result.routeResults, lang.hitch(this, function(routeResult, i) {
this.routes.push(
this.map.graphics.add(
routeResult.route.setSymbol(this.routeSymbols[routeResult.routeName])
)
);
}));
var msgs = ["Server messages:"];
array.forEach(evt.result.messages, lang.hitch(function(message) {
msgs.push(message.type + " : " + message.description); !
}));
if (msgs.length > 1) {
alert(msgs.join("\n - "));
}
}
Junxuan,
So does the alert ever display? Looking at your code and the console screenshot it looks like you are not providing a token for the RouteTask.
Hi Robert,
I think I have provided a token for the Route Task. Do you mean the Interface like this?
Thanks,
Junxuan
Yes, I did not see any identityManager stuff in your code.
Are you getting the Alert that you have setup in your code? If not then you are not getting any results to add to the map.
Currently, it is difficult for us to get your points.
1. What is Alert? How can I check that?
2. What does "identity manager stuff in code" mean? When I click the Solve Route Button, there is a pop-up window which asks me to enter the account information and that's it.
Thanks,
Junxuan
Junxuan,
In your code you have:
showRoute:function(evt) {
this.clearRoutes();
array.forEach(evt.result.routeResults, lang.hitch(this, function(routeResult, i) {
this.routes.push(
this.map.graphics.add(
routeResult.route.setSymbol(this.routeSymbols[routeResult.routeName])
)
);
}));
var msgs = ["Server messages:"];
array.forEach(evt.result.messages, lang.hitch(function(message) {
msgs.push(message.type + " : " + message.description);
}));
if (msgs.length > 1) {
alert(msgs.join("\n - "));
}
},
If you are getting the login popup (identityManager) already then you can ignore that comment of mine.