Eduardo Rosostolato
Eduardo Rosostolato

Reputation: 858

@ngrx/data with an odata backend server

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

Answers (2)

Eduardo Rosostolato
Eduardo Rosostolato

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

Meligy
Meligy

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

Related Questions