Experience Builder Tips and Tricks - Page 3

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Latest Activity

(39 Posts)
JeffreyThompson2
MVP Regular Contributor

Many of the more advanced options for the Editor Widget are handled indirectly through the Smart Forms functionality in the Web Map Viewer. Smart Forms can be used to add Arcade expressions, extract data from other layers, set a priority level for different layers, populate features with default values, fetch location attributes, and setting fields to be editable, required or hidden.

See the official ESRI blog post for more information.

Here's an article describing how to override calculated values. 

more
1 0 235
JeffreyThompson2
MVP Regular Contributor

The following is copied from this post by @Shen_Zhang. This is my quick summary of the method.

  1. Create an Experience that does whatever you want it to do.
  2. Create a second Experience that is just an Embed widget.
  3. Stuff the first Experience into the second Experience.

Filters set in the data source tab in experience builder will be applied automatically, any further filters made from URL parameters will not replace but added to the current filter. However, it is possible to embed app URL with data_filter parameter to an embed widget. Users can neither see filters applied nor modify them. 

If you would like to make the embedded app to be more flexible, try a dynamic URL - you can replace any value you were using and pass URL parameters from the app URL to the Embed widget at run time to control what appears in the embedded contents. Here's what I tried to do with my configuration:

1. Create a new app and add an Embed widget with a map and a table

2. Copy and paste my app with a filter (here I use dsId:OID<20 as an example). Save the new app, append &exampleParam= to the end of app URL, and refresh the app

JeffreyThompson2_0-1700678697236.png

 

3. From Embed widget setting, you will see exampleParam in URL info. It can be added to the embedded URL by clicking on it. You can replace any content with {appURL.search.exampleParam}. Let's say we replace value "20" - now the filter changes to 

data_filter=dataSource_3-Feature_NAD1983_8510:OID%3C{appURL.search.exampleParam}

 4. Now save the app, publish and view the app. The app launches with no data in it (as the SQL expression in the Embed widget is incomplete and invalid)

JeffreyThompson2_1-1700678697292.png

 

5. Add ?exampleParam=20 to the URL. Now the app displays with the corresponding filter applied. 

JeffreyThompson2_2-1700678697297.png

 

I think this might be helpful as you could customize the parameter name (add multiple parameters if you want), also it gives you the flexibility to control what appears in the app to save some trouble. When deleting the parameter the users will only get an empty app - but if they managed to figure out what it is for (like setting a huge value in my case above), they may still able to see the whole dataset.

more
1 0 668
MichaelGaiggEsri
New Contributor III

This is a common problem when configuring mobile screens. You have two instances of the section widget and the names of the views are now duplicated which for some reason isn't allowed (probably the name doubles as the id) - but then name is also the label on the views navigation. The builder will automatically append a number (probably 2) to your label.

MichaelGaiggEsri_0-1699577771336.png

 

The solution: Just add a space before AND after the label. In that way the view name is unique and it's also centered. If you need it a third time, add two spaces before and after the label, voila. Rinse and repeat as needed.

more
3 2 551
JeffreyThompson2
MVP Regular Contributor

This post is only for people using Developer Edition, if you are on the Online or Enterprise Edition, go away. Go away and be grateful that you do not have to read on, only madness lies ahead.

Hi coders, if you are reading this, you have decided to use Experience Builder Developer Edition. I commend your bravery, if not necessarily your wisdom. Have you used React before? If not, getting started in Developer Edition is going to be a rough ride. My number one tip for anyone booting up Developer Edition for the first time is to stop and spend several days studying React before ever trying to install Developer Edition. And yes, I do mean days. It takes that long to get enough of a grasp on the basics to begin to be able to actually use it. It is different enough from regular javascript that I think it is fair to classify it as an entirely separate language. React operates very differently than any programing language you have used before with the possible exception of Angular or Vue.js, which are other React-like frameworks. Using React is sort of like dancing, if you and your gorgeous bride React are in sync, it will be a beautiful experience and if not, you will have several broken toes and a visit to a divorce attorney.

I cannot teach you React. It is too big a subject and I am not an expert. Here is the official React guide. Start learning here. But I will share what I think are the most important concepts to keep in mind. Some of the concepts in React get a bit circular, but I will try to organize this post the best I can. Just trust that if I throw out a new term, I will get around to explaining it later. I am not sure if it would be better to read this or the official React guide first. Maybe make it a sandwich. Read this. Then, read the official guide and then read this again when it sounds slightly less insane.

React: A History

We will start with a little history lesson. I swear this is relevant. React was invented by Facebook in 2013 and became open-source in 2014. React was invented to solve a problem Facebook had users would scroll through their newsfeed and when they hit the bottom of the page, they might visit another website or even worse, turn off their computers. React was designed to continuously get or send data to or from an API without ever needing to reload the page, so now scroll all you want, you will never see the bottom of your Facebook feed.

Which brings me to the first Experience Builder relevant fact about React, when within an Experience, the page never reloads. You may have multiple pages within your Experience, but React is kind of lying to you, everything is happening in a single page and just showing you different things. This is generally speaking a good thing as it usually results in a faster, smoother user experience, but it may become an issue when combined with a quirk of Experience Builder. Typically on a React page, if you as a user click an X to close a popup, that popup would be destroyed and a new one created if you reopened the popup. But Experience Builder never destroys widgets, even when switching between pages. This can lead to some unexpected behavior, if you do not anticipate it.

Back to the history lesson.

In the beginning, React allowed for components to be made within classes or functions. Using classes had some minor advantages, so they were the preferred method, until 2018 when React Hooks came out. All of the React methods starting with use, like useState or useEffect, are Hooks and Hooks made React so much better and easier. But, Hooks cannot be used in class based components. So, over the next couple of years function based components became the preferred method to write React. Class based components are still 100% valid and functional and the React team swears they will never be removed, but you should consider them depreciated. Always write function based components, if you can. When looking for learning resources, check that they are 2020 or later and if they start talking about classes, move on to something else. The class based React documentation has been buried in the official React docs, but you can find it here.

I'd like to tell you, you can just ignore class based React completely. I'd like to say that, but... look at the dates in that last paragraph, 2018 to 2020. The exact timeframe that Experience Builder was being developed. Much of Experience Builder is in class based components, including most of the widget coding examples provided by ESRI. You may not need to write in class based React, but you will probably need to be able to read it. Sorry. 😢

JSX and the Virtual DOM

When a user loads a React application, the HTML contains only a single node. Everything that happens on the screen is the result of React manipulating what is known as the Virtual DOM. You should not, in standard React, ever attempt to directly manipulate the actual DOM, using methods like innerHTML or appendChild, etc.. Bad things happen to React developers who mess with the DOM.

JSX is a mashup language of HTML and Javascript. All of the HTML elements and React components are valid JSX elements. Every React component must resolve to a single return statement that returns a single JSX node. Like HTML, JSX nodes can contain any number of elements.

A React fragment (it looks like this, <></>) can be used for a component that should not paint something to the screen directly, but will add items through direct DOM manipulation. Direct DOM manipulation should be avoided at all cost, but when using the ESRI Javascript API there is often no way around doing it. Experience Builder 1.13 will be based on the 4.28 version of the API and includes map components that were specifically designed to help deal with this problem. For versions 1.13 or later of Experience Builder, you should never need to directly manipulate the DOM.

State and Re-renders

Components in React frequently re-render. During a re-render a component will clear its memory and forget everything it knows unless a variable is stored in state or a ref. The three triggers for a re-render are a change to state, a change to props, or a re-render higher up in the React tree.

Here is a widget designed to add and remove a layer from a map. It doesn't work quite right. It will add the layer, but it can't remove it.

 

import { React, AllWidgetProps } from 'jimu-core'
import { JimuMapViewComponent, JimuMapView } from 'jimu-arcgis'
import FeatureLayer from 'esri/layers/FeatureLayer'

const { useState } = React

const Widget = (props: AllWidgetProps<any>) => {
  const [jimuMapView, setJimuMapView] = useState<JimuMapView>()
  const layer = new FeatureLayer({
      url: 'URL1'
    })

  const activeViewChangeHandler = (jmv: JimuMapView) => {
    if (jmv) {
      setJimuMapView(jmv)
    }
  }

  const formSubmit = (evt) => {
    evt.preventDefault()
    jimuMapView.view.map.add(layer)
  }
  
const formSubmit3 = (evt) => {
    evt.preventDefault()

    jimuMapView.view.map.remove(layer)
  }
  
  
  return (
    <div className="widget-starter jimu-widget">
      {
        props.useMapWidgetIds &&
        props.useMapWidgetIds.length === 1 && (
          <JimuMapViewComponent
            useMapWidgetId={props.useMapWidgetIds?.[0]}
            onActiveViewChange={activeViewChangeHandler}
          />
        )
      }

      <form onSubmit={formSubmit}>
        <div>
          <button>Add Layer</button>
        </div>
      </form>
	   <form onSubmit={formSubmit3}>
        <div>
          <button>Delete</button>
        </div>
      </form> 
	  
    </div>
  )
}

export default Widget

 

The fundamental problem is that the widget will forget the reference to layer between re-renders, so will not be able to find the layer later to remove it. Here is this widget updated to use state. It works as expected.

 

import { React, AllWidgetProps } from 'jimu-core'
import { JimuMapViewComponent, JimuMapView } from 'jimu-arcgis'
import FeatureLayer from 'esri/layers/FeatureLayer'

const { useState } = React

const Widget = (props: AllWidgetProps<any>) => {
  const [jimuMapView, setJimuMapView] = useState<JimuMapView>()
  const item = new FeatureLayer({
      url: 'URL1'
    })
  const [layer, setLayer] = useState(item)

  const activeViewChangeHandler = (jmv: JimuMapView) => {
    if (jmv) {
      setJimuMapView(jmv)
    }
  }

  const formSubmit = (evt) => {
    evt.preventDefault()
    jimuMapView.view.map.add(layer)
  }
  
const formSubmit3 = (evt) => {
    evt.preventDefault()

    jimuMapView.view.map.remove(layer)
    setLayer(null)
  }
  
  
  return (
    <div className="widget-starter jimu-widget">
      {
        props.useMapWidgetIds &&
        props.useMapWidgetIds.length === 1 && (
          <JimuMapViewComponent
            useMapWidgetId={props.useMapWidgetIds?.[0]}
            onActiveViewChange={activeViewChangeHandler}
          />
        )
      }

      <form onSubmit={formSubmit}>
        <div>
          <button>Add Layer</button>
        </div>
      </form>
	   <form onSubmit={formSubmit3}>
        <div>
          <button>Delete</button>
        </div>
      </form> 
	  
    </div>
  )
}

export default Widget

 

State is not live data. It is a snapshot of data at the last re-render. This can result in all sorts of confusing errors. To make things more confusing, calling useState() does not instantly update state. React cues all of the useState() calls and runs them in order during the re-render.

 

const [num, setNum] = useState(0)
setNum(num + 1) //num would be 1 on the next render.
setNum(num + 5) //Now, num would be 5 on the next render.
setNum(num - 1) //Now, num will be -1 on the next render.
console.log(num) //Output is 0

const [num, setNum] = useState(0)
setNum(num + 1) //num would be 1 on the next render.
setNum(5) //Now, num would be 5 on the next render.
setNum(num - 1) //Now, num will be 4 on the next render.
console.log(num) //Output is 0

 

Confusing, isn't it? Welcome to the wonderful world of React.

React must know what to render at every microsecond. If at any point React does not know what to render, the component will break and display an error message. The most difficult render to handle is often the very first one as it often occurs before the required data is loaded. This is often accomplished with a ternary statement, like the one below. Ternary statements, deconstructing arrays and various ways of testing for truthiness are rarely used features of Javascript that are incredibly important to writing good React.

 

{value ? <MyComponent></MyComponent> : <Loading></Loading>}

 

If you have ever wondered why so many modern websites show a shimmery, data-free outline of the website design before the actual site loads, this is why. (Also, psychological research finds that users that are shown shiny things are more patient with load times and think websites load faster.) The UI StoryBook includes a Loading component and many other useful UI elements.

UseRef()

Other than state, the other way to store data between re-renders is the useRef() Hook. Changes to a ref occur instantly and do not trigger a re-render. It looks something like this.

 

 

const num = useRef(0)
//Use the .current property to access the value of a ref.
console.log(num.current) //Output is 0
num.current = 5
console.log(num.current) //Output is 5

 

UseEffect

UseEffect is the React Hook I use most often in Experience Builder. With useEffect(), you can create a block of code that runs once when the component is mounted, every time a component re-renders, when a specific value or set of values is changed, or when the component is unmounted. But remember that widgets do not fully unmount in Experience Builder, so that last option will not work in the highest level of an Experience Builder widget. Each of these uses of useEffect() has a different syntax. I keep this page bookmarked because I use it all the time to find the proper useEffect() syntax.

Props and One-Way Data Flow

