SaveSession Widget

37686
113
02-06-2016 09:57 AM
Labels (1)

SaveSession Widget

The SaveSession Widget for ArcGIS Web AppBuilder enables users to save the current map settings into a session and restore them again later. A saved session includes the extent, visible layers and annotations of the current map. Sessions may be saved and loaded from files so they can be shared with others.

Using the Widget

The SaveSession Widget is an in-panel widget that will be displayed in the toolbar of your WebApp Builder application. Click the  icon icon display the SaveSession panel.

Configure Icon

Saving a Session

Once you have the map just the way you want it - zoomed to the area and layers turned on, enter a name for the session and click the Save button. The session will be added to the Saved Sessions list.

Configure Icon

Restoring a Session

To return the map to the same state as when you saved the session, you can double-click the title of the session to load the map.

Managing Sessions in the List

Hover over the Actions column for a session entry to reveal the actions that can be performed on the entry.

Load Map = will load the restore the selected session to the current map

Load Action

Download Map = will save the session entry to a file that may be shared.

Download Map Action

Edit = allows the user to change the session name

Configure Icon

*Move Up - Down * = lets you arrange the entries in the session list in the desired order

Delete = click the Delete button to remove the entry from the list

Configure Icon

Sharing Sessions

Saved Sessions may be shared with other users or another browser on the same PC by saving the session to a file and then loading the session from the file into the session list.

To save a single session to a file, click the Download Map action for the entry. To save all sessions in the list to a file, click the Save to file link.

Configure Icon

To load the sessions from a file, click the Load from file link to display the Load sessions from file dialog

Configure Icon

Click the Choose File button and select the file to load. The selected file must be a saved session file. All sessions from the file will be loaded. If a loaded session has the same name as an existing session, a number will be appended to the session to make the name unique.

Configure Icon

Widget Setup

Source code and installation instructions may be found at GitHub - softwhere/SaveSession-Widget

v2.7  - released on 4/3/2018

Download

Thanks to the City of Garland, Texas for sponsoring the initial development of the SaveSession Widget and releasing it to the community.

Labels (1)
Tags (1)
Comments

Hello.

I modified the savesession script to just store the current settings including the basemap. It has been tested. The script is part of the application posted at: https://community.esri.com/thread/195193-app-with-modified-widgets-and-buffer-tool You can easily get widget from this app.

I made some modifications to this widget to re-enable saving the sessions to a local browser cache which worked fine in WAB 2.1, however it seems saving to cache no longer works when the widget is used in WAB 2.4.  It seems like the storageKey and sessions array are not being called in the loadSavedSessionsFromStorage function.  Any idea what has been changed?

The local browser cache has been very problematic for me. I stripped out the code and in the modified version you can save the current session to the download folder. It works well and saves the basemap as well. The modified widget is part of https://community.esri.com/thread/195193-app-with-modified-widgets-and-buffer-tool 

Does this work with all versions of WAB?

Thanks!

Does anyone have an example of what the service looks like that allows for downloading sessions in IE?  I'm planning to just write it in python and publish as a GP service on our ArcGIS Server but would like to understand what the script needs to do.

The service is only needed for older browsers that do not support downloading from dataurl. The web service would accept a post of the saved session information and returns it as a file attachment to the http response. You're basically sending the string to the server and getting it back as a downloadable file.

Here's an example using ASP.NET MVC

/// <summary>
/// accepts a post from client and returns a file attachment of the given content with the given filename
/// </summary>
/// <param name="content">content to save to file</param>
/// <param name="filename">name fo the file</param>
/// <returns></returns>
[HttpPost]
[Route("")]
public HttpResponseMessage Post([FromBody]SaveToFileModel model)
{
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StringContent(model.Content);
//a text file is actually an octet-stream
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
//used attachment to force download
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = model.FileName;
return result;
}

Hope that helps.

David

Thanks David!

I hoped to publish a simple GP service that would take an input JSON string, write it into a .json file and then set that as an output parameter but I'm unable to write to any intermediate .txt file that can be set to an output parameter "File" type on the GP tool.

import os
import arcpy

inSessionStr = arcpy.GetParameterAsText(0)

outputPath = os.path.join(r"outputSession.json")
outputSessionfile = open(outputPath, 'w')
outputSessionfile.write(inSessionStr)

#specify the output parameter as the outputSession file
outputSession = arcpy.SetParameter(1, outputSessionfile)

I went and implemented an ArcGIS Server solution for overcoming the issue with internet browsers like IE that do not support downloading data from url's.  I suppose this could be much easier to implement as a regular web service but I just don't have access to tools needed to build and deploy such a thing.  However, as a Geographer I do have the ability to create and deploy Geoprocessing services and that is why I've implemented this approach.

The solution consists of:

1. A Geoprocessing Service with two parameters:
       -Name: "inputSession" | DataType(GPString) | Direction(esriGPParameterDirectionInput) | Parameter Type(esriGPParameterTypeRequired)
       -Name: "returnFile" | DataType(GPDataFile) | Direction(esriGPParameterDirectionOutput) | Parameter Type(esriGPParameterTypeDerived)
      
2. A .py script source for the GP Tool.  Run the GP Tool in ArcGIS Desktop and publish as you would any GP Service and be sure to set it as a synchronous service.

 import os
 import arcpy
 import uuid

 inSessionStr = arcpy.GetParameterAsText(0)

 output = 'SessionFile_{}.{}'.format(str(uuid.uuid1()), "json")
 Output_File = os.path.join(arcpy.env.scratchFolder, output)

 outputSessionfile = open(Output_File, 'w')
 outputSessionfile.write(inSessionStr)
 outputSessionfile.close()

 
 #specify the output parameter as the outputSession file
 arcpy.SetParameterAsText(1, Output_File)
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍


 
3. Update the SaveSession's Widget.js to implement the Geoprocessing.  Locate the existing "onSaveToFileButtonClicked" function and replace with:

           

onSaveToFileButtonClicked: function (e) {
                if (!this.config.useServerToDownloadFile) {
                    // skipping since there is no url to submit to
                    console.log('SaveSession :: onSaveToFileButtonClicked :: saveToFileForm submit canceled.');
                    return;
                }
                var sessionString = JSON.stringify(this.sessions);
                //execute the GP service with sessionString value
                this.executeGPDownloadSessions(sessionString); 

        },‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 

3a. Don't forget to add the necessary "esri/tasks/Geoprocessor" reference!        
           

4. Add this function directly below the "onSaveToFileButtonClicked" function (or somewhere that makes sense for you).           

   

executeGPDownloadSessions: function (param) {
                
                var gp = new Geoprocessor("https://mydomain/myAGSsite/rest/services/GP/myGPServiceName/GPServer/myGPServiceTaskName");               
                var params = { "inputSession": param };
                gp.execute(params, downloadFile);

                function downloadFile(results, messages) {
                    //do something with the results                    
                    var theSessionFile = results[0].value;
                    window.open(results[0].value.url);                   
                }
            },‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

5. Be sure to set the widget to use the "Use Server To Download File" and add the address to your GP service url into the "Url for Save To File" box.


Now when the SaveToFile link is clicked on the widget, the user will be prompted to "SaveAs" the .json file that is returned from the GP service.  Only tested in Chrome and IE.

From my example I posted, we noticed that some clients would not download the .json file in IE and a new browser tab would open and simply display the contents of the file.  The solution is to .zip the file and return that to the client.  Also, window.open() can be affected by popup blocking:

executeGPDownloadSessions: function (param) {
                
var gp = new Geoprocessor("https://mydomain/myAGSsite/rest/services/GP/myGPServiceName/GPServer/myGPServiceTaskName");               
var params = { "inputSession": param };
gp.execute(params, downloadFile);

                function downloadFile(results, messages) {
                    //do something with the results                    
                    var theSessionFile = results[0].value;
                    window.open(results[0].value.url);                   
                }
            },

Instead of window.open(), add an <a href> within a <div> and then replace the href link in the JavaScript with the url that is returned from the service in the response:

//working function using <a href> as means to open download interface instead of window.open()
//this is because popup blocker may affect window.open() function on some clients
function downloadFile(results, messages) {
    //do something with the results                    
    //var theSessionFile = results[0].value;
    //set a var of the complete url address that is returned from the GP service request
    var urlOfFile = results[0].value.url;

    //get the div from the html of the widget
    link = document.getElementById('downloadSession');
    link.setAttribute('href', urlOfFile);
    link.appendChild(document.createTextNode("Save file"));
    document.body.appendChild(link);
    link.click();
}
},

