Reputation: 7153
New to Angular and wondering how to correctly initialize variables in an service.ts file. The purpose being to dry up my code a bit.
export class CollectionsService {
private urlBase: string;
private token: string;
constructor(
private authorized: AuthService,
private http: Http,
private httpHelper: HttpHelperService
) {
this.urlBase = `/app/${this.authorized.user.appdomainid}`;
this.token = this.authorized.user.token;
// this.authorized = undefined
}
// ...
}
If I just put the url base directly in a method it's fine:
getCollections(): Observable<any> {
return this.http
.get(
`/app/${this.authorized.ampUser.appdomainid}/collections`,
this.httpHelper.getRequestOptions(this.authorized.ampUser.token)
)
.pipe(map((response) => <any>response.json()))
.pipe(map((response) => response.Items))
.pipe(
catchError((err) => {
return throwError(err.json());
})
);
}
Upvotes: 0
Views: 545
Reputation: 7179
There's nothing wrong with keeping the base URL and token as instance variables on your service. Redefining them in each method seems inefficient and repetitive, but it makes sense if you want to react to changes in the AuthService
.
If that is the concern, you might consider using property getters instead:
@Injectable({
providedIn: 'root'
})
export class CollectionsService {
private get urlBase(): string {
return `/app/${this.authorized.ampUser.appdomainid}`;
}
private get token(): string {
return this.httpHelper.getRequestOptions(this.authorized.ampUser.token);
}
constructor(
private http: HttpClient,
private authorized: AuthService,
private httpHelper: HttpHelperService
) {}
getCollections(): Observable<any> {
return this.http
.get(
`${this.urlBase}/collections`,
this.token
)
.pipe(map((response) => <any>response.json()))
.pipe(map((response) => response.Items))
.pipe(
catchError((err) => {
return throwError(err.json());
})
);
}
}
If you're really looking to DRY, you could make it an InjectionToken
that's provided to your whole application. Any number of services could rely on these tokens without needing to instantiate/maintain their own instance variables.
You can make sure you react to changes in AuthService
by providing the tokens with the useFactory
strategy.
In your app.module.ts
:
import { AuthService } from 'auth.service.ts';
import { HttpHelperService } from 'http-helper.service.ts';
const BASE_URL = new InjectionToken<string>('BaseUrl');
const API_TOKEN = new InjectionToken<string>('ApiToken');
@NgModule({
providers: [
{
provide: BASE_URL,
useFactory: (authorized: AuthService) => `/app/${authorized.ampUser.appdomainid}`,
deps: [AuthService]
},
{
provide: API_TOKEN,
useFactory: (authorized: AuthService) => authorized.ampUser.token,
deps: [AuthService]
}
],
})
export class AppModule {}
Then in your collections.service.ts
:
import { BASE_URL, API_TOKEN } from 'app.module.ts';
@Injectable({
providedIn: 'root'
})
export class CollectionsService {
constructor(
private http: HttpClient,
private httpHelper: HttpHelperService,
@Inject(BASE_URL) private baseUrl: string,
@Inject(API_TOKEN) private apiToken: string
) {}
getCollections(): Observable<any> {
return this.http
.get(
`${this.baseUrl}/collections`,
this.httpHelper.getRequestOptions(this.apiToken)
)
.pipe(map((response) => <any>response.json()))
.pipe(map((response) => response.Items))
.pipe(
catchError((err) => {
return throwError(err.json());
})
);
}
deleteCollection(listId: unknown): Observable<any> {
const body = { foo: 1, bar: 2 };
return this.http
.delete(
`${this.baseUrl}/collections/${listId}/rows`,
this.httpHelper.getRequestOptionsWBody(this.apiToken, body)
)
.pipe(map((response) => <any>response.json()))
.pipe(map((response) => response))
.pipe(
catchError((err) => {
return throwError(err.json());
})
);
}
}
As an aside, if you're using Angular 14+, you can simplify the code a little using the inject()
method instead of the @Inject
decorator. This allows you to remove the constructor if all it was doing was declaring dependencies.
@Injectable({
providedIn: 'root'
})
export class CollectionsService {
private http = inject(HttpClient);
private httpHelper = inject(HttpHelper);
private baseUrl = inject(BASE_URL);
private apiToken = inject(API_TOKEN);
getCollections(): Observable<any> {
return this.http
.get(
`${this.baseUrl}/collections`,
this.httpHelper.getRequestOptions(this.apiToken)
)
// ...
}
deleteCollection(listId: unknown): Observable<any> {
const body = { foo: 1, bar: 2 };
return this.http
.delete(
`${this.baseUrl}/collections/${listId}/rows`,
this.httpHelper.getRequestOptionsWBody(this.apiToken, body)
)
// ...
}
I'm not sure what the Angular team's stance on inject()
in useFactory
is. They show it as an example in the usage notes. For your case:
import { AuthService } from 'auth.service.ts';
import { HttpHelperService } from 'http-helper.service.ts';
const BASE_URL = new InjectionToken<string>('BaseUrl');
const API_TOKEN = new InjectionToken<string>('ApiToken');
@NgModule({
providers: [
{
provide: BASE_URL,
useFactory: () => `/app/${inject(AuthService).ampUser.appdomainid}`
},
{
provide: API_TOKEN,
useFactory: () => inject(AuthService).ampUser.token
}
],
})
export class AppModule {}
On the other hand, the useFactory
documentation still declares the dependencies in the signature...
Upvotes: 1