Select to view content in your preferred language

Using the logged-in user's identification to automatically filter the map upon its opening

1429
7
Jump to solution
03-28-2024 04:42 AM
Labels (1)
DéveloppeurP
Emerging Contributor

Hello,

I am developing a custom widget that interacts with a webMap in my organization. My experience is secured with a user group from my organization. My goal is to filter the data on my map according to the logged-in user.

At the moment, I am able to retrieve the authenticated user. However, I am encountering difficulties in using this information to apply a filter to the data displayed when the map opens.

Does anyone have any ideas or has anyone worked on this scenario before?

0 Kudos
3 Solutions

Accepted Solutions
JeffreyThompson2
MVP Regular Contributor

The user information can be accessed in a custom widget with the props.user object. So your filter could work something like this.

if (props.user.username === 'Bob') {
    filterFunction()
}

For an example widget that uses the props.users object, you can look at the Security Widget.

GIS Developer
City of Arlington, Texas

View solution in original post

JeffreyThompson2
MVP Regular Contributor

Can you share more of your code? There isn't enough here to see why you can't connect to the dataSource.

Here is the official sample for filtering a feature layer using it's dataSource.

 https://github.com/Esri/arcgis-experience-builder-sdk-resources/tree/master/widgets/filter-feature-l...

GIS Developer
City of Arlington, Texas

View solution in original post

JeffreyThompson2
MVP Regular Contributor

The problem line here looks like this one.

const dataSource = dataSourceId && DataSourceManager.getInstance().getDataSource(dataSourceId);

With the && operator, I think this line is only capable of returning a Boolean value. Try taking out the dataSourceId && part.

const dataSource = DataSourceManager.getInstance().getDataSource(dataSourceId);

You might also need to use a shouldComponetUpdate() function to handle the initial load where the dataSource may not be available yet.

https://www.geeksforgeeks.org/what-does-shouldcomponentupdate-do-and-why-is-it-important/#

GIS Developer
City of Arlington, Texas

View solution in original post

7 Replies
JeffreyThompson2
MVP Regular Contributor

The user information can be accessed in a custom widget with the props.user object. So your filter could work something like this.

if (props.user.username === 'Bob') {
    filterFunction()
}

For an example widget that uses the props.users object, you can look at the Security Widget.

GIS Developer
City of Arlington, Texas
DéveloppeurP
Emerging Contributor

Thank you for your response and your promptness. The issue arises precisely when I try to apply the filter to the datasource :

if (this.props?.user?.username === "user1") {
    this.setState({query : "field = 'dispo'"})
    queryParams = {
      where: this.state.query
    };
    console.log(this.state.query);
  }
  }
console.log(dataSource)
  dataSource?.updateQueryParams(queryParams, this.props.id);



Since it can't retrieve the datasource, it displays "undefined

0 Kudos
JeffreyThompson2
MVP Regular Contributor

Can you share more of your code? There isn't enough here to see why you can't connect to the dataSource.

Here is the official sample for filtering a feature layer using it's dataSource.

 https://github.com/Esri/arcgis-experience-builder-sdk-resources/tree/master/widgets/filter-feature-l...

GIS Developer
City of Arlington, Texas
DéveloppeurP
Emerging Contributor
export default class Widget extends React.PureComponent<AllWidgetProps<unknown>,State> {
  constructor(props){
    super(props)
    this.state={filtre: '1=1'}
    this.state={user:'utilisateur'}
    this.state={query:"field = '1=1'"}
}



}