David,

Thanks for providing a long needed widget! 

It works fine in Chrome but "Save to file" does not work in IE.  Rather than set up a service, I'd like to just remind users to use Chrome to save by changing the button to read "Save to file (Chrome)".  Could you point me to where this label lives so I could modify text?

Thank You

A while ago, I posted a modified version of this widget. Since then, I made some corrections to fix some bugs and implemented some enhancements. With this version you can save in the session a local file (shapefile), in addition to any web service based layers,  and the basemap that you are using. The session file is saved locally in your computer. This version was developed in version 2.4 and it was tested in Chrome and IE.  Just add this widget in the widget folder in your app and add the entry in the config.json.

savesession1017zip

You should add this same information to your document page https://community.esri.com/docs/DOC-10878-savesession1017zip 

if you can. That would give users  direct access to the .zip and the info above.  For now, I added a link to the above comment in the /blogs/myAlaskaGIS/2017/03/04/web-appbuilder-the-custom-widgets-list-332017?sr=search&searchId=c4b04...  Thanks.

Done. Thank you for your suggestion.

Hello David McCourt‌,

This widget has been a well-received addition to our organizational web applications - thank you for sharing with the GIS community! I was hoping you could let me know the location of where a non-downloaded session gets saved to? 

save session widget screenshot

I see in the widget code that it's saved to the browser's web storage, but I'm having trouble understanding where this exists (specifically in IE 11). IT is adjusting user profile & local storage permissions at our organization, and we want to work with them to ensure this location is still writeable for users. Any help in understanding the physical location of the local/web storage for browsers is appreciated.

Thanks again,

Jacqueline

Each browser will implement the storage API differently, but for IE11 I think it's stored in the user profile at  %userprofile%\Local Settings\Application Data\Microsoft\Internet Explorer\DOMStore

Hope that helps,

David

Hi David McCourt,

I agree with Jacqueline that this has been an extremely beneficial widget for our organization.  I have a similar question to Jacqueline as well.  Many users in our organization utilize a VDI environment where each time they log out and back in, they in essence get a new computer profile.  This wipes any Saved Sessions that they had previously saved.  As Jacqueline was inquiring about, I am also looking for the location (in IE 11) where these sessions get saved in the web storage.  I have tried testing a few scenarios out where I would first save some sessions using the SaveSession widget.  Next, I then copied the Internet Explorer folders from the following paths:

%userprofile%\AppData\LocaLow\Microsoft\Internet Explorer

%userprofile%\AppData\Local\Microsoft\Internet Explorer

and paste them in a location where I can retrieve them after I log out and back in.  Once I logged back in I tried overwriting these folders (two separate tests) to see if my sessions were restored.  I was not successful in either test.  Would you have any suggestions for me to test out? 

Thanks in advance,

Craig

You can also save the session (custom drawings, all operational layers including imported local shapefile, current basemap) to a local file instead of relying on browser web storage). I modified the widget to do just that. https://community.esri.com/docs/DOC-10878-savesession1017zip 

Thank you for the suggestion Lefteris.  I will try this approach out.  This could be a good workaround for the time being but ideally we would not want our users having to save/load json files.  Ideally, the browser web storage is our best plan if I can determine where these saved sessions are being written to.

Thanks again,

Craig

ok. Let me know how is working out for you. I tried the web storage and I found it too problematic because it depends on the browser, the cache and other factors. At least by saving the json file, it allows you to send the json file to a colleague so you can collaborate on your work.

Thank you for creating this.  It seems like it will be quite useful and will probably be well-received by our users.  I am curious though, is there a possibility of having layers added via the Add Data Widget saved to the user session?  It doesn't appear to save layers not included in the WAB Application by default (such as layers available to my organization, or shapefiles added via a zip file).

Has anyone encountered the "Download Failed- Network error"?  I'm using Chrome 63 and all was working but then started having this issue when trying to save to file.  It is machine specific, I can go to another computer and it works but can't fix on troubled machine.  I'm about to trying reinstall of Chrome but wanted to see if anyone had any ideas first.

Thanks

