map.layers.items vs. map.layers._items

3223
6
Jump to solution
09-24-2015 07:22 AM
TyroneBiggums
Occasional Contributor III

What is the proper way to reference a dynamically created graphics layer in 4.0 beta1? When I debug after map.add(graphicsLayer), I get the following data:

map.layers.items.length = 0

map.layers._items.length = 10 (expected number of dynamically added layers)

I've confirmed a map and mapview instance. I create a new GraphicsLayer (providing no id at this time... please see code snippet below). I add a graphic to the graphics layer. Then, I call map.add(graphicsLayer).

var graphicsLayer = new GraphicsLayer({ });       

graphicsLayer.add(graphic);           

map.add(graphicsLayer);

In the above code, I've made sure that:

- graphic's declaredClass is esri.Graphic

- graphic's geometry's declaredClass is esri.geometry.Point and that there is an x/y value.

- graphicsLayer's declaredClass is esri.GraphicsLayer

- graphics do appear on the map

So, my questions:

1. Why are there API objects prefixed with an underscore? Are they intended for internal use only? (I've noticed this in 3.x and 4.0 beta 1)

2. Why is there data in map.layers._items but not map.layers.items?

3. If I programmatically set a graphicslayer id, I can reference the layer by id. But, without setting an id, the id is automatically generated and I won't know the id in code. What's the proper way to reference a dynamically created layer without a provided id?

Slightly OT and not as important: Did 3.x code allow for graphicsLayers (or other layers) to have the same id as other layers on a map? I noticed a hiccup in my code where a set id is blank, but all of the graphicsLayers still appeared on the map in 3.x but not for 4.0beta1.

Thanks,

D

Tags (1)
1 Solution

Accepted Solutions
KristianEkenes
Esri Regular Contributor

1. Yes, API objects prefixed with an underscore are intended for internal use only.

2. I'm not able to reproduce this. Can you post a live sample where we can see an empty collection in map.layers.items?

3. I'm not sure there's one proper way to reference layers programmatically. It all depends on the workflow, but there are a number of ways you can go about it.

A review of Collections would probably be helpful here. I noticed you're drilling down to the items property in order to get the length, or number of layers in the map.

map.layers.items.length is not the proper way of getting the length. You should be using map.layers.length instead. Collection.items is a property of the collection that is an array, but we don't document this because and we don't for a reason. That is because you should use the methods of Collection when working with items in the collection. Collections act much like arrays but can be more helpful because you can leverage the change event to know when an item has been added, removed, or moved inside the Collection. Arrays don't allow you to do this. So you can reference layers using any of the methods available in Collection - getItem(), getItemAt(), findIndex(), map(), filter(), etc. In short - take advantage of Collection; it has the same capabilities of an array, but with the extra event handling as well. Collection.items is an array, which means you can't take advantage of the change event.

I hope this helps. We plan on adding additional documentation to the page on Collections so that it is more clear how to use them. Also, keep in mind that the method names may change to more closely match the corresponding methods in Array.

View solution in original post

6 Replies
KristianEkenes
Esri Regular Contributor

1. Yes, API objects prefixed with an underscore are intended for internal use only.

2. I'm not able to reproduce this. Can you post a live sample where we can see an empty collection in map.layers.items?

3. I'm not sure there's one proper way to reference layers programmatically. It all depends on the workflow, but there are a number of ways you can go about it.

A review of Collections would probably be helpful here. I noticed you're drilling down to the items property in order to get the length, or number of layers in the map.

map.layers.items.length is not the proper way of getting the length. You should be using map.layers.length instead. Collection.items is a property of the collection that is an array, but we don't document this because and we don't for a reason. That is because you should use the methods of Collection when working with items in the collection. Collections act much like arrays but can be more helpful because you can leverage the change event to know when an item has been added, removed, or moved inside the Collection. Arrays don't allow you to do this. So you can reference layers using any of the methods available in Collection - getItem(), getItemAt(), findIndex(), map(), filter(), etc. In short - take advantage of Collection; it has the same capabilities of an array, but with the extra event handling as well. Collection.items is an array, which means you can't take advantage of the change event.

I hope this helps. We plan on adding additional documentation to the page on Collections so that it is more clear how to use them. Also, keep in mind that the method names may change to more closely match the corresponding methods in Array.

TyroneBiggums
Occasional Contributor III

1. Are there any other stand-out no-nos to consider in using the API along with underscore prefixed objects and .items on collections? It's difficult to stray from something that pops up in intellisense and provides the developer with the expected data when the developer isn't very familiar with the API to begin with. Is there ever an instance where .items is useful to the developer? I'm guessing no? Right now the only thing item returns in the 4.0 beta reference pertains to Collection.

2. I'm unable to post a live sample because limited resources unless there is an Esri play ground that I don't know about like jsfiddle? Otherwise, I'll try and whip something up on jsfiddle and cross my fingers that it renders. It's possibly irrelevant if I shouldn't be using .items at all. I will look into that farther using Collection methods.

3. In this particular example, I have a file browse for the user to select a CSV file. I convert the file to a JSON object and iterate through that object to add X, Y to the graphicLayer's graphic collection. I believe the graphicLayer is being added to the map given the code in my OP since I do see points on the map. I provide the graphicLayer an ID (not in the example above, I know, but it's there in my code). When I iterate through the JSON object, I want to check if a layer exists for that collection of points. If it doesn't exist, I create a layer for that collection. This is because I'm using a generic custom JavaScript object. I could be adding any layer type from anyone in the application. The code needs to add an item to the right Collection. This is why I want to reference the layer by ID. With that said, my pseudo code to reference the layer and it's Collection is:

layer = map.getLayer(myJSONObject.LayerId)

if (layer) {    

     layer.add(graphic)

}

else {

     layer = new graphicLayer { id: myJSONObject.LayerId }

     layer.add(graphic) // to add the first graphic from JSON after a layer is created for it

}

First time through, the JSON object finds no layer associated to it so it creates a new graphicLayer. All succeeding iterations will add a graphic from the JSON object to the graphic collection in that layer.

So Collections are for playing with the data and to reference the layer, I'll want to stick with map.getLayer(id). Is that about right? That's kind of (if not exactly) what I'm doing right now in code. What spawned this post is that I added several layers at once and wanted to test how many were showing on the map. I'm assuming that I could map.getLayer by Id for any of those layers. I only tested map.layers.items which returns [], so I thought something was wrong.

If I don't know the Id of a particular layer, I will have to use map.layers.length so I will know the index to grab the layer by index through the Collection. Is that also right?

0 Kudos
KristianEkenes
Esri Regular Contributor

Right now, I can't think of a reason to directly use items.

So Collections are for playing with the data and to reference the layer, I'll want to stick with map.getLayer(id). Is that about right? That's kind of (if not exactly) what I'm doing right now in code. What spawned this post is that I added several layers at once and wanted to test how many were showing on the map. I'm assuming that I could map.getLayer by Id for any of those layers. I only tested map.layers.items which returns [], so I thought something was wrong.

Yes, map.getLayer(id) should be fine for your workflow. It will work for any of the layers in the map.layers collection. map.layers.length will tell you the number of operational layers that are added to your map. If you don't know the id, but you know the index, you can use getItemAt(index) on the collection. For example:

layer = map.layers.getItemAt(index);

TyroneBiggums
Occasional Contributor III

After messing around with the layers object a little more, I notice that map.layers.items is rather redundant. I also understand that map.layers.length is the correct way to see how many layers I have. But, I do not see how map.layers.length can be 2 (for example), and map.layers[0] is undefined. It does not seem intuitive for a developer that is not familiar with the API (including 3.x). I do understand that layers is a Collection, and I should be using Collection methods, but I'm also trying to grasp how to be fluent in the API. I see that map.layers.getAll() returns an array of objects. Does that mean that it is proper to use map.layers.getAll()[0] to reference an element of that array?

And, if map.layers IS a Collection, and I have not explicitly required esri/core/Collection, is there ever a need to require esri/core/Collection or is it always going to be inherited internally through the API?

Thanks again,

D

0 Kudos
KristianEkenes
Esri Regular Contributor

You will only need to require esri/core/Collection if you need to explicitly create your own collection, which you probably won't have to do most of the time. It is inherited by the classes that use it. So although you may not require esri/core/Collection in your app, you can still use its methods to work with collections like layers and graphics.

A Collection is an object, not an array, so working with collections feels a bit awkward at times. That's why map.layers[0] returns undefined, because layers isn't an array.

As I referenced in the previous comment I wrote a few minutes ago, you can use .getItemAt() directly on any collection to get an item at a specific index. While map.layers.getAll()[0] works, it's a little simpler to write, map.layers.getItemAt(0). Both return the same result, but the latter is the preferred method of doing so.

You can also use other methods to get items if you don't now the id or the index - find(), filter(), and map() can help you get the item in the collection you may be looking for.

TyroneBiggums
Occasional Contributor III

You've been very helpful, thank you. Really all the answers are the correct answer, and the last two are the most helpful to me. But, per the subject, the first response answered those questions. Thank you for answering the follow-ups, too.

0 Kudos