I have a Web Mapping Application with a standard Print Widget. I would like to include the contents of the popup window in the printout.
Here is an example:
GIS Portal for the City of Abilene, TX
Standard printout:
Popup window:
Desired printout:
My research so far:
There is a BLOG POST by esri explaining how to do this. This post shows 2 methods. The first method is not suitable for my purposes cause it requires manual data entries for each printout. The second method seems incomplete, Format & Layout_Template parameters are missing. Print does not work even if I add those parameters manually before publishing the service. If the second method worked properly, I would have modified it to match my needs, unfortunately it doesn't.
http://stackoverflow.com/questions/32363943/arcgis-javascript-web-map-printout-with-popup-shown
dojoOn(map.infoWindow, "selection-change", function(){ //build custom text here }
My knowledge on JavaScript is limited, so I couldn't manage what he explained here. Where am I supposed to add this and what should I include in the function?
We have the print button generating an html page that pulls data from the database and formats it in pretty much an exact replica of the .mxd we were using. For the map it uses a function on the rest endpoint to generate an image from the map service.
This seems even more complicated than the others.
Either one the above solutions works for me.
TL;DR I want to include popup window or it's contents in the printout.
Thanks in advance.
Çankaya,
My method to get this done is to use method 1 in the blog post but also automatically feed those custom text fields in the print widget using a feature from a custom GraphicsLayer in the map.
So I have a custom search widget that selects my parcel and that widget creates a graphics layer in the map with all the attributes I need. The WAB print widget (which has been customized) then looks for that graphics layer and if it finds it it takes those attributes and adds then to the print layout customTextElements array.
Here is one thread:
Pushing Data From eSearch to Custom Print Template
Here is a thread where I show my code changes to the print widget for this:
How to print selected attributes on parcels in WAB application
Robert, thank you for your response
Let me go through what I've been doing:
template.showAttribution = false;
//See if there is a parcel search layer added to the map var plyr; array.some(this.map.graphicsLayerIds, lang.hitch(this, function (layerId) { var lyr = this.map.getLayer(layerId); if(lyr.name === "Search Results: Parcels"){ plyr = lyr; return true; } })); if(plyr){ var xppins = "", obj, cTextElements = []; array.map(plyr.graphics, lang.hitch(this, function(gra, index){ if (plyr.graphics.length === 1){ obj = {DistrictName: "District Name: " + gra.attributes.NAME}; cTextElements.push(obj); // obj = {PPIN: "PPIN: " + gra.attributes.PPIN}; // cTextElements.push(obj); obj = {DistrictId: "District Id: " + gra.attributes.ID}; cTextElements.push(obj); // obj = {StreetAdd: "Street Address: " + gra.attributes.STREET_ADDRESS}; // cTextElements.push(obj); }else if (plyr.graphics.length > 1){ // if (xppins === ""){ // xppins = gra.attributes.PPIN; // } else { // if([7,16,25,34,43,52,61,70,79,88,97].indexOf(index) > -1){ // xppins = xppins + ",\r\n" + gra.attributes.PPIN; // }else{ // xppins = xppins + ", " + gra.attributes.PPIN; // } // } // obj = {OwnerName: "Selected PPINs: " + xppins}; // cTextElements.push(obj); } template.layoutOptions.customTextElements = cTextElements; })); } //end my change
Am I missing anything? And how can I include eSearch in this procedure?
Thanks
Çankaya,
You are setting a custom text element called "DistrictId" and "DistrictName", but from your step one you only say you have custom text elements called cte_1 and cte_2. Your names need to match.
To include the eSearch (which is a must for the way you are doing the code) you just need to have a search layer called "Parcels" added to the eSearch search layers or you need to change:
if(lyr.name === "Search Results: Parcels"){
Robert,
Thank you for the information.
I changed Element Names to "DistrictId" and "DistrictName", added eSearch widget and added a search layer titled "Parcels". As Search URL, corresponding layer's MapServer URL is used. Included All Fields. However, text fields in the prinout remain unchanged.
By the way, I am using Web AppBuilder for ArcGIS (Developer Edition) 2.2
Çankaya,
So your Parcels search layer does have a "NAME" and a "ID" field (with the field name being all uppercase)?
Robert,
Yes the layer have those fields.
Layer fields: NAME & ID
Text element names: DistrictId & DistrictName
Print.js:
obj = {DistrictName: "District Name: " + gra.attributes.NAME}; obj = {DistrictId: "District Id: " + gra.attributes.ID};
eSearch:
Add a Search Layer
Title: Parcels
URL: Layer's MapServer URL
Included Fields: Name & ID
I am selecting a feature, popup is shown, then I print. In the printout the feature is selected but text fields are unchanged.
Çankaya,
you need to do some debugging then place some console.info("something"); statements into the code to see if you are getting to those functions
Robert,
Print.js console warning
"Get Layout Templates Info Error"
LayerInfos.getInstance(this.map, this.map.itemInfo) .then(lang.hitch(this, function(layerInfosObj) { this.layerInfosObj = layerInfosObj; return all([this._getPrintTaskInfo(), this._getLayerTemplatesInfo()]) .then(lang.hitch(this, function(results) { var taksInfo = results[0], templatesInfo = results[1]; if (templatesInfo && !templatesInfo.error) { var parameters = templatesInfo && templatesInfo.results; if (parameters && parameters.length > 0) { array.some(parameters, lang.hitch(this, function(p) { return p && p.paramName === 'Output_JSON' ? this.templateInfos = p.value : false; })); if (this.templateInfos && this.templateInfos.length > 0) { this.templateNames = array.map(this.templateInfos, function(ti) { return ti.layoutTemplate; }); } } } else { console.warn('Get Layout Templates Info Error', templatesInfo && templatesInfo.error); } if (!esriLang.isDefined(taksInfo) || (taksInfo && taksInfo.error)) { this._handleError(taksInfo.error); } else { this._handlePrintInfo(taksInfo); } })); })).always(lang.hitch(this, function() { this.shelter.hide(); }));
I am using "A4 Landscape.mxd" which is located under
C:\Program Files (x86)\ArcGIS\Desktop10.4\Templates\ExportWebMapTemplates
The only modification I made is; Insert Text and change Element Name.
Any ideas?
Çankaya,
What browser and version are you using?
Robert,
Chrome: 54.0.2840.71 m
Firefox: 49.0.2
Testing in both.
Çankaya,
I am at a loss here. I don't know why you are getting "Get Layout Templates Info Error".
Robert,
I managed to get rid of the error. Also, method #1 working fine now, so I can manually add values to the printout. The only problem is to make it automatically.
Can you tell me if there is something missing from the code below? (The rest of Print.js is unchanged v2.2)
template.showAttribution = false;
//See if there is a parcel search layer added to the map var plyr; array.some(this.map.graphicsLayerIds, lang.hitch(this, function (layerId) { var lyr = this.map.getLayer(layerId); if(lyr.name === "Search Results: Parcels"){ plyr = lyr; return true; } })); if(plyr){ var xppins = "", obj, cTextElements = []; array.map(plyr.graphics, lang.hitch(this, function(gra, index){ if (plyr.graphics.length === 1){ obj = {DistrictName: "District Name: " + gra.attributes.NAME}; cTextElements.push(obj); } else if (plyr.graphics.length > 1){ } template.layoutOptions.customTextElements = cTextElements; })); } //end my change
Çankaya,
Do you have more than one parcel selected? I don't see anything wrong with that code.
Nope, I just select 1 feature, popup shows up, then I use print widget.
Çankaya,
Try updating to this code and then check you browsers web console for the messages:
//See if there is a parcel search layer added to the map
var plyr;
array.some(this.map.graphicsLayerIds, lang.hitch(this, function (layerId) {
var lyr = this.map.getLayer(layerId);
if(lyr.name === "Search Results: Parcels"){
plyr = lyr;
return true;
}
}));
if(plyr){
console.info("we found the layer");
var xppins = "", obj, cTextElements = [];
array.map(plyr.graphics, lang.hitch(this, function(gra, index){
if (plyr.graphics.length === 1){
console.info("there is only one feature in the layer");
obj = {DistrictName: "District Name: " + gra.attributes.NAME};
cTextElements.push(obj);
console.info(obj);
} else if (plyr.graphics.length > 1){
}
template.layoutOptions.customTextElements = cTextElements;
}));
}
//end my change
Çankaya,
Make sure you are adding the eSeach layer as an operational layer.
Hello Robert,
I placed some console messages here and there as you asked.
var plyr; array.some(this.map.graphicsLayerIds, lang.hitch(this, function (layerId) { console.info("layerId: ", layerId); var lyr = this.map.getLayer(layerId); console.info("layer name: ", lyr.name); if (lyr.name === "Search Results: Parcels") { plyr = lyr; return true; } }));
I get my layer names & ids first, then;
layerId: labels
layer name: undefined
layerId: graphicsLayer3
layer name: undefined
Any idea why the name is passed as undefined?
Thanks
Çankaya,
Please use the advanced editor to attach your eSearch widget config_Enhanced Search Widget.json
Robert,
Normally I am modifying the files on the client side. The file you mentioned "config_eSearch.json" is located under:
server>apps>9>configs>eSearch
I am not sure what to do with config_eSearch.json file, it already contains a layer named Parcels.
"layers": [ { "name": "Parcels", "url": "https://blah.blah.blah/arcgis/rest/services/blah/blah/MapServer/0", "definitionexpression": "", "spatialsearchlayer": false, "export2Csv": false, "export2Geo": false, "export2FC": false, "zoomScale": 10000, "forceZoomScale": false, "shareResult": true, "addToAttrib": false, "expressions": { "expression": [] }, "titlefield": null, "fields": { "all": false, "field": [ { "name": "ID", "alias": "ID" }, { "name": "NAME", "alias": "NAME" }, ...
Çankaya,
FYI, when I am editing code, I first create a new app and that copies the client/stemapp contents or to the new app (i.e. server\apps\9) and t in that apps files is where I make all my changes. If you make you changes in the stemapp then you have to create a new app or ad and remove the widget to get the code changes into your app.
So on immediate thing that strikes me as off from the portion of the json you posted above is that you have no expressions defined. You need some search expression.
Hello again Robert,
Thanks for the recommendation. That's exactly what I'm doing. Everytime I make changes on the code, I create a new app. Read about this on one of your other posts.
After many modifications on eSearch parameters, Print layout still remains unchanged.
First I changed Search Layer parameters from the UI, tried different Search Expressions, didn't work. Then I manually edited config.json to make my Search Layer parameters similar to the others.
{ "name": "Parcels", "url": "https://.../server/rest/.../.../MapServer/0", "definitionexpression": "", "spatialsearchlayer": true, "export2Csv": false, "export2Geo": false, "export2FC": false, "zoomScale": 10000, "forceZoomScale": false, "shareResult": true, "addToAttrib": true, "expressions": { "expression": [ { "alias": "Parcel Name", "textsearchlabel": "Search label", "values": { "value": [ { "fieldObj": { "name": "NAME", "label": "NAME", "shortType": "string", "type": "esriFieldTypeString" }, "valueObj": {}, "prompt": "NAME starts with", "textsearchhint": "hint", "sqltext": "NAME LIKE '[value]%'", "operation": "stringOperatorStartsWith", "userlist": "" } ] } } ] }, "titlefield": "NAME", "fields": { "all": false, "field": [ { "name": "NAME", "alias": "NAME" }, { "name": "ID", "alias": "ID" }, { "name": "NUMBER1", "alias": "NUMBER1", "isnumber": true }, { "name": "NUMBER2", "alias": "NUMBER2", "isnumber": true }, { "name": "SHAPE.AREA", "alias": "SHAPE.AREA", "isnumber": true }, { "name": "SHAPE.LEN", "alias": "SHAPE.LEN", "isnumber": true } ] }, "links": { "link": [] }, "orderByFields": [], "layersymbolfrom": "config" }, { "name": "Traffic Cameras", "url": "http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Louisville/LOJIC_PublicSafety_Louisville/...", "definitionexpression": "", "spatialsearchlayer": true, "zoomScale": 5000, "shareResult": true, "addToAttrib": true, "expressions": { "expression": [ { "alias": "Traffic Camera Description", "textsearchlabel": "Search Traffic Cameras by Location...", "values": { "value": [ { "fieldObj": { "name": "DESCRIPTION", "label": "DESCRIPTION", "shortType": "string", "type": "esriFieldTypeString" }, "valueObj": {}, "prompt": "DESCRIPTION starts with", "textsearchhint": "Example: I-71", "sqltext": "DESCRIPTION LIKE '[value]%'", "operation": "stringOperatorStartsWith", "userlist": "I-71,I-64" } ] } } ] }, "titlefield": "DESCRIPTION", "fields": { "all": false, "field": [ { "name": "DESCRIPTION", "alias": "Description" }, { "name": "URL", "alias": "Show Image", "visible": false }, { "name": "ONLINE", "alias": "Online" }, { "name": "LAST_UPDATED", "alias": "Last Updated", "dateformat": "MM/dd/yyyy", "useutc": true, "isdate": true } ] }, "links": { "link": [ { "alias": "View Traffic Photo", "content": "{URL}", "icon": "http://resources.arcgis.com/en/help/flex-viewer/live/assets/images/i_camera.png", "disablelinksifnull": true, "popuptype": "image" } ] }, "layersymbolfrom": "server" },
Enhanced Search Widget's search function on "Parcels" is working pretty good. Finding and zooming on to the parcels starting with 'value'. However, when using Print Widget, layer name returns as undefined.
Çankaya,
I don't remember If I asked this already but what version of WAB and the eSearch widget are you using?
Robert,
WAB 2.2
eSearch 2.1
Chrome: 54.0.2840.87 m
Firefox: 49.0.2
Robert,
Here is the latest situation.
When I search a parcel from Enhanced Search Widget's Parcels layer, the result is selected and highlighted in red. If I use print widget at this time, NAME attribute of the highlighted parcel is included in the printout.
I would like the printout to have the attributes in the popup window when a parcel is selected via mouse click.
Any idea how I can achieve this?
Çankaya,
For that you will have to change the code in the Print.js to get the popups selected feature:
var gra = this.map.infoWindow.getSelectedFeature();
var obj = {DistrictName: "District Name: " + gra.attributes.NAME};
cTextElements.push(obj);
template.layoutOptions.customTextElements = cTextElements;
Hey Robert, I was wondering if you could help me? Where would you put this in the Print.js.
Rich,
In the Print function. if you go back to my very first reply there is a link to another forum post.
I have been to those and I don't see where the var gra = this.map.infoWindow.getSelectedFeature is at. I just need to add the info window attribute info from a selected parcel into a print service.
Rich,
Those threads do not exactly deal with what you are wanting. Bu the basic location of the code is the same.
Sorry, but I am fairly new to some of this. I see the
//See if there is a parcel search layer added to the map
var plyr;
array.some(this.map.graphicsLayerIds, lang.hitch(this, function (layerId) {
var lyr = this.map.getLayer(layerId);
if(lyr.name === "Search Results: Parcels"){
plyr = lyr;
return true;
}
}));
if(plyr){
var xppins = "", obj, cTextElements = [];
array.map(plyr.graphics, lang.hitch(this, function(gra, index){
if (plyr.graphics.length === 1){
obj = {OwnerName: "Owner Name: " + gra.attributes.NAME};
cTextElements.push(obj);
obj = {PPIN: "PPIN: " + gra.attributes.PPIN};
cTextElements.push(obj);
obj = {ParcelNum: "Parcel Number: " + gra.attributes.PARCEL_NUMBER};
cTextElements.push(obj);
obj = {StreetAdd: "Street Address: " + gra.attributes.STREET_ADDRESS};
cTextElements.push(obj);
}else if (plyr.graphics.length > 1){
if (xppins === ""){
xppins = gra.attributes.PPIN;
} else {
if([7,16,25,34,43,52,61,70,79,88,97].indexOf(index) > -1){
xppins = xppins + ",\r\n" + gra.attributes.PPIN;
}else{
xppins = xppins + ", " + gra.attributes.PPIN;
}
}
obj = {OwnerName: "Selected PPINs: " + xppins};
cTextElements.push(obj);
}
template.layoutOptions.customTextElements = cTextElements;
}));
}
//end my change
not sure what I can remove or keep.
Rich,
Remove it all and just use the new code.
Ok thanks do I keep template.showAttribution = false;
If you don't want to show the attribution.
Robert,
var gra = this.map.infoWindow.getSelectedFeature();
var obj = {DistrictName: "District Name: " + gra.attributes.NAME};
cTextElements.push(obj);
template.layoutOptions.customTextElements = cTextElements;
I am guessing this change still requires layer to be found first? But in my situation, if I use print widget, layer is returned as "undefined" so that portion of the code is not reached.
Hello Robert,
Finally got it working. Thanks a lot for your time.
I was wondering if you could share your final print.js script, When I add the code the print widget is just blank. Says there is a "unexpected identifier" syntax error
Hello Rich,
//Hide map attribution template.showAttribution = false; //end my change template.showLabels = form.showLabels && form.showLabels[0]; template.layoutOptions = { authorText: hasAuthorText ? form.author : "", copyrightText: hasCopyrightText ? (form.copyright || this._getMapAttribution()) : "", legendLayers: legendLayers, titleText: hasTitleText ? form.title : "", customTextElements: cteArray }; // final edit var cTextElements = []; var gra = this.map.infoWindow.getSelectedFeature(); var obj = { TXT1: "Name: " + gra.attributes.NAME }; cTextElements.push(obj); template.layoutOptions.customTextElements = cTextElements; // edit end
Robert,
This method works great but how do I format the date and numeric fields?
See attached for fields "SALEDATE" AND "ACRES".
Rich,
You would use dojo formatters.
dojo/number — The Dojo Toolkit - Reference Guide
dojo/date/locale::format() — The Dojo Toolkit - Reference Guide
I think I understand the logic but I have no idea where this code is supposed to go?
Have been trying all sorts of things.
Should it reside in the print.js file?
Rich
Rich,
You are wanting to format the data before it goes to the print so, yes you would do that in the print.js
example:
require(["dojo/date/locale", ...], function(locale, ...){
....
obj = {SALEDATE: "Sales Date: " + locale.format( gra.attributes.SALEDATE, {selector:"date", datePattern:"MMM d, yyyy"} );
Thank You,
I was able to change the format of numeric fields, but I am doing something wrong with the date values.
When I add "locale" to the format string the Print widget bombs out and will not create a print. Something is incorrect in one of the attached images. Any further help would greatly be appreciated?
Thank You!
Rich
Rich,
Did you add the require for locale to the existing requires in the js file?
I believe so? I'm still learning....I added what is in the attached images.
What am I missing?
Rich,
I am not sure why you added those two function commented with BELL ADDITION in your code. There should be no need for those.
Ahh! I deleted the BELL ADDITIONS. That was from my first attempt at this.
I do not know where to add the Require in the file
Hello again Robert,
var gra = this.map.infoWindow.getSelectedFeature(); var obj = { TXT1: "Name: " + gra.attributes.NAME };
Once a feature is selected, Popup Window is displayed. When Popup Window is closed, feature is no longer selected but the above code still uses the last selected feature.
How can I clear getSelectedFeature() when Popup Window is closed?
this.map.infoWindow.clearFeatures()
Thank you, but I meant where and can I handle the event when the Popup Window is closed.
I tried the ones below but didn't work.
map.infoWindow.on('hide', function(){ console.info("infoWindow hide event"); this.map.infoWindow.clearResults(); });
====================================
dojo.connect(map.infoWindow.hide, "onclick", function () { console.info("infoWindow hide event"); this.map.infoWindow.clearResults(); });
Mission accomplished.
this.map.infoWindow.on('hide', function(){ console.info("infoWindow hide event"); this.map.infoWindow.clearFeatures(); });
I am going to prepare a tutorial for printing popup contents soon.
Thank you Robert
Ok, I am still at a loss! Tried my best.. What am I doing wrong? The "SALEDATE" date field will not format correctly.
Robert,
I found the Print Format issue.
This is the correct string to format the dates.
// edit start var cTextElements = []; var gra = this.map.infoWindow.getSelectedFeature(); var obj = { TXT1: "Name: " + gra.attributes.NAME }; cTextElements.push(obj); template.layoutOptions.customTextElements = cTextElements; // edit end this.printparams.template = template;
utils.combineRadioCheckBoxWithLabel(extentRadio, this.printWidgetMapExtentLabel); // edit start this.map.infoWindow.on('hide', function () { this.map.infoWindow.clearFeatures(); }); // end edit
Can this be done without using portal? I only have the 'plain' AGOL.
No
Grateful for your contributions Robert and Çankaya, I was able to adapt this code in short order. I'm using this to generate webmaps where the focus of the printout is actually the attributes, with the map small (3"x3") for general situational awareness only.
In this instance, it becomes important to center the printout on my point of interest and fix the zoom level, do you know if there is a way to programatically send this to Print Widget?
Thanks again, this is fantastic!
Jeff,
If I understand your question correctly you need to publish a custom print service on your own ArcGIS Server with a custom print layout.
So the custom print service can tell the widget to print on the centroid of the selected feature? Basically i'm trying to ensure that the selected object in the WebApp prints at center every time.
Oh, ok I did not understand that was what you were after. You will need to add code in the print widget to set the maps extent center to the selected features centroid then.
Hello Jeff,
You can do something like:
this.map.setLevel(this.map.getLevel() + 2); this.map.centerAt(this.map.infoWindow.getSelectedFeature()._extent.getCenter());
Hope it helps.
This document was generated from the following discussion: Printing Popup Contents
Hi, @JustinJacobs , may i know how do you add the date in the print layout? at which script that you add the add date feature? i tried to add mine from add dynamic text from ArcGIS Pro but it did not work.