We ran into an issue with downloading of .json file types on some computers because there was no file association set for that file type.  We changed our webservice to return a .zip file containing the .json file rather than returning the actual .json file.

In my case json files were saving but then started having the download error.  I don't have a separate web service set up just using Chrome's.  I ended up deleting all the saved sessions from the list and then was able to add sessions and save to json file again.  I'm assuming a corruption of some kind in one of the sessions.

James, can you let me know how you made this change? We used to be able to download the .json file but all of a sudden we are all receiving the "Download Failed- Network error". Even after I reload the Save Session widget the .json file will download once and then it won't download again. I even tried removing any .json files that have been downloaded in the past like Hunter mentions above but that didn't help either. 

-Mike G

Michael

You may make done so already but I think it's possible to have a corruption in a session in the list. You could try deleting all sessions from list then try save json.

Sent from my iPhone

Thanks Hunter. I did try that already. I deleted every .json file that has ever been downloaded and even tried reinstalling the Save Session widget from scratch. Still does not work.   Mike

Michael,

My comments above on 9/21/2017 has the overall layout of what we implemented.  I had to publish a Geoprocessing service that basically writes the sessionString to a .json/txt file and adds it to a .zip.  The output parameter of that GP service is the .zip

I only used the GP service because that's the only thing I have available for publishing services.

When trying to download a saved session, we get the 405 error below.  We do not get any errors when trying to save a session or load a session, just when we attempt to download map from the list of saved sessions.  Is there a way to fix this?

405 - HTTP verb used to access this page is not allowed.

The page you are looking for cannot be displayed because an invalid method (HTTP verb) was used to attempt access.

I've submitted a Pull Request to GitHub from my fork at https://github.com/ordizzle/SaveSession-Widget.  If you upgrade your Web AppBuilder to version 2.7, the Load Map icon will be missing in the SaveSession widget due to changes to the sprite files (sprite.css and sprite.png) at ...\client\stemapp\jimu.js\css.

In order to fix the missing icon, updates are required to ...\SaveSession\SimpleTable.js and ...\SaveSession\css\style.css

Hi Zachary Ordo,

It is my first time working with the Save Session Widget, i am trying to deploy this widget and i am receiving the same error that you received ( 405 - HTTP verb used to access this page is not allowed.). Do you remember what you did to solve this error?


Thank you

 

Unfortunately we haven't found a solution to this issue yet, but I think it is happening somewhere during the execution of onSaveItemToFileClicked in Widget.js (lines 440 - 463):

/**
* save the single item to file
* @param {Object} e the event args
*/
onSaveItemToFileClicked: function (e) {

var sessionString = "",
fileName = "",
sessions = [];

fileName = string.substitute(this.config.fileNameTplForSession, e.item);

sessions.push(e.item);
sessionString = JSON.stringify(sessions);

// update form values
this.saveToFileName.value = fileName;
this.saveToFileContent.value = sessionString;

// trigger the post to server side
this.saveToFileForm.submit();

console.log('SaveSession :: onSaveItemToFileClicked :: end');
},

Hi, This is my first time using this widget, and it looks promising - Thanks! I was able to restore session on the same machine however when trying to download the session file (to share with others) I get

Cannot POST /webappbuilder/apps/2/

error in both chrome and IE. Is this a problem with installation? David McCourt

Thanks,

Shital

Good Morning,

I have been working with this widget and I noticed that if two dynamic map services are added using the "Add Layer to Map Function", the first layer appears properly in the Layer List but the second dynamic map service does not have any sublayers.  Has anyone run into this yet?  

Would it be possible to supply a link to a web app that includes this widget so that I can try it out? Many thanks.

Hi John,

This is an problem. It seems the LayerInfos is not updated completely when setLayerOnMap function gets called.

Without knowing too much about the details on how the LayerInfos are updated (the architecture of whole jimu library is not clearly documented!) , I simply recreated the whole LayerInfos instance when all the layers are loaded. I also created my own version of SetLayersOnMap function which returns a promise.

I am happy to share the code here:

            _loadSession: function (sessionToLoad) {
                //  zoom the map
                if (sessionToLoad.extent) {
                    extentToLoad = new Extent(sessionToLoad.extent);
                    this.map.setExtent(extentToLoad).then(function () {
                        console.log('SaveSession :: loadSession :: new extent  = ', extentToLoad);
                    }, function () {
                        var msg = new Message({
                            message: string.substitute("An error occurred zooming to the ${name} map.", sessionToLoad),
                            type: 'error'
                        });
                    });
                }


                // load the saved graphics   
                 this.setGraphicsOnCurrentMap(sessionToLoad.graphics);


                // toggle layers
                if (sessionToLoad.layers) {
                    //this.setLayersOnMap(sessionToLoad.layers);
                    theWidget = this;
                    //make sure all the added layers are loaded before retrieving the new layerInfos,
                    //replace the current layerInfos with the new layerInfos, this will make sure the layerlist will
                    //alwas show the correct layers and sublayers of the added layer
                    this.mySetLayersOnMap(sessionToLoad.layers).then(function(flags){
                         newLayerInfos = LayerInfos.createInstance(theWidget.map);
                         LayerInfos.setInstance(theWidget.map,newLayerInfos);
                         //update the layerlist?
                         theWidget.layerInfosRestoreState(sessionToLoad.layerOptions);
                    });
                }

                // fire custom event
                topic.publish("SaveSession/SessionLoaded", sessionToLoad);

                console.log('SaveSession :: loadSession :: session  = ', sessionToLoad);
            },‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍


            mySetLayersOnMap: function (settings) {
                //var allDynamicLayersReadyDefs = new Deferred();
                var layersLoadedDefs = [];
                //var layerLoadedFlag;
                var layer;

                array.forEach(settings, lang.hitch(this,function(layerSettings) {
                    layer = this.map.getLayer(layerSettings.id);
                    if (!layer) {
                        layer = this.addLayerToMap(layerSettings);
                        layer._layerLoadedFlag = new Deferred();
                        layersLoadedDefs.push(layer._layerLoadedFlag);

                        layer.on("load",function(e){
                                e.layer._layerLoadedFlag.resolve("loaded!");
                                delete e.layer._layerLoadedFlag;//it seems that deleting the temprary property here won't affect the Deferred. by simoxu
                        });
                    }
               }));
               return all(layersLoadedDefs).then(function(flags){
                       return flags;
                       });
           },‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Hope it helps.

I'll see if I can contribute to the widget project in GitHub, so that this small fix can be merged into the product. 

Great widget! 

Solid and elegant code, love it. Hat off to the programmer.

It supports graphics, added layers, and not only layer visibility but also layer order! and you can expand this widget easily.

I has been and will be relevant till the out-of-box Bookmark widget has the same functionality, which seems unlikely given it's been more than 3 years

Great thanks,  this is great.  I see you have to reload the LayerInfos.  I keep losing the sublayer visibility though and the following line is causing issues: theWidget.layerInfosRestoreState(sessionToLoad.layerOptions)

Thanks

Anonymous User

simo xuJohn Lucotch‌ it seems I am also seeing this problem?  ....  I have a 'label' layer for my parcels. It is a group, in the MXD. There are two sublayers. one of them is a very, very heavy layer to load and it is unchecked and OFF by default. The other sublayer with measurements is on.  However....  when I save a Session with this, let's say it turns off Parcel labels and turns on schools. When I open a session to turn the Parcel Label layer back on, it should only turn on the measurement Annation in the Labels group and NOT the 'owner' label sublayer, the heavy dynamic label layer.  But it turns them both on. David McCourt

Here is my example site with your widget (WAB 2.12!)    https://public.sagis.org/sagisorg2019/

Otherwise seems to work perfect. But the visibility sublayer issue is a big catch and I have to resolve this before I can deploy it because that sublayer that is supposed to stay off is very heavy and has to stay off.

simo xu where do I paste your code?

Hi Kevin, 

Did you figure out where to put the above code? I am having the same problem with an unwanted layer turning on. Thanks!

Anonymous User

Hi Scott,   No, unfortunately.  I really want to use this widget, because it's awesome!  As an aside, Layer visibility across the Esri platform in general harder than it should be.  I will probably just take some things out of groups, like those layers. But that is definitely a trade-off. I really want to be able to squirrel away a few things in to groups so only power users will use them.  I hope we can resolve this in the next few weeks. I might look more at it in a week or so but I have a lot of other things like managing the chaos of setting up Portal for the first time first.  This is probably why things like grouping layers in webmaps on AGOL is taking so long.  I am almost hesitant, too to re-create a whole chunk of jimu and WAB underpinnings, I wonder about unintended consequences, scope issues, etc.  Why not just locate the exact problem and fix it, in Layerinfos module itself? (sounds more like jurisdiction for the official WAB team itself)  

