FeatureTable widget with a map

Explore in the sandboxView live

This sample demonstrates how to add a FeatureTable widget to your application. The FeatureTable widget allows users to view and sort data and attributes from a FeatureLayer. In this example, the table is displayed in addition to its associated map.

It is possible to select a feature in the table and have its associated feature automatically reflect as highlighted in the map. In order for this to work correctly, the table's view property must be set. Currently, there is no direct way in the API to wire up selecting a feature in the map and have its associated row in the table selected. This sample demonstrates how to select a feature in the map and have that selection reflected within the table.

In addition, this sample also demonstrates how to filter and have the table display only the associated features that are currently displayed within the map. It does this by listening for when the view's extent updates. Whenever a view's extent changes, the FeatureTable's filterGeometry is set to this new extent and only those features that fall within this area display.

How it works

First, we add the FeatureTable with a specified FeatureLayer and field column templates to display. The view must also be set in order for any selected row(s) in the table to display its associated feature(s) as highlighted in the map. In addition, we remove the Zoom to selection visibleElement since it is not needed as we are already filtering by the view's extent.

Use dark colors for code blocksCopy
                                                         
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
view.when(() => {
  //grabs the first layer in the map
  const featureLayer = webmap.layers.getItemAt(0);
  featureLayer.title = "USFS Recreational areas";

  // Create the feature table
  const featureTable = new FeatureTable({
    layer: featureLayer,
    visibleElements: { // autocast to VisibleElements
      menuItems: {
        clearSelection: true,
        refreshData: true,
        toggleColumns: true,
        selectedRecordsShowAllToggle: true,
        selectedRecordsShowSelectedToggle: true,
        zoomToSelection: false
      }
    },
    view: view, // required for feature highlight to work
    tableTemplate: { // autocast to TableTemplate
      columnTemplates: [ // takes an array of FieldColumnTemplate and GroupColumnTemplate
      { // autocast to FieldColumnTemplate
        type: "field",
        fieldName: "RECAREANAM",
        label: "Recreation area name",
        direction: "asc"
      },
      {
        type: "field",
        fieldName: "FORESTNAME",
        label: "Forest name"
      },
      {
        type: "field",
        fieldName: "OPENSTATUS",
        label: "Open status"
      },
      {
        type: "field",
        fieldName: "OPEN_SEASO",
        label: "Season begins"
      },
      {
        type: "field",
        fieldName: "RECAREADES",
        label: "Recreation description"
      },
      {
        type: "field",
        fieldName: "RESTRICTIO",
        label: "Restrictions"
      }]
    },
    container: document.getElementById("tableDiv")
  });
});
...

Next, listen to the table's selection-change event. If the row (feature) is checked, or added to the selected features, add it to the features[] array. If unchecked, remove it.

Use dark colors for code blocksCopy
                   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Listen for the table's selection-change event
featureTable.on("selection-change", (changes) => {
  // If the selection is removed, remove the feature from the array
  changes.removed.forEach((item) => {
    const data = features.find((data) => {
      return data.feature === item.feature;
    });
    if (data) {
      features.splice(features.indexOf(data), 1);
    }
  });
  // If the selection is added, push all added selections to array
  changes.added.forEach((item) => {
    const feature = item.feature;
    features.push({
      feature: feature
    });
  });
});

Next, listen for when the view's extent updates. Once this happens, pass the updated extent to the table's filterGeometry.

Use dark colors for code blocksCopy
         
1
2
3
4
5
6
7
8
9
featureLayer.watch("loaded", () => {
  reactiveUtils.when(() => view.stationary === false, () => {
    // Get the new extent of view/map whenever map is updated.
    if (view.extent) {
      // Filter and show only the visible features in the feature table
      featureTable.filterGeometry = view.extent;
    }
  });
});

Lastly, listen for the view's immediate-click event. It performs a hitTest on the point location and, if applicable, selects the corresponding feature's row in the table.

Use dark colors for code blocksCopy
          
1
2
3
4
5
6
7
8
9
10
// Listen for the click on the view and select any associated row in the table
view.on("immediate-click", (event) => {
  view.hitTest(event).then((response) => {
    const candidate = response.results.find((result) => {
      return result.graphic && result.graphic.layer && result.graphic.layer === featureLayer;
    });
    // Select the rows of the clicked feature
    candidate && featureTable.selectRows(candidate.graphic);
  });
});

Known Limitations

For a comprehensive list of limitations, please refer to the widget's API Reference documentation.

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.