Select to view content in your preferred language

dynamically created layers not usuable for layer commands

843
6
06-11-2013 01:09 PM
NigelAlford
Occasional Contributor
I'm building layers dynamically and this is working great but I don't seem to be leveraging the api properly.  The layers are being built at runtime properly but I can't attach to them after the page is loaded, which leads me to believe that their creation is not seen by the API, but I'm unsure of how to solve this.  My first thought is to use the **new** keyword to instantiate the object but I can't get that working.

The code below is what is actually generating new objects from the GIS api, 'makeid()' is a function that creates a unique string from uppercase/lowercase letters and numbers.  I call this function for every new layer I want to add, the variable creation is where I think I'm off but I'm not sure.....

    var setTiledLayer = function(URL) {
        var tuID = makeid();
        tuID = new esri.layers.ArcGISTiledMapServiceLayer(URL, {id: tuID + 'ID'});
        return tuID;
    };
0 Kudos
6 Replies
BenFousek
Deactivated User
You are using the same variable for two different things.
    var setTiledLayer = function(URL) {
        var tuID = makeid(); //here tuID is whatever makeid() returns
        //now tuID is is the layer object
        tuID = new esri.layers.ArcGISTiledMapServiceLayer(URL, {id: tuID + 'ID'}); //you are trying to use an object for part of the string "id"
        return tuID; //this should throw an error - are you watching this in firebug?
    };


Try this.

    var setTiledLayer = function(URL) {
        var tuID = makeid();
        var layer = new esri.layers.ArcGISTiledMapServiceLayer(URL, {id: tuID + 'ID'});
        return layer;
    };
0 Kudos
NigelAlford
Occasional Contributor
Thanks, I finally figured out that the function was creating an object around tuID.  I rewrote this because ultimately I'm having runtime issues, ESRI won't attach to these dynamically generated items. 

I've created a solution but now I'm stuck on how to talk to the object, which if I use the console I can see all the attached ESRI functionality.  I dont' know how to retrieve the nested object dynamically, every way I'm currently trying keeps returning the object vs the name itself. 

    var lyrHldr = {};

    var setTiledLayer = function(URL, lyrNm) {
        var temp = makeid();
        lyrHldr[temp] = new esri.layers.ArcGISTiledMapServiceLayer(URL, {id: lyrNm});
        return lyrHldr[temp];
    };
0 Kudos
BenFousek
Deactivated User
I don't know if I totally have my head wrapped around what you are trying to do. I get the feeling you are trying to load layers dynamically on application load using custom parameters.

I start with an object containing the info I need to create the layer plus added functionality, either by making a server call that returns the object in json or a javascript object in a config .js file. Here's an example of the latter:
var config = {
    layersMapService: [
        {
            url: 'http://www.sample.com/arcgis/rest/services/some_layer/MapServer', //url
            type: 'dynamic', //type (dynamic or tiled)
            name: 'Some Layer', //name as shown in toc and elsewhere
            id: 'somelayer', //id must be unique
            visible: false, //initial visibility
            opacity: 1, //initial opacity
            imageFormat: 'png32', //image format
            dpi: 96, //dpi
            legend: true, //legend in toc and map
     identify: false, //is layer available in identify widget
     identifyLayers: [], //which layers
     query: false, //is layer available in query widget
     queryLayers: [] //which layers
        }, {
            url: 'http://www.sample.com/arcgis/rest/services/another_layer/MapServer',
            type: 'dynamic',
            name: 'Another Layer',
            id: 'anotherlayer',
            visible: false,
            opacity: 0.5,
            imageFormat: 'png32',
            dpi: 96,
            legend: true,
     identify: true,
     identifyLayers: [0,1,6],
     query: true,
     queryLayers: [0,1,3,4,5,6]
        }
    ]
};


Then during your initialization function, after initializing your map iterate through the config.layersMapService object to add them to the map:
var app = {
    build: function() {
        //build viewer, load mods, etc
        //map
        app.map = new esri.Map('map', {});

        //add layers
        dojo.forEach(config.layersMapService, app.layers.add.ms);

    },
    layers: {
        add: {
            ms: function(l) {
                //l is the object with all the params
                if (l.type === 'dynamic') {
                    var imageParameters = new esri.layers.ImageParameters();
                    imageParameters.format = l.imageFormat;
                    imageParameters.dpi = l.dpi;
                    var layer = new esri.layers.ArcGISDynamicMapServiceLayer(url, {
                        id: l.id,
                        imageParameters: imageParameters,
                        visible: l.visible,
                        opacity: l.opacity
                    });

                 /*
  //because the layer is an object I add the params object
  //directly to the layer object in case I want them
  //again for some other functionality
  //
  //this is an important concept to really leverage the api
  //
  //for example: when the query widget gets the json object
  //back from the server for each map service layer that information
  //(fields, domains, etc) will be stored as an object in the
  //corresponding layerIds object
  */
                    layer.layer_params = l;

                    app.map.addLayer(layer);

                    /*
     //this is the time to do all the extra things
     //while we have the layer variable
     */
                    app.toc.add(layer, l.legend);

                    if (l.identify) {
                        app.identify.add(layer, l.identifyLayers)
                        }

                    if (l.query) {
                        app.query.add(layer, l.queryLayers)
                        }
                } else if (l.type === 'tiled') {
                    //add tiled layer
                    }
            }
        }
    }
};


Don't know if this is what you're looking for, but hope it helps.
PS: you'll have to clean up the 2nd piece of code; it wouldn't hold the indents for some reason.
0 Kudos
NigelAlford
Occasional Contributor
Ben,
I love what you've created, and yes that' exactly what i'm going for.  I'll dig through your code a bit more to work on implementing it vs what I have.  So my problem lies in not being able to manipulate the layer after its created (dynamically).  You may have solved this in your code based on how your creating each layer.  I'll have a go with this and get back to you, your seperate config.js sheet is a cleaner way for app maintenance which is the ultimate goal of my app as well.

This is the click function that I'm using to look through the layers and toggle visibility(main goal). I find it a bit quicker to create an array and manipulate it during layer resume/suspend and use that for referencing if the layer is in the map vs calling the esri method.

var setChkBox = function(lyr, layerName) {
    "use strict";
   if(lyrIDs.indexOf(lyr) > 1) {
     layerName.suspend();
     lyrIDs.splice(lyrIDs.indexOf(lyr), 1);
   } else if (!(lyrIDs.hasOwnProperty(lyr))) {
     lyrIDs.push(lyr);
     layerName.resume();
   }
};
0 Kudos
BenFousek
Deactivated User
Glad to help. Couple of things:

1) Keep in mind that the esri api is simply an extension of the dojo api. I spend way more time working with dojo than with the esri api. If you want to build powerful, high functioning apps, you must know dojo.

2) I mentioned adding your own objects to existing objects. When doing this you need to make sure the class doesn't use the same name. When 3.0 was released I ran into problems because the api added new objects and functions to classes with names I was using for objects and functions.

3) You can create your controls in the same function as you create the layer. The snippet I shared sends the layer object to a toc widget which adds the controls, menus, etc, but it's easily done with some simple dojo.

Suppose we have a region and we want add simple layer toggle controls to it:
[HTML]<div id="left" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'left'">

</div>[/HTML]

In that same function (truncated for ease of reading) we build a control, place it and add layer toggle functionality to the checkbox:
ms: function (l) {
    if (l.type === 'dynamic') {
        var imageParameters = new esri.layers.ImageParameters();
        imageParameters.format = l.imageFormat;
        imageParameters.dpi = l.dpi;
        var layer = new esri.layers.ArcGISDynamicMapServiceLayer(l.url, {
            id: l.id,
            imageParameters: imageParameters,
            visible: l.visible,
            opacity: l.opacity
        });
        layer.layer_params = l; //add params to layer object
  app.map.addLayer(layer);
  
  //create control
  var control = '<div style="padding:6px 0 3px; margin-bottom:3px; border-bottom:solid 1px #000;">'
   + '<input id="' + l.id + '_layer_control" />'
   + '  '
   + l.name
   + '</div>';
  //place contol
  dojo.place(control, 'left', 'first');
  
  //create checkbox
  var checkbox = new dijit.form.CheckBox({
   checked: l.visible, //set checked same as default visibility
   onChange: function () { //onChange function to toggle layer using the layer object itself
    if (layer.visible) {
     layer.hide()
    } else {
     layer.show()
    }
   }
  }, l.id + '_layer_control');
  
    } else if (l.type === 'tiled') {
        //add tiled layer
    }
}


Once again the indents are goofy.
0 Kudos
NigelAlford
Occasional Contributor
You helped get me to the place I needed.  I'm using Jquery heavily these days and your dynamic add of the checkbox was exactly what I was missing.  I added a jquery button dynamically and the problem was solved in minutes.  The dynamic layer adding is beautiful and makes app maintenance so much better.

Thanks a lot
0 Kudos