Hi Kevin

Please try the following steps.

1. Declare an array to store the added layers (layer IDs).

because I have the Add Data widget, so there will be added layers to the default web map. in different sessions the user may have very different layers added to the map, so I need to remove all these added layers in the current session before I load a new session. I don't reload the webmap, because this will cause failure in all loaded widgets since they are tied to the current map object.

2. Replace the loadSession function with the following one in Widgets.js:

loadSession: function (sessionToLoad) {
// zoom the map
if (sessionToLoad.extent) {
extentToLoad = new Extent(sessionToLoad.extent);
this.map.setExtent(extentToLoad).then(function () {
console.log('SaveSession :: loadSession :: new extent = ', extentToLoad);
}, function () {
var msg = new Message({
message: string.substitute("An error occurred zooming to the ${name} map.", sessionToLoad),
type: 'error'
});
});
}

//remove the layers added in the current map
theMap = this.map;
if (this.addedLayers.length>0){
this.addedLayers.forEach(function(layerId){
lyr = theMap.getLayer(layerId);
theMap.removeLayer(lyr);
});
}
//clear the previously added layerIDs
this.addedLayers=[];

// toggle layers
if (sessionToLoad.layers) {
//this.setLayersOnMap(sessionToLoad.layers);
theWidget = this;//save "this" context in a local variable, in case the context changes in the callbacks.
//make sure all the added layers are loaded before retrieving the new layerInfos,
//replace the current layerInfos with the new layerInfos
this.mySetLayersOnMap(sessionToLoad.layers).then(function(flags){
var newLayerInfos = LayerInfos.createInstance(theWidget.map);
LayerInfos.setInstance(theWidget.map,newLayerInfos);
//change layer visisbilities in the layerlist
theWidget.layerInfosRestoreState(sessionToLoad.layerOptions);
//this will make sure the layerlist always shows the correct layers and sublayers of the added layer
theWidget.refreshLayerList(newLayerInfos);

});
}

// fire custom event
topic.publish("SaveSession/SessionLoaded", sessionToLoad);

console.log('SaveSession :: loadSession :: session = ', sessionToLoad);
},

Sorry, I've lost all the formatting of the code here, please reformat it in your Javascript editor, then it should look much better and less scary

3. Paste all the supporting functions after the loadSession function.

/**
* restore the layer visisbilities
* @param array of layer visisbilities
* by simoxu
*/
layerInfosRestoreState: function (layerOptions) {
var layerInfosObj = LayerInfos.getInstanceSync();
var layers = {};
if (layerOptions) {
for (var key in layerOptions) {
if (layerOptions.hasOwnProperty(key)) {
var layer = layerOptions[key];
var visible = false;
if ("undefined" === typeof layer.visible) {
visible = layer.selected;//turn .selected to .visible
} else {
visible = layer.visible;//keep: layers[key].visible
}

layers[key] = {
visible: visible
};
}
}

layerInfosObj.restoreState({
layerOptions: layers
});
} else {
//keep current visibility
}
},

refreshLayerList: function(operLayerInfos) {
var wman = this.widgetManager;
if (wman) {
layerListWidget = wman.getWidgetByLabel("Layer List");
if (layerListWidget) {
layerListWidget.operLayerInfos = operLayerInfos;
layerListWidget._refresh();
}
}
},

mySetLayersOnMap: function (settings) {
//var allDynamicLayersReadyDefs = new Deferred();
var layersLoadedDefs = [];
//var layerLoadedFlag;
var layer;
var len = settings.length;

array.forEach(settings, lang.hitch(this,function(layerSettings,index) {
var layer
layer = this.map.getLayer(layerSettings.id);
if (!layer) {
layer = this.addLayerToMap(layerSettings);
layer._layerLoadedFlag = new Deferred();
layersLoadedDefs.push(layer._layerLoadedFlag);
//record the layers added on top the intial Web Map
this.addedLayers.push(layerSettings.id);
var theMap = this.map;
var theOrder = layerSettings.order;

layer.on("load",function(e){
e.layer._layerLoadedFlag.resolve("loaded!");
//set the order again after the layer loaded
theMap.reorderLayer(e.layer,theOrder);
delete e.layer._layerLoadedFlag;//it seems that deleting the temprary property here won't affect the Deferred. by simoxu
});
} else {
this.map.reorderLayer(layer,layerSettings.order)
}
}));
return all(layersLoadedDefs).then(function(flags){
return flags;
});
},

