Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

addSelectedRows body props do not update with selected state #164

Open
fnimick opened this issue Oct 11, 2023 · 8 comments · May be fixed by #231
Open

addSelectedRows body props do not update with selected state #164

fnimick opened this issue Oct 11, 2023 · 8 comments · May be fixed by #231

Comments

@fnimick
Copy link

fnimick commented Oct 11, 2023

Not entirely sure why, this is working in the REPL demo, yet doesn't work in my application. There are no errors logged to the console.

<script lang="ts">
  const table = createTable(tableData, {
    select: addSelectedRows(),
  });

  const columns = table.createColumns([
    table.display({
      id: "selected",
      header: "",
      cell: ({ row }, { pluginStates }) => {
        const { isSelected, isSomeSubRowsSelected } = pluginStates.select.getRowState(row);
        return createRender(SelectIndicator, { isSelected, isSomeSubRowsSelected });
      },
    }),
  ]);
  const { headerRows, rows, tableAttrs, tableBodyAttrs, pluginStates } = table.createViewModel(
    columns,
  );
  const { selectedDataIds } = pluginStates.select;
  $selectedDataIds = { "0": true }; // this sets the prop for row 0
</script>
<div>
  <div class="table-container w-full">
    <table class="table-hover table" {...$tableAttrs}>
      <thead>
        {#each $headerRows as headerRow (headerRow.id)}
          <Subscribe rowAttrs={headerRow.attrs()} let:rowAttrs>
            <tr {...rowAttrs}>
              {#each headerRow.cells as cell (cell.id)}
                <Subscribe attrs={cell.attrs()} let:attrs props={cell.props()}>
                  <th {...attrs}>
                    <Render of={cell.render()} />
                  </th>
                </Subscribe>
              {/each}
            </tr>
          </Subscribe>
        {/each}
      </thead>
      <tbody {...$tableBodyAttrs}>
        {#each $rows as row (row.id)}
          <Subscribe rowProps={row.props()} let:rowProps>
            <tr class:selected={rowProps.select.selected}>
              {#each row.cells as cell (cell.id)}
                <Subscribe attrs={cell.attrs()} let:attrs>
                  <td {...attrs}>
                    <Render of={cell.render()} />
                  </td>
                </Subscribe>
              {/each}
            </tr>
          </Subscribe>
        {/each}
      </tbody>
    </table>
  </div>
</div>

<style>
  .selected {
    background: rgb(148, 205, 255);
  }
</style>

And my SelectIndicator is:

<script lang="ts">
  import { writable, type Readable, type Writable } from "svelte/store";

  export let isSelected: Writable<boolean>;
  export let isSomeSubRowsSelected: Readable<boolean> | undefined = undefined;

  $: indeterminateStore = writable($isSomeSubRowsSelected ?? false);
</script>

<input
  type="checkbox"
  class="checkbox"
  bind:checked={$isSelected}
  bind:indeterminate={$indeterminateStore}
/>

The row's props for the selected state is only true for row 0, and false for all other rows. This does not update along with selections.

@fnimick
Copy link
Author

fnimick commented Oct 11, 2023

Actually, this is very odd, I figured out what's happening.

If the incoming table data is updated via a call to invalidateAll() in sveltekit, the row props appear to get desynchronized from the actual table. This leads to behavior such as where checking a row marks a different row as checked, even though the id in $selectedDataIds is correct.

I suspect this is due to the row prop association to the row not being recalculated when the id assigned to each row changes. The row props are stored by index (I think), so if e.g. a row had id 1 and index 3, and then the data updates such that the row with id 1 is at index 1, checking that row now marks the row at index 3 as checked in row props.

@shawnphoffman
Copy link

Adding to this. The issue only pops up when linkDataSubRows: true. I'm experiencing the same issue when I have a basic table with sub rows that expand and the select plugin. It initializes with random rows being selected even though the selectedDataIds is empty. The only row that works as expected for me is id = '0', its children, and anything at the top-level without sub rows. Everything else misbehaves and never corrects itself.

@fnimick
Copy link
Author

fnimick commented Oct 26, 2023

@shawnphoffman are you sure? in my case, I am not using subrows, yet I still experience this desynchronization from the table input data when the input data is updated with some rows removed and other rows added compared to the currently selected row set.

@hgoona
Copy link

hgoona commented Nov 22, 2023

@fnimick @shawnphoffman I think I experienced this issue also when i was using table.display <<< it seems to not maintain sync with the 'id' of the cell / row.

Use table.column instead and I think it should solve your issue! 🤞🏾

@jrsimp
Copy link

jrsimp commented Feb 7, 2024

This sounds very similar to an issue I'm experiencing which I put together on the discussions board. I'm not using table.display, instead have been using table.column which hasn't resolved the problem.

#186 (comment)

@dj-nuo
Copy link

dj-nuo commented May 18, 2024

Found a solution:

const { selectedDataIds } = pluginStates.select;
setTimeout(() => {
    $selectedDataIds = get(selectedTraktorCollectionIds);
}, 0);

Don't ask why)))
(because I don't know proper explanation, maybe someone smarter can explain)

@shawnphoffman
Copy link

This sparked me to dig into it again and I finally figured out a solution for my issues. My issue stemmed from the combination of addSelectedRows, addSubRows w/linkDataSubRows=true, and addExpandedRows.

My table is basically a file/folder tree and it was misbehaving by pre-selecting entire folder structures that didn't have any files in them.

Digging through the documentation I noticed that when addSubRows.children is a string, that property must exist. For me, this wasn't the case if a leaf folder didn't have any files/folders in it (it was undefined). So, I switched it to a function that checks for the property and, if undefined, return []. This too was buggy by selecting everything despite the documentation. On a whim, I switched the else case to return undefined and everything works perfectly.

I haven't dug into the library code yet but it looks like there is a truthy bug when it comes to checking whether or not an item has children and that subsequently trickled up and caused issues with the "is the row selected" logic. Anyway, I hope this helps someone...

The Fix

Before:

sub: addSubRows({
	children: 'someProperty',
}),

After:

sub: addSubRows({
	children: (item) => {
		return item.someProperty?.length ? item.someProperty : undefined;
	},
}),

Weird...

@bryanmylee
Copy link
Owner

@shawnphoffman Thank you for the extensive and detailed report. You're right in that it's probably a truthiness bug, and I would greatly appreciate if you could file a fix for this issue in the plugin!

shawnphoffman added a commit to shawnphoffman/svelte-headless-table that referenced this issue Aug 28, 2024
Fixes bryanmylee#164
Issue occurs when children string property is an empty array
@shawnphoffman shawnphoffman linked a pull request Aug 28, 2024 that will close this issue
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants