angular-architects / ngrx-toolkit

Various Extensions for the NgRx Signal Store
MIT License
137 stars 21 forks source link

How to use DataService when my entity has no 'id' property? #67

Open alcaidio opened 1 month ago

alcaidio commented 1 month ago

Is it possible to use an adapter to map my "id" property to "someId"?

rainerhahnekamp commented 1 month ago

Hey @alcaidio, yes, it should be possible to implement it. If you want to contribute, PRs are always welcomed.

alcaidio commented 1 month ago

PR https://github.com/angular-architects/ngrx-toolkit/pull/69

tabeatheunicorn commented 2 weeks ago

I would be interested in that feature, too! Unfortunately I am not sure how to contribute a solution yet but I wanted to share my current workaround, in case anybody else ends up here, too.

First, I create a type to annotate the DataService with. Because it is just an extension, it should not break code already written. In my case, I just need to map an already existing attribute to id, so if I don't delete it, my code ist still working.

type WithIdGetter<T, K extends keyof T> = T & {
    get id(): T[K];
};

As my backend only returns elements of type T without the id attribute, I also wrote a generic mapper function

function mapToIdGetter<T, K extends keyof T>(items: T[], key: K): (T & { get id(): T[K] })[] {
    return items.map((item) => ({
        ...item,
        get id() {
            return item[key];
        },
    }));
}

Again, it just adds a getter to my interface. As you can see, it works on a list of items. The principle would be very similar for just one element (you just leave out the list type and map function).

Update: https://github.com/microsoft/TypeScript/issues/58020 Due to this bug, I replaced the getter by a hardcoded item

export type WithIdGetter<T, K extends keyof T> = T & {
    id: T[K];
};

export function mapToIdGetter<T, K extends keyof T>(items: T[], key: K): (T & { id: T[K] })[] {
    return items.map((item) => ({
        ...item,
        id: item[key],
    }));
}
type Product = WithIdGetter<PreviousType, 'uuid'>;
const productCollection = 'products';
export const productState = signalStore(
    { providedIn: 'root' },
    withDevtools('ProductsSignal'),
    withCallState({collection: productCollection}),
    withEntities({
        entity: type<Product>(),
        collection: productCollection,
    }),
    withDataService({
        dataServiceType: ProductService,
        filter: {},
        collection: productCollection,
    }),
    withHooks({
        onInit: (store) => {
            console.log('loading'), store.loadProductsEntities();
        },
    }),
);

This store is working fine for me (I need to use it somewhere for it to work)