Liferay portlet ESRI esri.map.graphics init timing issue

1121
1
10-13-2010 08:58 AM
BrianFrutchey
New Contributor
I have a liferay portlet which is rendering a map using the javascript api v2.0, and then plotting points on the map.  I am using an ajax call to update the map as user queries change, and have an issue in firefox when my first ajax render call is made (in IE8 it works fine).  The problem is that I call esri.map.graphics.clear before adding each new point, and my javascript throws an exception at that call stating (in firebug) "this.esriMap.graphics is null".  My graphics manipulation and esri.map initialization is happening in a js file called by my view.jsp.  If I place an alert() just prior to the object call to init my esri.map, which is done just prior to making the ajax call to reload the graphics, this problem disappears.  If I place the alert after the init but prior to the ajax call the problem still exists.  I have tried writing wait loops to wait for the graphics obj to init, but they wait forever.  Again, the same code works fine in IE.  Thoughts?  Some of my code:

view.jsp:

e<portlet:namespace/> = new MyObj.Portlets.EsriMap('<portlet:namespace/>', ${showMarkers});
       e<portlet:namespace/>.baseApplyGeoUrl = "<c:out value="${geoActionUrl}" escapeXml="false" />";
       e<portlet:namespace/>.baseGetTileUrl = "<c:out value="${getTileResourceUrl}" escapeXml="false" />";
       e<portlet:namespace/>.latLngParamName = "<c:out value="${LatLngParamName}" escapeXml="false" />";
       e<portlet:namespace/>.latLngValueParamName = "<c:out value="${LatLngValueParamName}" escapeXml="false" />";
       e<portlet:namespace/>.radiusParamName = "<c:out value="${radiusParamName}" escapeXml="false" />";
       e<portlet:namespace/>.baseGetDataUrl = "<c:out value="${getDataResourceUrl}" escapeXml="false" />";
       e<portlet:namespace/>.analyticsBased = "<c:out value="${analyticsBased}" escapeXml="false" />";
       e<portlet:namespace/>.filterOnMove = ${enableFilterOnMove};
       
       e<portlet:namespace/>.initMap("heatmap_map_<portlet:namespace/>", "${geoCodeProperty}", ${prefsHeatMap}, "${esriMapService}");
     e<portlet:namespace/>.testAsync = function(){
      if(typeof(asyncObj<portlet:namespace/>) != 'undefined'){
       e<portlet:namespace/>.asyncObject = asyncObj<portlet:namespace/>;
       e<portlet:namespace/>.reloadMap(); 
      }else{
       setTimeout("e<portlet:namespace/>.testAsync();", 100);
      }
       };
       e<portlet:namespace/>.testAsync();


back-end js

  initMap: function(mapElement, geocodeField, enableHeatMap, esriTiledMapService, renderDataFunc) {
  //TODO - do we need to check for existing map and clear it?
 
  this.geoCodeField = geocodeField;
    this.enableHeatMap = enableHeatMap;
    this.mapElement = mapElement;
    
  this.esriMap = new esri.Map(mapElement);
  var mapLayer = new esri.layers.ArcGISTiledMapServiceLayer(esriTiledMapService);
  this.esriMap.addLayer(mapLayer);
     dojo.connect(this.esriMap, "onLoad", this.initToolbar());
  //ASYNC should do below calls as it sets data elements on page
  //this.autozoom();
       //this.plotmarkers();
     this.enableGeoFilter = false;
  jQuery('#modeButton_' + this.portletId).addClass("buttonUnClicked");
  jQuery('#modeButton_' + this.portletId).removeClass("buttonClicked");
   },
   
   initToolbar: function() {
  this.esriToolbar = new esri.toolbars.Draw(this.esriMap);

    var instance = this;
  // Set range filter event handler...
        dojo.connect(this.esriToolbar, "onDrawEnd", function (geometry) {
         instance.toggleSelectionMode();
        var symbol = instance.esriToolbar.fillSymbol;
        instance.esriMap.graphics.add(new esri.Graphic(geometry, symbol));
        
      var centerLat = (geometry.ymax+geometry.ymin)/2;
      var centerLng = (geometry.xmax+geometry.xmin)/2;
      var radius = instance.distanceBetween(centerLat, centerLng, geometry.ymin, geometry.xmin);
          instance.applyGeoFilter(instance.geoCodeField, centerLat, centerLng, radius,
            "("+geometry.ymin+","+geometry.xmin+", "+geometry.ymax+","+geometry.xmax+")");
       });
   },

   plotmarkers: function(){
     try {
      this.esriMap.graphics.clear();

      var dataPoints = jQuery('#' + this.portletId).data('dataPoints');
      if(!dataPoints){
       setTimeout("e"+this.portletId+".plotmarkers()",500);
         return false;
        }
 
      //var symbol = new esri.symbol.PictureMarkerSymbol('./images/marker.gif', 13, 13);
      var symbol = new esri.symbol.SimpleMarkerSymbol(esri.symbol.SimpleMarkerSymbol.STYLE_CIRCLE, 15,
        new esri.symbol.SimpleLineSymbol(), new dojo.Color([255,255,0,0.5]));
 
      for (var i=1;i<dataPoints.length;i++){
       var marker=dataPoints;
       if (marker.lat==''||marker.lon=='') continue;
       if (marker.lon>180 || marker.lon<-180) continue;
       if (marker.lat<0) continue;
       
       var lat=parseFloat(marker.lat);
       var lon=parseFloat(marker.lon);
       
       if (lat!=marker.lat||lon!=marker.lon) continue;
       
       var geometry=new esri.geometry.Point(lon,lat, new esri.SpatialReference({ wkid: 4326 }));
       var info = new esri.InfoTemplate("<div class=\"popUpDiv\"><pre>"+marker.popupString + "</pre></div>");
       //var info = new esri.InfoTemplate("Record: "+marker.lbl, "<a href=\""+marker.url+"\">"+marker.lbl+"</a>");
       var graphic = new esri.Graphic(geometry, symbol);
       graphic.setInfoTemplate(info);
       this.esriMap.graphics.add(graphic);  
      }    
         //initialize onmouseover and onmouseout listeners for markers
      var map = this.esriMap;
         dojo.connect(this.esriMap.graphics, "onMouseOver", function(evt) {
             var overGraphic = evt.graphic;
             map.infoWindow.setTitle(overGraphic.getTitle());
             map.infoWindow.setContent(overGraphic.getContent()+"<br>lat,lon: " + evt.mapPoint.y + "," + evt.mapPoint.x);
             map.infoWindow.show(evt.screenPoint,map.getInfoWindowAnchor(evt.mapPoint));
         });
         dojo.connect(this.esriMap.graphics, "onMouseOut", function(evt) {
             map.infoWindow.hide();
         });

        this.asyncObject.stopSpinner();
       }
       catch(e)
       {
        this.asyncObject.stopSpinner();
        if(console)
         console.log(e);
       }  
    },
    
   reloadMap: function() {
    this.asyncObject.startSpinner();
    jQuery('#' + this.portletId).removeData('dataPoints'); 
    this.updateMapContent();
   },
   
   updateMapContent: function() { 
    // Trigger AJAX request
  var url = this.baseGetDataUrl;
  var instance = this;
  jQuery.ajax({
   url:url,
   cache: false,
   type: "GET",
   data:this.callbackParamName + "=?&" +
    this.latLngParamName + "=" + this.geoCodeField +
    "&PortletId=" + this.portletId+"&reloadType="+this.reloadType,
   success: function(data) {
    var jData = jQuery("<div/>").html(data);
    var paging = jData.find(".ResultsPagination");
    var vizContainer = jQuery("#container_" + instance.portletId);
    if(instance.showMarkers){
     vizContainer.find(".ResultsPagination").empty().append(paging.children());
     var resultsControls = jData.find(".ResultsControls");
     vizContainer.find(".ResultsControls").empty().append(resultsControls.children());
    }
    var results = jData.find(".encodedResults");
    vizContainer.find(".encodedResults").empty().append(results.children());
    
    instance.plotmarkers();
    instance.autozoom();
      }
  }); 
   }
   
0 Kudos
1 Reply
BrianFrutchey
New Contributor
I have more details to add from further investigation.  It seems that the 'onLoad' event for the esri.map is not being properly tracked by dojo.  Any dojo.connect(map, "onLoad", function(){}) will have the callback function executed immediately, even when the map.loaded is false.  The only thing that seems to be able to resolve this issue is putting an alert() anywhere after the loading of the esri javascript API and before the jQuery ajax call I use to get the graphics data (inside updateMapContent).  Putting the alert before the api js loads or after my ajax method runs do not affect the error.  setTimeout loops and even asynch ajax calls in the same places I put alerts will loop forever waiting for map.loaded to be true....  Why can an alert() fix this problem but nothing else I try?
0 Kudos