Multiple Groups for Add Data Widget

Document created by khibmaesri-ca-esridist Employee on Dec 5, 2018
Version 1Show Document
  • View in full screen mode

I've worked out a way to allow multiple groups (curated content) inside the Add Data widget. I'm sharing this hack as this has been requested by a few people: add Open Data to Add Data Widget and allow multiple Curated Categories 

A couple things to note:

  1. There are probably better ways to do this: like make it dynamic instead of all the hard coded references
  2. This modifies the Add Data code once the widget has been deployed. As such, you cannot go and modify the app-widget using the builder.
  3. These changes were made against WAB 2.10

 

After creating your App with an Add Data widget, you'll need to update 4 files

  • Configs\AddData\config_widget_AddData_Widget_xx.json
  • Widgets\AddData\search\templates\ScopeOptions.html
  • Widgets\AddData\widget.js
  • Widgets\AddData\search\ScopeOptions.js

 

The first step is to turn the config of Curated items into a list. You can add as many into the list as you want. The rest of the workflow has been setup for 6 groups. If you want more than 6, that's fine, just make sure to match relevant nodes. Yes, the syntax is a little strange with the 'Curated' key:value pairing inside the list. This was done to reduce the number of JavaScript code changes.

"Curated":[{     
      "Curated":{
        "allow": true,
        "label": "Group 1",
        "filter": "group:abcdef12346"
       }
    },
    {
      "Curated":{
        "allow": true,
        "label": "Group 10",
        "filter": "group:zywyx987"
      }
}],

You then need to update the HTML template. The important point here is the data-dojo-attach-point with the 0-indexed buttons. If you have more than 6 groups you'll need to continue adding them into this template file and updating the value. If you have less than 6 groups within the config, that's fine, the code will remove them from the widget UI.

<button type="button" class="btn btn-sm btn-default" name="${id}_scope"
      data-dojo-attach-point="CuratedToggle0"
      data-dojo-attach-event="onClick: optionClicked"
      data-option-name="Curated">
      ${i18n.search.scopeOptions.curated}
    </button>
    <button type="button" class="btn btn-sm btn-default" name="${id}_scope"
      data-dojo-attach-point="CuratedToggle1"
      data-dojo-attach-event="onClick: optionClicked"
      data-option-name="Curated">
      ${i18n.search.scopeOptions.curated}
    </button>
      <button type="button" class="btn btn-sm btn-default" name="${id}_scope"
      data-dojo-attach-point="CuratedToggle2"
      data-dojo-attach-event="onClick: optionClicked"
      data-option-name="Curated">
      ${i18n.search.scopeOptions.curated}
    </button>
      <button type="button" class="btn btn-sm btn-default" name="${id}_scope"
      data-dojo-attach-point="CuratedToggle3"
      data-dojo-attach-event="onClick: optionClicked"
      data-option-name="Curated">
      ${i18n.search.scopeOptions.curated}
    </button>
    <button type="button" class="btn btn-sm btn-default" name="${id}_scope"
    data-dojo-attach-point="CuratedToggle4"
    data-dojo-attach-event="onClick: optionClicked"
    data-option-name="Curated">
    ${i18n.search.scopeOptions.curated}
    </button>
    <button type="button" class="btn btn-sm btn-default" name="${id}_scope"
    data-dojo-attach-point="CuratedToggle5"
    data-dojo-attach-event="onClick: optionClicked"
    data-option-name="Curated">
    ${i18n.search.scopeOptions.curated}
    </button>

 

The changes to widget.js are straight forward. Around line 123, you need to comment out the initOption check and add code to loop through instead. (Remember the config was turned into a list of groups)

 

        //initOption(options,"Curated");
        // Loop through the list of Curated items from the JSON
        options["Curated"].forEach(function(c){
          initOption(c,"Curated");
        });

Also within the _initTabs function, we need need to disable the chkAllowSearch('Curated') -- about line 350. We're simply skipping this because if you're going to the work of updating the config with more groups to search, why do you need to do an allow-check? If you don't comment this out, further work on fixing the chkAllowSearch will be required.

   //chkAllowSearch("Curated");

The ScopeOptions.js requires the most updates. (Changes are listed here, but it'd be easier to grab them from the zip file)

Update the initOptions function

      initOptions: function() {
        var context = this.searchPane.searchContext;
        var hasUsername = (typeof context.username === "string" && context.username.length > 0);
        //var hasOrgId = (typeof context.orgId === "string" && context.orgId.length > 0);
        var options = this.getConfig().scopeOptions;
        //This will be incorrectly set, but we won't use this any more
        //  CuratedFilter is handled further on with the togglebutton ID
        this.curatedFilter = options.Curated.filter;
        var activeNode = null;

        var initOption = function(name, node) {
          if (typeof name === 'string' || name instanceof String) {
            var opt = options[name];
            var cure = false;
          // Curated is no longer a string, special handling
          } else {           
            var cure = true;
            var opt = name['Curated'];
          }
         
          if (opt && opt.allow) {
            if (typeof opt.label === "string" && lang.trim(opt.label).length > 0) {
              util.setNodeText(node,lang.trim(opt.label));
            } else {
              if (!hasUsername && name === "MyOrganization") {
                // "My Organization as a label doesn't make sense
                util.setNodeText(node,i18n.search.scopeOptions.anonymousContent);
              }
            }
            //Special handling of Curated
            if (options.defaultScope === name || cure) {
              activeNode = node;
              //Using the id of the ToggleButton to hold the filter
              node.id = opt.filter;
            }
          } else {
            node.style.display = "none";
          }
        };
        initOption("MyContent", this.MyContentToggle);
        initOption("MyOrganization", this.MyOrganizationToggle);
        initOption("ArcGISOnline", this.ArcGISOnlineToggle);
        //initOption("Curated", this.CuratedToggle);
        //As Curated is now a list, we need to loop through and match them to a ToggleButton
        var curArray = {
          0: this.CuratedToggle0,
          1: this.CuratedToggle1,
          2: this.CuratedToggle2,
          3: this.CuratedToggle3,
          4: this.CuratedToggle4,
          5: this.CuratedToggle5
        }
        options["Curated"].forEach(function(c, i){
            initOption(c, curArray[i]);
          });

        // Hide all the ToggleButtons that we don't need
        for(var i= options["Curated"].length; i < Object.keys(curArray).length; i++ ){
          curArray[i].style.display = "none";
        }     
     
        if (!activeNode) {
          if (options.MyOrganization.allow) {
            activeNode = this.MyOrganizationToggle;
          } else if (options.ArcGISOnline.allow) {
            activeNode = this.ArcGISOnlineToggle;
          //Allow is an element on the Curated item (within the list)
          //} else if (options.Curated.allow) {
          } else if (options.Curated) {
          // This is actually wrong, but never seems to be called
            activeNode = this.CuratedToggle;
          } else if (options.MyContent.allow) {
            activeNode = this.MyContentToggle;
          }
        }
        if (activeNode) {
          domClass.add(activeNode, "active");
          this.scopePlaceholderText.innerHTML = activeNode.innerHTML;
        }
      },

Update the  appendQueryParams function from the start to just before the if-scope per content grouping logic

      appendQueryParams: function(params, task) {
        var scope = null;
        var filter = null;
        array.some(this.btnGroup.children, function(node) {
          if (domClass.contains(node, "active")) {
            scope = node.getAttribute("data-option-name");
            //Always set the filter to the node.id
            //  Only the Curated have an appropraite filter set here, we'll
            //  straigthen this out below
            filter = node.id;
            return true;
          }
        });
        if (typeof scope === "undefined") {
          scope = null;
        }
        //console.warn("scope",scope);

        var q = null;
        var curatedFilter = null
        // If Curated, we have a good filter
        if (scope === "Curated"){
          curatedFilter = filter;
        // Otherwise fall back to the filter that was set in Init
        }else{
          curatedFilter= this.curatedFilter;
        }
        var context = this.searchPane.searchContext;
        var username = context.username;
        var orgId = context.orgId;
        var considerOrg = true;
        if (context.portal && context.portal.isPortal) {
          considerOrg = false;
        }
2 people found this helpful

Attachments

Outcomes