Better code organization with the multi option of angular dependency injection (translation)

 

 

In almost all applications there is a file that throws all the application's endpoints. If you're using Angular, it might look like this:

 

export const API = new InjectionToken('API');
const API_URL = `${environment.BASE_URL}/api/v1`; 

export const endpoints = { 
   login: '${API_URL}/auth/login', 
   signup: '${API_URL}/auth/signup', 
   createTodo: '${API_URL}/todos/create',
   updateTodo: '${API_URL}/todos/:id/update',
   deleteTodo: '${API_URL}/todos/:id/delete,
   .....
};

At first glance, everything looks fine, but this file creates some problems as the application grows.

We violated the Single Responsibility Principle. We're facing merge conflicts, our endpoints are not portable, and it's hard to find the endpoint location in the file.

We need to assign responsibility to the responsible modules, so each module is responsible for exposing its endpoints.

Then we can aggregate and join the entire endpoint in the API service.

Let's see how to achieve this with the help of Angular dependency injection and the multi option.

Create API service

First, we need to create the service responsible for performing the aggregation and exposing the application endpoints.

import {Inject, Injectable, InjectionToken} from '@angular/core';

export const END_POINTS = new InjectionToken('END_POINTS');

@Injectable()
export class API {
  private readonly _baseUrl = environment.BASE_URL;
  endPoints: EndPoints;

  constructor(@Inject(END_POINTS) private _endPoints) { }
}

api.service.ts

We have one representing each endpoint InjectionToken. Now let's see how we populate the END_POINTStoken.

Create an authentication module

Two additional files need to be created for each module.

export const api = {
  login: "/auth/login",
  logout: "/auth/logout",
  signup: "/auth/signup",
};

auth.api.ts

As the name suggests, this file will be responsible for the Auth endpoint.

interface EndPoints {
  login: string;
  logout: string;
  signup: string;
}

auth.d.ts

We still want to use the advantages of typescript declaration merging .

Fundamentally, the mechanism of merging is to put the members of both parties into an interface of the same name.

Now, typescript will merge each endpoint into an EndPointsinterface so we can autocomplete.

Next, we provide the Authendpoint to Angular dependency injection in the appropriate module, in our case the Authmodule.

import { END_POINTS } from "../config/api";
import { api } from "./auth.api";

const AUTH_API = { provide: END_POINTS, multi: true, useValue: api };

@NgModule({
 ...
 providers: [AuthService, AUTH_API]
})
export class AuthModule {
}

auth.module.ts

You can think of multioptions as an array. Every time we add a new one provider, Angular providerwill push into the array.

So if we go back to our API service and print the _endpointsproperties, we will see that there is one item in the array - the  Authendpoint.

 

END_POINTS Provider

END_POINTS Provider

 

Great, now we just need to flatten our endpoints onto a large object.

@Injectable()
export class API {
  ...
  
  constructor(@Inject(END_POINTS) private _endPoints) {
    this.endPoints = _endPoints.reduce((acc, current) => {
      return {
        …current,
        …acc
      };
    }, {});
  }

}

api.service.ts

Finally, we create a simple method to help us parse the url.

class API {
  ...

  resolve(url: string, params?) {
    if (!params) {
      return `${this._baseUrl}${url}`;
    }
    const resolved = Object.keys(params).reduce((acc, param) => {
      return acc.replace(`:${param}`, params[param]);
    }, url);
    return `${this._baseUrl}${resolved}`;
  }
}

api.service.ts (Translator's Note: This function is used to replace :idthe real parameters in the url)

Now we can use the API service in every service we need. For brevity, I've added a Todosmodule where we describe the same process. example:

 

 

Summarize

In this article, we witnessed the power of Angular Dependency Injection and how we can leverage the multioptions. With this change, we are less susceptible to merge conflicts, and the modules are portable with the API, so we can use them with other applications. We also avoid violating the Single Responsibility Principle, and our code is better organized.

Original link

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325202253&siteId=291194637