Getting selected items in Fluent UI DetailsList
Asked Answered
N

4

6

I am using Fluent UI DetailsList. In the example the component is implemented as a class component but I am using a functional component.

I am having difficulties in getting the selected items, I assume and think my implementation is incorrect. The problem is I do not get ANY selected items.

export const JobDetails = () =>  {
    const { actions, dispatch, isLoaded, currentTabJobs, activeTabItemKey } = useJobDetailsState()

    let history = useHistory();

    useEffect(() => {
        if (actions && dispatch) {
            actions.getJobListDetails()
        }
    }, [actions, dispatch])

    const getSelectionDetails = (): string =>  {
        let selectionCount = selection.getSelectedCount();

        switch (selectionCount) {
            case 0:
                return 'No items selected';
            case 1:
                return '1 item selected: ' + (selection.getSelection()[0] as any).name;
            default:
                return `${selectionCount} items selected`;
        }
    }

    const [selectionDetails, setSelectionDetails] = useState({})
    const [selection, setSelection] = useState(new Selection({
        onSelectionChanged: () => setSelectionDetails(getSelectionDetails())
    }))

    useEffect(() => {
        setSelection(new Selection({
            onSelectionChanged: () => setSelectionDetails(getSelectionDetails())
        }))
    },[selectionDetails])

    return (
        <div>
            <MarqueeSelection selection={selection}>
                <DetailsList
                    items={currentTabJobs}
                    groups={getGroups()}
                    columns={_columns}
                    selection={selection}
                    selectionPreservedOnEmptyClick={true}
                    groupProps={{
                        onRenderHeader: props => {
                            return (
                                <GroupHeader 
                                    {...props} 
                                    selectedItems={selection}
                                />
                            )
                        },
                        showEmptyGroups: true
                    }}
                />
            </MarqueeSelection>
        </div>
    )
}

export default JobDetails;
Norward answered 28/4, 2020 at 9:32 Comment(2)
Did you ever solve this yourself? I'm running into the exact same question.Pollitt
@ErikJ. yes I did. Will update with solutionNorward
N
4

I found a solution to the problem I was having and I had to memorize the details list

What I did:

const [selectedItems, setSelectedItems] = useState<IObjectWithKey[]>();
const selection = useMemo(
    () =>
      new Selection({
          onSelectionChanged: () => {
          //console.log('handle selection change',selection.getSelection())
          setSelectedItems(selection.getSelection());
      },
    selectionMode: SelectionMode.multiple,
  }),
[]);

const detailsList = useMemo(
    () => (
        <MarqueeSelection selection={selection}>
            <DetailsList
                items={currentTabJobs}
                groups={getGroups()}
                columns={columns}
                ariaLabelForSelectAllCheckbox="Toggle selection for all items"
                ariaLabelForSelectionColumn="Toggle selection"
                checkButtonAriaLabel="Row checkbox"
                selection={selection}
                selectionPreservedOnEmptyClick={true}
                groupProps={{
                    onRenderHeader: (props) => {
                        return <GroupHeader {...props} selectedItems={selection} />;
                    },
                    showEmptyGroups: true,
                 }}
                 onRenderItemColumn={(item, index, column) =>
                     renderItemColumn(item, index!, column!)
                 }
             />
         </MarqueeSelection>
     ),
     [selection, columns, currentTabJobs, activeTabItemKey]
 );

 return (
     <div>
         {detailsList}
     </div>
 )
Norward answered 8/7, 2020 at 7:3 Comment(0)
G
5

I might have a more simple answer, this example is for a list with 'SelectionMode.single' activated but I think the principle of getting the selected item remains the same

const [selectedItem, setSelectedItem] = useState<Object | undefined>(undefined)
const selection = new Selection({
    onSelectionChanged: () => {
        setSelectedItem(selection.getSelection()[0])
    }
})

useEffect(() => {
    // Do something with the selected item
    console.log(selectedItem)
}, [selectedItem])

