gabrielguerrero / ngrx-traits

NGRX Traits is a library to help you compose and reuse state logic in your angular app. There is two versions, @ngrx-traits/signals supports ngrx-signals, and @ngrx-traits/{core, common} supports ngrx.
MIT License
44 stars 3 forks source link

trying to get a dumb component to work with mult-selection #73

Closed jmls closed 1 month ago

jmls commented 1 month ago

new to ngrx/signals and traits so perhaps I'm missing something ;)

I was wanting to create a dumb component that uses the primeng table

So I have the store

const entity = type<SablTenant>();

export const TenantStore = signalStore(
    { providedIn: 'root' },
    withState(initialState),
    withCallStatus({ initialValue: 'loading' }),
    withEntities({ entity }),
    withEntitiesMultiSelection<SablTenant>({ entity }),
    withEntitiesSingleSelection({ entity }),

the TenantComponent

export class TenantComponent {
    store = inject(TenantStore);
    constructor() {}

and the dumb table component

export class TenantTableComponent {
    entities = input<SablTenant[]>([]);
    selected = input<SablTenant[]>([]);

    onEdit = output<SablTenant>();
    onDelete = output<SablTenant>();
    onDeleteAll = output<SablTenant[]>();
    onSelectedChange = output<SablTenant[]>();
    onNew = output();

and the HTML of the TenantComponent

<tenant-table 
            [entities]="store.entities()" 
            [selected]="store.entitiesSelected()"

            (onEdit)="onEdit($event)" 
            (onSelectedChange)="onSelectionChange($event)"
            (onDelete)="onDelete($event)"></tenant-table>    

the primeng table has a [(selection)]="selected()" bind which I can't get to work (Unsupported expression in a two-way bindingngtsc(-995002))

so I thought I'd try this instead

(selectionChange)="onSelectedChange.emit($event)"

which runs the onSelectionChange function in the top level TenantComponent

onSelectionChange(items: Entity[]) {
        const data = { ids: items.map((item) => item.id) };
        console.log('received', data);
        this.store.selectEntities(data);

        console.log('selected', this.store.entitiesSelected());
    }
at this point, when I select an item in the table I get this

Screenshot from 2024-05-15 22-59-51

which is expected

however, when I deselect the item in the table I get this

Screenshot from 2024-05-15 22-57-57

and as you can see, the "store.entitiesSelected()" doesn't get cleared

I would like to know a couple of things

a) is there a way to bind the store.entitiesSelected() to a 2-way b) if not, what am I doing wrong with this code ;)

many thanks

gabrielguerrero commented 1 month ago

Hey @jmls , regarding question a, sadly no way to do double binding [()], because the signals generated by ngrx-signals are all read only, and you can only change them using the patchState, I think they have a feature request to have them writable but I think is complicated to support that due to the way the lib works b). are you using deselectEntities to deselect them? also there is a toggleSelectEntities and will select and deselect them

There is an example using multi selection here https://github.com/gabrielguerrero/ngrx-traits/blob/main/apps/example-app/src/app/examples/signals/product-shop-page/components/product-basket-tab/product-basket-tab.component.ts you can run the examples by checking out the project and doing yarn && yarn start

Also if you provide a stackblitz I could help you out more

jmls commented 1 month ago

a) thanks !

b) I think the problem I have is that the primeNg table component only sends the list of selected items in the event - so all I know is what is currently selected, not what was changed or what was deselected

the workflow goes has follows

(nothing selected) => select item => event(items[1,2]) => selectEntities(items[1,2]) (2 items selected)

at this point, the "store.entitiesSelected()" signal does return the selected items from the store

now, when I deselect an item

(2 items selected) => deselect item 2 => event(items[1]) => selectEntities(items[1])

at this point, the "store.entitiesSelected()" signal still returns the 2 selected items from the store

I was surprised at this, as I expected the selectEntities method in traits to clear existing entries and replace with the new array. I am now wondering if it simply adds to the existing entries. If I pass [] nothing is added. If I pass the single selected item from the component, because it's already in the entitiesSelected() array, the value is not changed and stays at 2 entries

jmls commented 1 month ago

I am also presuming that in order to "clear" all selected entities I'd have to

store.deselectProducts({ids: store.productsEntitiesSelected().map(item => item.id)})

as there doesn't seem to be a "clear" method and I can't send an empty array to selectProducts

gabrielguerrero commented 1 month ago

Yes, you are correct; it always adds; this is because you could design your own bulk select, like add all in page or add all items that meet a condition. But also I found a bug :), there is a clearEntitiesSelection but is only in the interface when there are no collections property, so I guess you are using the collectons prop?, I will add into the interface used by the collections prop. It is a small fix, so I will release it soon. So you will just call clearEntitiesSelection before the selectEntities and that should solve your problem

gabrielguerrero commented 1 month ago

Ok realized the clearEntitiesSelection has a bug as well, so fixing that, and adding relevant unit test. So either way you will need to a release to use it, will be likely today

gabrielguerrero commented 1 month ago

Ticket related to the bug previous described https://github.com/gabrielguerrero/ngrx-traits/issues/75

gabrielguerrero commented 1 month ago

@jmls @ngrx-traits/signals@17.3.1 has the fix , you can now use clearEntitiesSelection or clear[Collections]Selection if you are using the collection param before the selectEntities method and that should fix your issue

jmls commented 1 month ago

awesome ! thanks so much

gabrielguerrero commented 1 month ago

@jmls I close this issue because I think you have what you need now, feel free to reopen if you are still having issues