popups

This bundle provides support for displaying custom contents (feature information and title) and custom actions based on the clicked feature in map popups.

It provides also a way to configure the behavior of the popup, like docking.

It also provides the mechanism for adding a default popup. Without the need of manual configuration, layers that do not initially have a popup configured, will get the default popup type assigned.

Implementors of custom content only need to comply with the interfaces defined by this bundle to get their content into a popup, while app creators can easily link layers of their map configuration to custom content by type IDs. See this bundle's API docs for details.

Usage

You can control the popup behaviour like docking by setting various options as described in the Configuration Reference.

Default Popups

If the defaultPopupEnabled property is true the defaultPopupType is added to any map image and feature layer which do not declare a popup template.

A special case are WMS Layer, in this case the defaultPopupEnabled will automatically set the popupEnabled to true in all sublayers of a WMS. You can prevent this behavior by setting the flag popupEnabled to false on a WMSLayer.

You will require to add the popups-default bundle to your app to get the default popups working in your app. The PopupDefinitionFactory implementation for the defaultPopupType value default is located there. See popups-default for further configuration capabilities.

Custom Popups

This bundle provides the api to integrate custom popup content. To make use of custom popups you need following:

  1. Integrate one or more bundles which provide custom popup types. See Implementing a PopupDefinitionFactory.
  2. Reference a popup type in a layer's popupTemplate configuration:
    {
        "load": {
            "allowedBundles": [
                ...
                "popups",
                "my-popups"
                ...
            ]
        },
        "bundles": {
            "map-init": {
                "Config": {
                    "map": {
                        "layers": [
                            {
                                ...
                                "popupTemplate": {
                                    "popupType": "my-popup"
                                }
                            }
                        ]
                    }
                }
            }
        }
    }
    

Custom Actions

This bundle provides the api to integrate custom actions. To make use of custom actions you need following:

  1. Integrate one or more bundles which provide custom action types. See Implementing a ActionFactory.
  2. Reference a action type in a layer's popupTemplate configuration:
    {
        "load": {
            "allowedBundles": [
                ...
                "popups",
                "my-actions"
                ...
            ]
        },
        "bundles": {
            "map-init": {
                "Config": {
                    "map": {
                        "layers": [
                            {
                                ...
                                "popupTemplate": {
                                    "customActions": ["my-action"]
                                }
                            }
                        ]
                    }
                }
            }
        }
    }
    

Configuration Reference

The following sample shows all configurable properties and their default values of this bundle:

"popups": {
    "Config": {

        // default popup options
        "defaultPopupType": "default",
        "defaultPopupEnabled": true,

        // popup behavior options
        "highlightEnabled": true,
        "autoCloseEnabled": false,
        "collapseEnabled": false,
        "dockingForced": false,
        "dockingDisabled": false,
        "dockEnabled": false,
        "dockPosition": "auto",
        "dockButtonEnabled": true,
        "dockBreakpoint": true
    }
}
PropertyDescription
defaultPopupTypeId of the default popup type.
defaultPopupEnabledDetermines if default popups are enabled.
highlightEnabledHighlight the selected popup feature. Note: this is working only in 3D (Details).
autoCloseEnabledThis closes the popup when the View camera or Viewpoint changes. (Details).
collapseEnabledIndicates whether to enable collapse functionality for the popup. (Details).
dockingForcedForce all popups to be docked. See here.
dockingDisabledDisable docking for all popups. See here.
dockEnabledIndicates whether the placement of the popup is docked to the side of the view. (Details).
dockPositionThe position in the view at which to dock the popup. (Details).
dockButtonEnabledIf true, displays the dock button. If false, hides the dock button from the popup. (Details).
dockBreakpointDefines the dimensions of the View at which to dock the popup. (Details).

Constraints

Use Cases

Force docking for all popups

The simplest way to force a docked popup is to use the flag dockingForced.

"popups": {
    "Config": {
        "dockingForced": true
    }
}

This is equal to declaring following properties:

"popups": {
    "Config": {
        "dockEnabled": true,
        "dockButtonEnabled": false,
        "dockBreakpoint": false
    }
}

Disable docking for all popups

You can use the dockingDisabled option to disable docking behaviour:

"popups": {
    "Config": {
        "dockingDisabled": true
    }
}

This automatically disables the popup button and breakpoint:

"popups": {
    "Config": {
        "dockEnabled": false,
        "dockButtonEnabled": false,
        "dockBreakpoint": false
    }
}

Implementing a custom popup definition factory

To implement a custom popup definition factory, you need to implement the PopupDefinitionFactory interface as defined in the API.

This requires to provide a CustomPopupDefinition which implements the PopupDefinition interface

export default function CustomPopupDefinition() {
    return {
        resolvePopupTemplate(layerOrSublayer) {
            // use the layer to get metadata e.g. about fields.
            // create a custom PopupTemplate.
            return {
                title: "'{title}'",
                content: "objectId is '{objectid}'"
            };
        };
    };
}

Next a CustomPopupDefintionFactory which implements the PopupDefinitionFactory interface is required.

import CustomPopupDefinition from "./CustomPopupDefinition";
export default function CustomPopupDefinitionFactory() {
    return {
        getTypes() {
            return ["custom"];
        },
        createPopupDefinition(type) {
            switch(type){
                case "custom":
                    return new CustomPopupDefinition();
                default:
                    throw new Error("unsuported type");
            }
        }
    }
}

Then, register an instance of this factory at the component system. Make sure it is provided as popups.PopupDefinitionFactory in the bundle's manifest.json:

{
    "components": [
        {
            "name": "TweetablePopupDefinitionFactory",
            "provides": "popups.PopupDefinitionFactory",
        }
    ]
}

Implementing a custom popup definition which provides custom widgets

If the features of a PopupTemplate are not sufficient then it is possible to create custom widgets using the following approach:

export default function CustomWidgetPopupDefinition() {
    return {
        resolvePopupTemplate(layer) {
            return {
                // declare which fields should be fetched
                // these are provided as attributes inside the graphic
                fields: ["objectid", "Name", "Description"],
                
                // title may contain attributes from the fields array
                title: "This is {Name}",
                
                // define a content function
                // NOTE:
                //  * can return a Promise(dojo/Deferred required in 4.7)
                //  * this is global window, be careful about that!
                content( {graphic}) {
                    let widget = layer._$popup_widget;
                    if (!widget) {
                        widget = layer._$popup_widget = createWidget();
                        // widget is a dijit Widget
                        widget.startup();
                    }
                    updateWidgetContent(widget, graphic);
                    return widget;
                }
            };
        }
        cleanupPopupTemplate(layer) {
            let widget = layer._$popup_widget;
            delete layer._$popup_widget;
            widget && widget.destroyRecursive();
        }
    };
}

Implementing a custom action factory

To implement a custom actions factory, you need to implement the ActionFactory interface as defined in the API. An action factory creates Action instances (see API documentation for supported types).

function TweetActionFactory() {
    return {
        createAction(type) {
            return {
                id: "tweetablePopups.action.tweet",
                type: "button",
                title: "Tweet Feature",
                className: "esri-icon-share2",
                trigger(context){
                    // Code to be performed, when the action is triggered,
                    // i.e. someone clicks the link that is displayed in the popup.

                    const features = context.features; // All features that were hit by clicking on the map.
                    const selectedFeature = context.selectedFeature; // The currently selected and visible feature in the popup.
                    const location = context.location; // The point position were the popup was opened on the map.

                    // Tweet it: e.g. selectedFeature.attributes.description ...
                }
            };
        },
        getTypes() {
            return ["tweet"];
        }
    }

Then, register an instance of this factory at the component system. Make sure it is marked to provide the popups.ActionFactory interface in the bundle's manifest.json:

{
    "components": [
        {
            "name": "TweetActionFactory",
            "provides": "popups.ActionFactory"
        }
    ]
}

Another option is to implement an Action class by extending from popups/Action:

import Action from "popups/Action";

class MyCustomAction extends Action {

    trigger(context) {
        // do something when action is clicked
    }
}
export default MyCustomAction;

An instance of your class can then be returned by the createAction() method of your ActionFactory - remember to provide id and title properties in the constructor:

import MyCustomAction from "./MyCustomAction";

class MyCustomActionFactory {
    getTypes() {
        return ["mycustomaction"];
    }
    createAction(type) {        
        return new MyCustomAction({
            id: type,
            title: "customAction"
        });
    }
}
export default MyCustomActionFactory;

Display actions dependant on the selected feature

You can provide the method isVisibleForFeature(feature) in your custom action implementation that should return true if you want your action to be displayed for that feature and false if it should be hidden.

If you do not provide that method, actions are visible for every feature (of a layer the action is configured for) by default.

Using the provided default popup only for specific layers

In some cases, you might not want to use the provided default popup from the popups-default bundle in all of your layers (without a configured popup template), but only in some specific layers. You have two options to do this:

  1. Leave the popup bundle configuration as is and for the layers you want to exclude, in your layer's configuration set popupEnabled to false (as described in the ESRI JS API).
  2. The other option is, to disable the default popup mechanism entirely by setting defaultPopupEnabled of this bundle to false. Then add the popups-default bundle to your app and set the popupType to default only for those layers, you want the popup for.

Using your own popup implementation as a default

As mentioned above, it is possible to configure another popup type to be used as a default popup. To do this, just set the defaultPopupType property to the type that your own custom popup definition factory provides. For example, if you wanted to use the popup from the sample above, set defaultPopupType to tweetable and ensure, that the bundle containing your factory is used in your app.