componentDidMount() {
  this.setState({ user: this.props.user?.username });

  const dataSourceId = this.props.useDataSources?.[0]?.dataSourceId;
  const dataSource = dataSourceId && DataSourceManager.getInstance().getDataSource(dataSourceId);

  let queryParams;
  if (this.props?.user?.username === "user1") {
    this.setState({query : "field = 'OK'"})
    queryParams = {
      where: this.state.query
    };
  }

  if (!dataSource) {
    console.log("Failed to get dataSource");
  } else {
    dataSource?.updateQueryParams(queryParams, this.props.id);
  }
  
  }

  render() {

    return (
<div className="widget-starter jimu-widget">
</div>
)
0 Kudos
JeffreyThompson2
MVP Regular Contributor

The problem line here looks like this one.

const dataSource = dataSourceId && DataSourceManager.getInstance().getDataSource(dataSourceId);

With the && operator, I think this line is only capable of returning a Boolean value. Try taking out the dataSourceId && part.

const dataSource = DataSourceManager.getInstance().getDataSource(dataSourceId);

You might also need to use a shouldComponetUpdate() function to handle the initial load where the dataSource may not be available yet.

https://www.geeksforgeeks.org/what-does-shouldcomponentupdate-do-and-why-is-it-important/#

GIS Developer
City of Arlington, Texas
Jason-Daily-Dev
Emerging Contributor

hi, Jeffrey, would you like to help? I have code as above, and it need to filter data source by props.user, I used esri github example filter layer, also took a look at your security widget. but it seem data sources do not work as expected during initial loading.

0 Kudos
Jason-Daily-Dev
Emerging Contributor

hi, any luck for solving this issue? I am doing exactly the same thing, but it seems data source is not available, or sometimes it becomes available, not sure how to wait it properly:

my widget.tsx, I put one text input with button, so I can click either by using props.user or test user, that way it always work, but for initial loading, just cannot get data source available most of time.

import { AllWidgetProps, jsx, FeatureLayerDataSource, SqlQueryParams, DataSourceManager } from 'jimu-core';
import { IMConfig } from '../config';
import { TextInput} from 'jimu-ui';
import React, { Component } from 'react';

interface State {
  testInput: string;
  unavailableDataSources: string[];
}

class Widget extends Component<AllWidgetProps<IMConfig>, State> {
  constructor(props: AllWidgetProps<IMConfig>) {
    super(props);
    this.state = {
      testInput: '',
      unavailableDataSources: props.useDataSources ? props.useDataSources.asMutable().map(ds => ds.dataSourceId) : []
    };
  }

  componentDidMount() {
    this.retryFilterDataSources();
  }

  componentDidUpdate(prevProps: AllWidgetProps<IMConfig>) {
    if (prevProps.useDataSources !== this.props.useDataSources || prevProps.user !== this.props.user) {
      this.setState({
        unavailableDataSources: this.props.useDataSources.asMutable().map(ds => ds.dataSourceId)
      }, this.retryFilterDataSources);
    }
  }

  retryFilterDataSources = (retries = 50, delay = 1000) => {
    const dsManager = DataSourceManager.getInstance();
    const { unavailableDataSources } = this.state;

    const stillUnavailable = unavailableDataSources.filter(dsId => !dsManager.getDataSource(dsId));

    if (stillUnavailable.length === 0) {
      this.filterDataSources();
    } else if (retries > 0) {
      stillUnavailable.forEach((dsId, index) => {
        const originalIndex = this.props.useDataSources.findIndex(ds => ds.dataSourceId === dsId);
        console.warn(`Data source ${originalIndex + 1} of ${this.props.useDataSources.length}: ${dsId} not found.`);
      });
      console.log(`Retrying to check data sources... Attempts left: ${retries}`);
      setTimeout(() => this.retryFilterDataSources(retries - 1, delay * 2), delay);
    } else {
      console.warn('Data sources not ready after multiple attempts.');
    }
  };

  textInputChangeHandler = (evt: React.ChangeEvent<HTMLInputElement>) => {
    this.setState({ testInput: evt.target.value });
  };

  filterDataSources = () => {
    if (this.props.useDataSources?.length > 0 && this.props.user) {
      const dsManager = DataSourceManager.getInstance();
      const username = this.props.user.username;
      const clientName = username.split('_')[1];

      console.log('Username:', username);
      console.log('Client Name:', clientName);
      console.log('Test Input:', this.state.testInput);

      this.props.useDataSources.forEach((useDataSource, index) => {
        const ds: FeatureLayerDataSource = dsManager.getDataSource(useDataSource.dataSourceId) as FeatureLayerDataSource;

        if (!ds) {
          console.warn(`Data source ${index + 1} of ${this.props.useDataSources.length}: ${useDataSource.dataSourceId} not found.`);
          return;
        }

        let queryParams: SqlQueryParams;

        if (this.state.testInput === '') {
          const 
          clientName = this.props.user.username.split('_')[1];
          queryParams = {
            where: `LOWER(Client) LIKE LOWER('OWNER%')`
          };
        } else {
          const testClientName = this.state.testInput.split('_')[1];
          queryParams = {
            where: `LOWER(Client) LIKE LOWER('${testClientName}%')`
          };
        }

        console.log(`Query Params for data source ${index + 1} of ${this.props.useDataSources.length}:`, queryParams);
        ds.updateQueryParams(queryParams, this.props.id);
      });
    } else {
      console.warn('No data sources available to filter.');
    }
  };

  render() {
    return (
      <div className="widget-get-map-coordinates jimu-widget p-2">
        <p>
          <TextInput 
            placeholder='Filter by username' 
            onChange={this.textInputChangeHandler} 
            value={this.state.testInput} 
          />
          <button onClick={this.filterDataSources} style={{ marginLeft: '10px' }}>
            Test Filter
          </button>
        </p>
      </div>
    );
  }
}

export default Widget;



0 Kudos