Reputation: 858
I'm already used to ngrx-store but, to run away from boilerplate, I decided to go with ngrx-data. Ngrx-data docs are a little limit and I'm facing difficulties to find a way to implement odata pattern.
As you can see here, ngrx Entity DataService has a rest pattern, so it sends a put like: PUT api/heroes/1
; but in odata pattern you should do: PUT api/heroes(1)
.
Besides it, we have functions and actions in OData that uses the api like this example:
GET api/heroes/Default.GetMostPowerful(minimumPower=5)
.
Also we have the return pattern of Odata that is a wrapper that contains data inside, like this:
{
"@odata.context": "http://localhost:57003/api/v2/$metadata#Users",
"value": [
{
"id": 1,
"mail": "[email protected]",
"displayName": "foo, Adam "
},
{
"id": 2,
"mail": "[email protected]",
"displayName": "bar, Andy",
}
}
How can I override it all? Is there any service that I can extend and provide to angular that will implement all odata pattern?
Thanks in advance!
-- EDIT --
Thanks @Meligy, I could perform odata persistences, but I'm still struggling with odata function. This is what I did so far:
const getByEmail = createAction(
'[User] Get User by Email',
(payload: string) => ({ payload })
);
@Injectable({ providedIn: 'root' })
export class UserService extends EntityCollectionServiceBase<User> {
constructor(serviceElementsFactory: EntityCollectionServiceElementsFactory) {
super('User', serviceElementsFactory);
}
getByEmail(email: string) {
this.dispatch(getByEmail(email));
}
}
When you call getByEmail('[email protected]')
it dispatches the action, but I don't know how to extend effects
to call my class that extends DefaultDataService
and then dispatch a success action again with the fetched data.
I already have the function in DefaultDataService
extended class ready to fetch data, but I'm trying to find a way to trigger it from the action.
export class OdataService<T> extends DefaultDataService<T> {
constructor(entityName: string, protected factory: OdataServiceFactory) {
super(entityName, factory.http, factory.httpUrlGenerator);
}
protected buildRoute(id?: number) {
const route = this.factory.pluralizzer.pluralize(this.entityName);
return id
? `${environment.api}/${route}(${id})`
: `${environment.api}/${route}`;
}
protected getCollection(route: string) {
return this.http.get<any>(route).pipe(
map(data => data.value)
);
}
protected getOne(route: string) {
return this.http.get<any>(route).pipe(
map(data => {
delete data['@odata.context'];
return data;
})
);
}
protected serializeParams(params?: Dictionary<any>) {
if (!params) {
return '';
}
return Object.keys(params).map(key => `${key}=${params[key]}`).join(',');
}
getAll() {
return this.getCollection(this.buildRoute());
}
getWithQuery(params: string) {
return this.getCollection(`${this.buildRoute()}?${params}`);
}
getById(key: number) {
return this.getOne(this.buildRoute(key));
}
// this is the function method that will call my generic odata functions
// so the question is how to tell ngrx/data that my custom action should come here
getFunction(namespace: string, name: string, params?: Dictionary<any>) {
const route = this.buildRoute();
const url = `${route}/${namespace || 'Default'}.${name}(${this.serializeParams(params)})`;
return this.getOne(url);
}
}
Upvotes: 1
Views: 1140
Reputation: 858
Just to update this topic... after struggling a lot with ngrx, I found another framework that is way better then ngrx called akita.
It's so easy to use and much less boilerplate that was easy to implement OData pattern for it. I even created this library to work with akita, check it out:
https://github.com/rosostolato/akita-ng-odata-service
Upvotes: 0
Reputation: 36624
You should be able to override the Entity Service and define your own URL.
The docs mention it briefly: https://ngrx.io/guide/data#creating-entity-data-services
But the library that later become NGRX Data has a clearer example in the old repo https://github.com/johnpapa/angular-ngrx-data/blob/master/docs/entity-dataservice.md#custom-entitydataservice
Upvotes: 2