A React application is structured like a tree or at least the part of a tree that is above ground. The stuff closest to the root is referred to as being "high in the React tree" and we get "lower or deeper in the React tree" as we get into sub-components. (I guess people at Facebook don't get outside very often.) Water flows up the trunk, through the branches and out the leaves. Water never goes back down the tree. If a squirrel, starts jumping up and down on a branch, it will (or should) only affect that branch, not the trunk or a totally separate branch. If the whole tree starts shaking when a squirrel jumps on one branch, run away, that tree is about to fall over.

Another common React metaphor is that of children and parents. With the key phrase being, parents are allowed to alter their children, but children are not allowed to alter their parents. (The people at Facebook sound like pretty lousy parents.) 

It's not often shown in the ESRI coding samples, but your widgets can and often should call in sub-components. If you are using any elements from the UI StoryBook, you are already doing this. Adding additional levels and branches within your widgets is often necessary to get your desired outcome. My custom List widget is five levels deep it branches in the middle and comes back together at the lowest level.  Data in React should always* flow from the root, which is much higher in the React tree than anything you should be doing in Experience Builder to the lowest levels, which are your widgets. (Developer Edition comes with a folder called "your-extensions", if you are doing anything outside of that folder, you are probably doing something wrong.) Trying to fight one-directional flow is a React anti-pattern that will infuriate you and probably result in undesired outcomes.

One-way data flow is managed through props. Props must be explicitly passed from a parent component to the next child down in the React tree. The props the child receives are immutable. If the props are changed by the parent, the component will re-render. Children must receive props by passing them as an argument into the component function. It looks something like this.

 

//Passing props down
render(
 <MyComponent myProp={'value'}></MyComponent>
)

//Recieving props
const MyComponent = (props) => {
  console.log(props.myProp) //Output is 'value'
}

 

*Redux Or Breaking One-Way Data Flow

Experience Builder comes packaged with a state manager called Redux. As you read through the React docs, you will encounter a design pattern called "lifting up state".  In lifting up state, we rewrite a set of components containing a parent and two or more children so that state is managed by the parent and passed to the children as props, effectively allowing the children to communicate with each other. State managers take this concept to the logical extreme. They sit at the top of the React tree thus allowing any component to send data up to the state manager and retrieve it in any other component regardless of what branch it is on. ESRI has kindly done all the heavy lifting in setting up Redux, so all you need to know is how to send and receive messages. Note that the messages are received through props, so getting a message will cause the widget to re-render.

 

//To send a message, with a widget id of 'widget_id', use:

//Dispatch- This should be your preferred method, as it is immutiable.
getAppStore().dispatch(appActions.widgetStatePropChange('widget_id','nameOfMessage', message))

//MutableStoreManager- Use this only if you need to send a complex object.
MutableStoreManager.getInstance().updateStateValue('widget_id', 'nameOfMessage', message)

//To read a message

//From Dispatch:
props.stateProps?.nameOfmessage

//From MutableStoreManager:
props.mutableStateProps?.nameOfmessage

 

I think that covers all of the basic information you need to know about React to use Experience Builder Developer Edition and yes, the React iceberg goes much deeper.

Good Luck and May Your Toes Never Be Broken!

more
22 14 4,355
JeffreyThompson2
MVP Regular Contributor

A common request is options in Experience Builder to turn on and off groups of layers at the same time and managing large numbers of layers in the layer list. There is not a perfect, all-encompassing way to do this in Experience Builder, but there are at least five partial solutions to these problem. We will be discussing the upsides and downsides of all these methods and how to pull them off. I was inspired to make this post now as three of these methods are recently added options and another one is a hack that was recently pointed out to me. We'll start with the newest options first.

Use Multiple Map Layers Widgets

In the October 2023 ArcGIS Online update, the Map Layers Widget received an update that allows for multiple Map Layers Widgets on the same map to be customizable. This update was made to better organize groups of related layers.

How To
  1. Add a Map Layers Widget.
  2. Select Interact with Map Widget and pick your map in the dropdown.
  3. Click your map under customize layers.
  4. Enable Customize Layers and deselect all the layers not in the group you are building.
  5. Repeat for each group you want to make.
  6. Organize your Map Layers Widgets into a Widget Controller with a unique icon and title for each group or collect them all into a Card or some other layout widget.

JeffreyThompson2_0-1698855977027.png

Benefits
  • Logically organizes your layers into thematic groups that may not represent drawing order.
  • Can make a very large set of layers more easily navigable.
  • It is core functionality of Experience Builder, so it shouldn't interfere with any build mode options.
  • Easy for end user to understand.
  • Allows access to all layers in your map, so the end user can mix items from multiple groups.
Drawbacks
  • Does not allow for quickly changing between themes.
  • The user will have to individually select every layer to turn on or off.
  • At time of writing, this is only an option in ArcGIS Online. Developer Edition will get this optionality in a few weeks. Enterprise users will likely have to wait for 11.3.

Exclusive Visibility

The June 2023 ArcGIS Online update added an option to the Map Viewer that allows for layers within a group to be made mutually exclusive, so that when one layer in that group is turned on others will automatically turn off.

How To

Here is the ESRI article explaining the basic steps for using Exclusive Visibility, but I want to highlight this line.

With exclusive visibility, you can intuitively control and focus on individual layers in a group, including nested groups.

You can make groups within groups! 

  1. Organize all of your related layers into groups.
  2. Group all of your layers into one big group.
  3. Turn on Exclusive Visibility for the big group.
  4. Use a standard Map Layers Widget in your Experience based on this webmap.
Benefits
  • Allows end-users to turn groups of layers on and off with a single click.
  • Should offer the end-user a good user experience, although it is not perfect.
  • Can make a large set of layers more easily navigable.
  • It is core functionality of Experience Builder, so it shouldn't interfere with any build mode options.
Downsides
  • Depending on the drawing order you need, this may not be functional for you.
  • Does not allow for the end user to mix and match layers.
  • At time of writing, it requires the use of an ArcGIS Online webmap. Enterprise users should expect this functionality in 11.2.

Add/Remove Layers By Group Custom Widget

In June 2023, a developer (who's probably very smart and handsome) made a custom widget specifically designed to add or remove groups of layers from a map all at once.

How To

The developer (who's probably kind to kids and animals) left instructions on how to use the widget here on the custom widgets page. Basically, replace the dummy data with links to your data and it's good to go.

Benefits
  • Built to offer a simple, aesthetically pleasing and easy-to-use experience for the end-user.
  • End-users can rapidly turn groups of layers on and off with a single click.
  • End-users still have access to all map layers, so they can mix and match as needed.
  • Layers are completely removed from the map (not set to invisible as in the other options on this list) making for a cleaner Map Layers list and less strain on the end-users computer.
  • By strategically turning layers on and off, the end-user can re-order layers in their map. (Useful, if combined with the Add Data Widget.)
  • It is a good platform for further modification. I have heard that the original developer (who's probably super humble) has modified this widget to restrict access to certain layers and vary layer properties (like altering symbology from the defaults).
  • Layers are given specific, known ids making them easier to find in other custom widgets.
  • The experience developer is able to control the drawing order for the layers within the groups.
Downsides
  • By loading layers at runtime, you are essentially opting out of many of the features in the build mode of Experience Builder and many OOTB widgets become unusable.
  • Only available in Developer Edition. (Or Enterprise 11.X, if you are able to host the custom widget on a web server.)

Use Multiple Maps with the Section Widget

These last two options have been around for some time, if not the beginning of Experience Builder, so should be available to all users. The concept behind this method is to switch out the entire map instead of changing the map layers.

How To
  1. Create a webmap for each of you layer groups.
  2. In Experience Builder, add a Section widget and in each section add a Map widget containing a different version of your map.
  3. Add a Views Navigation widget to control the sections.
  4. Give each section an appropriate title and icon. (This option is not currently available in all versions of Experience Builder.)
  5. Use the Message Action > Extent Changes > Map > Zoom To option on each map for each other map to keep your extents in sync (or your end-users will be very confused).

JeffreyThompson2_0-1698870640145.png

It is possible to do similar workflows with multiple pages or a Widget Controller, but the Section widget is going to be the best choice in most cases.

Benefits
  • Available in all versions of Experience Builder.
  • Done well, this could offer the best user experience of all the options on this list.
  • Other widgets could be added to the sections, so they fly in and out with the map layers. (Maybe one group of layers would go well with a Near Me widget and other would be good with a Swipe?)
  • Could be an excellent choice for an StoryMap-esque Experience.
  • The experience developer has excellent control over the drawing order.
  • Logically organizes your layers into thematic groups that may not represent drawing order.
  • It is core functionality of Experience Builder, so it shouldn't interfere with any build mode options...
Downsides
  • ...But if your Experience has a large number of widget interactions, making them work with all your maps will be somewhere between tedious and impossible.
  • Done poorly, this could offer the worst user experience of all the options on this list.
  • The end-user cannot mix and match map layers from different groups. (You could also give them a Map Layers widget with each map, but it would likely defeat the upside of this method.)

Hack the Bookmarks Widget

This last method could be the best option in many cases and it never occured to me until it was pointed out by a new user, @JasonBOCQUET, a few weeks ago. The Bookmarks widget contains an option to set what layers are active at a given view, so let's make use of that.

How To
  1. Add a Bookmark widget and pick one of the Advanced Templates. Selecting the Advanced Template will allow you to fully customize how you want your Bookmarks to appear (just text, text and an icon, just an icon, a picture of the map with the layers on, or anything else you may want) and it also turns off the option for a user to create their own bookmark, which we want turned off for this hack.
  2. Add a bookmark.
  3. Hit the little map icon to Change the Bookmark View.
  4. Select the Layers icon and turn on/off the layers to match the thematic group you are building.
  5. Hit save.
  6. Repeat steps 2 to 5 for each group.
  7. Style your bookmarks as necessary.
  8. If you are using this widget in a Widget Controller, you will need to give it a more appropriate name.

JeffreyThompson2_1-1698934666632.png

Benefits

  • Available in all versions of Experience Builder.
  • End-users can rapidly turn groups of layers on and off with a single click.
  • Logically organizes your layers into thematic groups that may not represent drawing order.
  • Could be combined with a Map Layers Widget to give the end-user the option to mix and match layers from different groups.
  • The experience developer has control over the drawing order.
  • Can make a large set of layers more easily navigable.
  • It is core functionality of Experience Builder, so it shouldn't interfere with any build mode options.
  • Should offer the end-user a good user experience...
Downsides
  • ...But if the end-user does any panning or zooming before clicking your bookmark, they will be very frustrated and confused when the map suddenly zooms off somewhere else.
  • Does not help with organizing a large set of layers in a Map Layers Widget.

more
10 1 2,186
JeffreyThompson2
MVP Regular Contributor

There is a trap that will catch every new Experience Builder developer at some point, usually the first time they add a sidebar. 

Here I have a Legend in a Sidebar that covers up about half my map area.

JeffreyThompson2_0-1698692044480.png

I open up the Preview mode and I start clicking my map. The popups work fine in the west, but as I move east. They suddenly stop working.

JeffreyThompson2_0-1698694770020.png

Why is this happening? Well, even though the sidebar is closed the Sidebar widget is still on top of the map and it is effectively blocking you from interacting with your map.

The fix is to rearrange your layout, so that the map is now in the always open side of the sidebar. Starting with my arrangement with the sidebar directly on top of the map:

  1. I grab and shrink my map down so I have some room to work.
  2. I drag my sidebar so it is outside my map.
  3. I drag my map into the sidebar. If your sidebar, now shows First and Second on the Page panel, you are doing this right. Which side is labeled First and Second is dependent on how your Sidebar widget opens.
  4. Resize your sidebar and map as necessary.

JeffreyThompson2_1-1698695593477.png

JeffreyThompson2_2-1698695851361.png

Pay attention to this Overlay option. Turn it on if you want to have the sidebar cover the map. Leave it off if you want the map to resize without covering any content as the sidebar opens.

Note: This problem of one widget blocking interaction with another widget can happen between any two widgets. Put a big empty text box over your map and your map will stop working as well. So, it is good practice to make your widgets as small as you can and trim any unnecessary whitespace.

JeffreyThompson2_3-1698696589915.png

But, what if I have two sidebars? Well, you can do this trick again. Take the whole Sidebar widget and stuff it just under the First or Second label (whichever is the always open side) of Sidebar 2 and you should have two sidebars that don't mess with your map. I've never gone beyond two, but I don't see any reason you can't have three or four sidebars, maybe even more...

more
10 2 1,287
JeffreyThompson2
MVP Regular Contributor

Before starting this group, I came up with a list of topics I wanted to cover. This is the last of the topics on that list. We will start with the using the Message Action to filter one list by the selection made in another. Then I will attempt to build a filter that will filter both layers at once. I have put this post off for last in part because I am not sure if it will work. You will see my results either way. So, let's find out.

For this demo, I have made a map with two copies of the same feature layer. One is symbolized by blue stripes and the other has orange stripes. I have a map and two list widgets for each of the two layers.

JeffreyThompson2_0-1698680296467.png

In the Blue Stripe List, I add a Message Action > Record Selection Changes > Framework > Filter Data Records. And set the Action Data to Orange Stripe and set the Trigger/Action Connection to OBJECTID on both layers. The top dropdown menu represents the trigger data (Blue Stripe) and the bottom menu is the action data (Orange Stripe).

JeffreyThompson2_1-1698680701038.png

Selecting a Blue Stripe park will filter the Orange Stripe parks.

JeffreyThompson2_0-1698687324755.png

It seems like you should be able to extend this a little further and make a filter that filters both layers at once, but it just doesn't quite work. Sorry, I couldn't do it. The limitation here appears to be that widgets in Experience Builder are not allowed to chain react. Widget A can make widget B do something, but widget B will not pass it along to widget C. I would love to be proven wrong if anyone can figure this out and I will revisit this topic should Experience Builder add the capacity to filter two layers at once.

Edit: Here is an article by ESRI that shows a method for using the search bar to filter two layers at once. 

https://www.esri.com/arcgis-blog/products/experience-builder/mapping/filter-multiple-layers/

Edit: As of February 2024, Group Filters now exist in ArcGIS Online. 

more
4 2 605
JeffreyThompson2
MVP Regular Contributor

This wound up being a much longer post than I originally intended. It starts off pretty basic for new users, but advanced users should stick around to the end. There will likely be a few things you didn't know you could do.

Let's build a List. (I'm not going to discuss all of the possible options. There are just too many. Nor am I going to try to make something beautiful. This is just an overview of the options available to you.)  I started this project with the Jewelry Box Template so it's already got a List widget with a template. If I want to change the template, I can pick a different one at the top of the Settings panel.

JeffreyThompson2_0-1698264261780.png

I'm going to use this last option. Why? I feel like it, that's why.

JeffreyThompson2_1-1698264411471.png

There's a big button that says I need to select some data, so I press it. I'm making a map of parks so I find my parks feature layer in my map.

JeffreyThompson2_2-1698264631381.png

I'm going to change a few options in the settings panel. I'm going to add a No Data Message. This will show up if I build some filters that filter out all of my parks. It'll say "No parks found". Then I'll click Arrangement and edit some choices in the sub-panel that drops down. Right now there are three pictures per row, I only want two, so I'll change the width to 50%. At the bottom, there are two switches for Scroll Bar and Navigator. Scroll Bar allows the user to scroll through the list and Navigator has little arrows to make the list go up and down. I'll turn them both on. See below to see where I clicked.

JeffreyThompson2_0-1698266144673.png

And my list looks like this...

JeffreyThompson2_1-1698266211114.png

Let's put some stuff in this list. Double click to edit text? Ok, I'll do that. I want this to be the name of the park so I'll use Dynamic Content with the stacked circles icon.

JeffreyThompson2_2-1698266431331.png

I select my park name field (Name1) out of my feature layer and as I close this window my park names appear. When you see the {curly brackets} in Experience Builder, whatever is inside those brackets will be replaced with the value from the field inside those brackets.

JeffreyThompson2_3-1698266709714.png

Now let's fix these pictures. My feature layer has an attribute called PhotoLocation. It's the ending of a URL to where these pictures are located. I can use it to make a unique picture for each park. I click the image, Dynamic, URL and Expression.

JeffreyThompson2_0-1698267326734.png

The park images are stored on my website at https://webapps.arlingtontx.gov/websitephotos/ParkFinder/ + parkName.jpg, but the feature layer only holds the parkName.jpg part. No problem, that's what the Expression is for. In the rectangle near the top of the Expression panel I type (I can't paste in this box.) the following [inside the square brackets, don't use the square brackets]: ["https://webapps.arlingtontx.gov/websitePhotos/ParkFinder/" + ] . Double-quotes and the + sign are important here. Then, I pick PhotoLocation from the list below and hit Insert.

JeffreyThompson2_0-1698328947587.png

Now my List looks like this:

JeffreyThompson2_2-1698329432755.png

Now I'll go back to the List Widget and click the Tools menu open. I'll turn all the options on. Search, Sort and Filter need some configuring, so I'll do that next. Search needs to know what field(s) to search in, so I select Name1 from the dropdown menu. I'll make the Hint text a bit more specific to help out my users and type "Search By Park Name". These parks should be in alphabetical order, so I pick Name1 in the sort option dropdown and click the pencil to rename this sort "Alphabetical". Users may want to find the biggest park, so I hit Add An Option and pick SHAPE.STArea() from the dropdown. This is a standard field that should be in every feature layer. I hit Descending so the biggest parks rise to the top and rename this sort "Largest To Smallest". My Tools menu currently looks like this.

JeffreyThompson2_3-1698330853562.png

Next, I hit Set Filters and the SQL Expression Builder pops up. One day, I may do a post about SQL tricks, but that day is not today. This post is already getting very long and I haven't even gotten to the part I want to talk about yet. As a father of a young child, I need to know if the park has a playground, so I click Add Clause then use the first dropdown to pick PG and in the second dropdown I pick "is not blank".

JeffreyThompson2_0-1698331951191.png

Note: The Set Filter Data Action and the Filter Widget both offer much better user experiences, so maybe use those instead.

Alright this is the part, I have been trying to get to this whole time. Go up a bit in the List Settings panel to States. Here you can change how your list looks based on user interactions. I'm currently using ArcGIS Online, if you are using an older version of Experience Builder, I believe these options are found under Advanced. The Jewelry Box Template already comes with a Selected state enabled. I'm going to click on it to enable editing.

JeffreyThompson2_1-1698332960643.png

At the bottom of the next screen, I switch the layout from Auto to Custom and say Ok to the warning that pops up. Now, I can change how the list appears based on its state. Users have selected this park, so presumably they want to go there. Let's show them the address. In first item in my list, I drag my park name text box to the top of the image and add a new Text widget to the bottom part. The new Text widget is automatically linked to my data. My feature layer does not have a full Address field. I'll need to combine a few fields with an Expression to make a full address. In Dynamic Content, I pick AddressNumber from the menu at the bottom, type + " " + in the box and then pick Street. I click the gear, turn on Number Formatting, turn off the thousands separator and set decimal points to 0. It looks like this.

JeffreyThompson2_3-1698334288611.png

I changed the text color to black so it stands out better against the image and here's what I have.

JeffreyThompson2_4-1698334499581.png

And it works as expected. When the user clicks the park, the address appears.

Let's go a step further. Have you ever seen pictures like this on a website and when you hover your mouse over them but don't click they get a bit bigger? Can we do that in Experience Builder? As I type this, I don't know. Let's find out.

Back in the List settings panel, let's click Hover and then Enable Hover and set the Layout to Custom.

Now I click on the Image for the first park and I drag it to make it bigger. Nope, it doesn't work. But I can make it smaller, so I make it a bit smaller and use the Vertical Centering and Full Height options to put it in the center of the square. To make things a bit silly, I change the image shape to a circle and add a thick border.

Ok, scratch that last paragraph. It sort of works, but it is changing the image in every state. That's not what we want. What if I make this a new image that only shows up in the Hover state? I duplicate my Image widget so I don't have to reconfigure the image link. And send my original image to the Pending list. 

Here's a badly animated gif of my final list.

ezgif.com-gif-maker.gif

more
3 1 2,121
JeffreyThompson2
MVP Regular Contributor

Let's make our own custom theme. We'll do it in Online/Enterprise Edition first, but I'll comment on Developer Edition at the end.

I am using the JewelryBox Template that defaults to the Violet theme. If I just want to use a different theme, I can change it by hitting the paint palette on the left side of the screen and picking a different choice. But none of the default themes have my color of blue, so I'll go to the bottom of the screen and pick Customize.

It brings up this panel:

JeffreyThompson2_3-1698241452329.png

From top to bottom the options available to you are:

  • Primary - The main color of the application
  • Advanced color setting - More color options
  • Theme font - The default font of the application
  • Font Size Slider - Makes the default text larger or smaller
  • Reset - Restores the theme to it's default settings

The Primary color and the Theme Font are the most impactful settings on your theme and I would recommend not touching the default size slider. Don't touch the Reset button unless you mean it. There is no confirmation before it wipes your changes.

Pressing the color circle brings up this menu. You can select your color by clicking in that purple shaded box, messing with the sliders underneath it, or using the color boxes at the bottom of the menu, but I would recommend using the Hex or RGBA options for greater precision.

JeffreyThompson2_4-1698242326847.png

Quick primer on RGBA & Hex - All of computer color is generated by mixing Red, Green and Blue light in values of 0 to 255. (Why those numbers? It's an 8-bit computer thing. Don't worry about it too much.) With these values, it is possible to make the over two million possible colors your monitor can produce. The A is a measure of opacity with 100 being fully opaque and 0 being fully transparent. Hex is a complicated and confusing way to encode RGB values without the A option.

But what if you don't know what Hex or RGB values to use? Here is a free tool for finding colors that helps finding complementary colors and has accessibility...

The blue I want is hex code #0055fa, so I will put that in and close this window.

JeffreyThompson2_5-1698243336012.png

The header bar, buttons and the space between the list items have all change colors. Many of the colors in the theme are derived from the primary color, making it lighter or darker.

Now, we will pick a font. Here are the choices. If you want to use something else, you will need Developer Edition.

JeffreyThompson2_6-1698243938984.png

I picked Georgia and here's how it looks.

JeffreyThompson2_7-1698244014776.png

Let's go back to the Advanced color setting option. In this panel, you will see a set of 8 colors colors that are used in the application. Clicking the carrot next to the colors will open an additional menu containing sets of complementary colors that can be used to quickly replace all 8 colors.

JeffreyThompson2_8-1698244597887.png

The colors from left to right are:

  1. Primary - The main color
  2. Secondary - An accent color. By default, it is never used. You must ask for it.
  3. Dark - The "black" color
  4. Light - The "white" color
  5. Success - Used for messages like "Form received".
  6. Info - Rarely used for messages that are not the other three categories.
  7. Warning - Used for non-critical errors.
  8. Danger - Used for serious alerts like "This will be permanently deleted." and critical errors.

You should keep 3-8 close to their default values. Messing with the Dark and Light values may make your text very hard to read. And users expect the various warnings to be in certain colors. You can set the Secondary color to anything you want. But why bother setting a value that is never used? Because setting the colors of a theme will change the values in the color picker throughout the Builder mode, so you can find it later and use it somewhere else in your application.

My theme colors are the blue I have already set as my Primary color, a red (#e00053) and a silvery-grey (#888b8d). The grey is too dark to be my Light and too light to be my Dark, so I will make it my Secondary color. And the red is very close to the default Danger red, so I will replace that value. (There is no rule against using Danger as an accent color, but be careful not to confuse your users. They may think a red button is dangerous to click.)

And that's it. My theme is ready. I can even save my project as a template and share it with my organization.

Now, for some quick comments on Developer Edition. I won't go through the whole process as the ESRI Guide is quite good.

  • It's ok just to use the tools in build mode. (I won't judge you.)
  • CSS for the entire application should go in style.scss. CSS for an individual widget should go in a css file in that widget's folder. (It's the React way.) But remember, that css in any file can target anything in the entire application.
  • Properly configured, a custom font will appear as a blank slot in the font dropdowns of the build mode.
  • I could not import a font using the @import declaration in the guide. I used a font-face declaration in my styles.scss like:

 

@font-face {
    font-family: 'Relish Pro Medium';
    src: url('./assets/fonts/RelishProMedium.otf') format('truetype');
}​

 

  • Experience Builder appears to support .otf and .ttf, but not .woff or .woff2.

more
4 0 585
JeffreyThompson2
MVP Regular Contributor

I made a new Experience with a Print Widget in a Widget Controller. I am using the Floating panel arrangement option and it looks awful.

JeffreyThompson2_0-1698238262799.png

I need to resize it, so I go to the Style pane, but "This widget does not support style settings."

JeffreyThompson2_1-1698238381511.png

I have gone deep into the weeds on this one trying and failing to hack a solution out of Developer Edition, but the real solution is super simple.

In the builder mode, grab the little white corner in the lower-right of the widget. Drag your mouse til it is the size you want and hit save.

JeffreyThompson2_2-1698238681216.png

This is now the default size of the panel for this widget. Every widget within this Widget Controller can be made a different size using this method.

more
5 1 410
93 Subscribers