4. Optional. if you want it to support imagery layers, you need replace the addLayerToMap function withe the following one:

/**
* create a new map layer with the given settings
* @param {Object} layerSettings settings for the layer
* @return {Object} layer oject
*/
addLayerToMap: function (layerSettings) {
console.log('SaveSession :: addLayerToMap :: adding layer = ', layerSettings);
var layer,
options;
switch (layerSettings.type) {
case "ArcGISDynamicMapServiceLayer":
options = lang.clone(layerSettings.options);
options.imageParameters = new ImageParameters();
lang.mixin(options.imageParameters, layerSettings.options.imageParameters);
layer = new ArcGISDynamicMapServiceLayer(layerSettings.url, options);
//layer = new ArcGISDynamicMapServiceLayer(layerSettings.url);
console.log('SaveSession :: addLayerToMap :: created ArcGISDynamicMapServiceLayer layer = ', layer);
break;
case "FeatureLayer":
layer = new FeatureLayer(layerSettings.url, layerSettings.options);
console.log('SaveSession :: addLayerToMap :: created Feature layer = ', layer);
break;
case "ArcGISTiledMapServiceLayer":
layer = new ArcGISTiledMapServiceLayer(layerSettings.url, layerSettings.options);
console.log('SaveSession :: addLayerToMap :: created ArcGISTiledMapServiceLayer layer = ', layer);
break;
case "ArcGISImageServiceLayer":
layer = new ArcGISImageServiceLayer(layerSettings.url,layerSettings.options)
break;
default:
console.log('SaveSession :: addLayerToMap :: unsupported layer type = ', layerSettings.type);
break;
}

if (layerSettings.name) {
layer.name = layerSettings.name;
}

// The bottom most layer has an index of 0.
this.map.addLayer(layer, layerSettings.order);
console.log('SaveSession :: addLayerToMap :: created layer for ', layer.id, ' using settings = ', layerSettings);
return layer;
},

Because I changed this widget quite a bit to suit my particular application needs, It might not be a perfect fit in your application. But,  I hope this at least can provide some clue to help you to find a better solution if it does not work straight away.

Good luck.

Simo

Hi John,

Hope you've figured it out. otherwise please see if my latest response to Kevin MacLeod can help you out.

Cheers  

Anonymous User

simo xu‌ I also have Add Data in my sites and want to allow for users to add imagery layers potentially.

So.. I pasted in all code in steps 2 to 4 in to my SaveSession's widget.js.  https://public.sagis.org/savesessiontest/

Now it doesn't do anything.   It restores the extent but does not restore any layers at all.  Just leaves whatever is already on, and does not change anything.  Here is my example so far. 

On Step 1... where shall I declare the array? Would you be kind enough to show an example of that code and where to paste it?  Thank you! Best, Kevin

For those of you who don't use a webmap on their app, I posted this modified version of Save Session a while ago.

https://community.esri.com/docs/DOC-10878-savesession1017zip 

Anonymous User

That's for LocalLayers right Lefteris?

Actually, it does not matter how the layers were added. I used it for apps that use the LocalLayers widget, but also with apps that add layers by simply using the map.addLayer.

Never tested it with a webmap because I never use portal for the apps. I just define the basemap and then I add the layers programmatically.

Hi Kevin

I had a quick debug into your code, and I can see where the code stops

Please declare the array here in the Widget.js :

Good luck.

Don't worry about pasting the code. I just extracted some code from my work and created a customized SaveSession Widget, and you should be able to use it to replace the original one, please check it out: 

SaveSession that supports heavy map layers and imagery Layers 

Checkout this customized SaveSession widget:

SaveSession that supports heavy map layers and imagery Layers 

Version history
Last update:
‎02-06-2016 09:57 AM
Updated by:
Contributors