<DetailsList
    columns={columns}
    items={items}
    selection={selection}
    selectionMode={SelectionMode.single}
    selectionPreservedOnEmptyClick={true}
    setKey="exampleList"
/>
Goldarned answered 26/8, 2020 at 10:31 Comment(1)
This is pretty close I think, but for some reason this causes the component to rerender which wipes out the selection.Niedersachsen
N
4

I found a solution to the problem I was having and I had to memorize the details list

What I did:

const [selectedItems, setSelectedItems] = useState<IObjectWithKey[]>();
const selection = useMemo(
    () =>
      new Selection({
          onSelectionChanged: () => {
          //console.log('handle selection change',selection.getSelection())
          setSelectedItems(selection.getSelection());
      },
    selectionMode: SelectionMode.multiple,
  }),
[]);

const detailsList = useMemo(
    () => (
        <MarqueeSelection selection={selection}>
            <DetailsList
                items={currentTabJobs}
                groups={getGroups()}
                columns={columns}
                ariaLabelForSelectAllCheckbox="Toggle selection for all items"
                ariaLabelForSelectionColumn="Toggle selection"
                checkButtonAriaLabel="Row checkbox"
                selection={selection}
                selectionPreservedOnEmptyClick={true}
                groupProps={{
                    onRenderHeader: (props) => {
                        return <GroupHeader {...props} selectedItems={selection} />;
                    },
                    showEmptyGroups: true,
                 }}
                 onRenderItemColumn={(item, index, column) =>
                     renderItemColumn(item, index!, column!)
                 }
             />
         </MarqueeSelection>
     ),
     [selection, columns, currentTabJobs, activeTabItemKey]
 );

 return (
     <div>
         {detailsList}
     </div>
 )
Norward answered 8/7, 2020 at 7:3 Comment(0)
P
0

Put the selection object in a state. Example:

...

export const Table: FunctionComponent<TableProps> = props => {
  const { items, columns } = props

  const { setCopyEnabled } = useCommandCopy()
  const { setDeleteEnabled } = useCommandDelete()

  const onSelectionChanged = () => {
    if (selection.getSelectedCount() === 0) {
      setCopyEnabled(false)
      setDeleteEnabled(false)
    }
    else if (selection.getSelectedCount() === 1) {
      setCopyEnabled(true)
      setDeleteEnabled(true)
    }
    else {
      setCopyEnabled(false)
      setDeleteEnabled(true)
    }
  }

...

  const [selection] = useState(new Selection({ onSelectionChanged: onSelectionChanged }))

  useEffect(() => {
    selection.setAllSelected(false)
  }, [selection])

...

  return (
    <ScrollablePane styles={{
      root: {
        position: 'fixed',
        top: 105, left: 285, right: 20, bottom: 20
      },
    }}>
      <DetailsList
        items={items}
        columns={columns}

        selection={selection}
        selectionMode={SelectionMode.multiple}

        layoutMode={DetailsListLayoutMode.justified}
        constrainMode={ConstrainMode.horizontalConstrained}

...

      />
    </ScrollablePane>
  )
}
Pursuer answered 10/1, 2021 at 14:48 Comment(0)
O
0

I think the main issue here is onSelectionChanged function is getting called twice, second time with empty data. Reason I found is React useState method re-rendering the data. Solution that worked for me here :

Store value in a normal variable instead of state variable(if you don't want to re-render detailslist after this):

let selectedItem = undefined;
const selection = new Selection({
    onSelectionChanged: () => {
        selectedItem = selection.getSelection()
        // console.log(selectedItem)
        // You can use selectedItem value later anywhere you want to
        // track your selection.
    }
})

<DetailsList
    columns={columns}
    items={items}
    selection={selection}
    selectionMode={SelectionMode.multiple}
    selectionPreservedOnEmptyClick={true}
    setKey="exampleList"
/>
Owings answered 15/7, 2022 at 6:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.