In angular2 app, we need to call a lot of 3rd party restful APIs. Any idea how to organise these APIs, so we can centralize the management of these APIs (maybe in a config file)?
For example, in angular2 tutorial
https://angular.io/docs/ts/latest/guide/server-communication.html#!#fetch-data
imagine I have 20 services, every one of them will have a few functions inside to use http get, post accessing a bunch of 3rd party restful APIs. So I want to put all these API links in a centralize place, later if there is any change from 3rd party API providers, I can change without going into my services ts files?
Updated
config-values.ts
import {OpaqueToken} from 'angular2/core';
export const API_LOGIN = new OpaqueToken('API_LOGIN');
main.ts to bootstrap
...
import {API_LOGIN} from './app/shared/services/config-values';
bootstrap(AppComponent, [
ROUTER_PROVIDERS,HTTP_PROVIDERS,
provide(API_LOGIN, {useValue: 'http:bar.com/somepath'}),
provide(PLATFORM_DIRECTIVES, {useValue: [ROUTER_DIRECTIVES], multi:true}),
provide(APP_BASE_HREF, { useValue: '<%= APP_BASE %>' })
]);
In login.service.js which calls it
...
import {API_LOGIN} from 'config-values.ts';
#Injectable()
export class LoginService {
private loggedIn = false;
constructor(#Inject(API_LOGIN) private apiUrl:string,private http:Http) {
this.loggedIn = !!localStorage.getItem('auth_token');
}
}
I would create a typescript file containing OpaqueTokenss and pass them using DI
config-values.ts
export const API_FOO_URL = CONST_EXPR(new OpaqueToken('API Foo URL'));
export const API_BAR_URL = CONST_EXPR(new OpaqueToken('API Foo URL'));
...
export const API_URLS = CONST_EXPR([
provide(API_FOO_URL, {useValue: 'http:foo.com/somepath'}),
provide(API_BAR_URL, {useValue: 'http:bar.com/somepath'}),
...
]);
main.ts
import {API_URLS} from 'config-values.ts';
bootstrap(AppComponent, [API_URLS, ...]);
foo.service.ts
import {API_FOO_URL} from 'config-values.ts';
#Injectable()
export class FooService {
constructor(#Inject(API_FOO_URL) private apiUrl:string) {}
}
Related
I am pretty new to developing REST APIs using the NestJS framework (started using it last week).
For Authorization purposes I want to implement a CanActivate Guard to my app. This guard simply looks for an authorization header to extract the jwt. Furthermore the user needs to be fetched via a service to get its role and check for the required permission. Additionally there is a #Permission decorator, which is used on protected routes, that takes in a permission string.
This decorator file looks like this:
export const PERMISSION_KEY = "requiredPermission"
export const Permission = (permission: string) => {
return SetMetadata(PERMISSION_KEY, permission);
}
But I am experiencing strange behaviour: Only injecting the Reflector (so I can look up the required permission from the route) is working fine. When now trying to inject a service, let's say AuthService, the constructor of the AuthenticationGuard isn't even called, so the service results in undefined. Event the reflector instance is undefined, though it worked before. Here is how it looks in my authentication.guard.ts:
#Injectable()
export class AuthenticationGuard implements CanActivate {
constructor(
private reflector: Reflector,
private authService: AuthService
) {}
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
// ... do some things to extract jwt from header ...
console.log(this.authService); // Results in undefined
// This line does not work because reflector also is undefined.
const requiredPermission = this.reflector.get<string>(PERMISSION_KEY, context.getHandler());
console.log(requiredPermission);
}
}
This guard is imported as a provider in a feature module called AuthModule:
#Module({
imports: [
JwtModule.register({
secret: "EXTREMELY_SECRET"
})
],
providers: [
AuthService,
{
provide: APP_GUARD,
useClass: AuthenticationGuard
}
],
controllers: [ AuthController ],
exports: [
AuthService
]
})
export class AuthModule {}
Now when removing the service from dependency injection and make a console.log, I can see that it gets instantiated as the first dependency of the whole app. Does this maybe cause the service to fail injecting? How would I possibly change fix that?
Or does injecting services not work for guards in general? I think this is not the problem.
Maybe someone can help me with this problem and give me some advice on to fix this issue.
Thank you very kindly in advance for your support!
I am trying to port the code of an Angular 2 app to Angular Dart, so that I can reuse the business logic in Flutter.
My app makes use of HttpInterceptors for Error handling and server authorization.
In typescript I would inject a simple service :
#Injectable({
providedIn: 'root'
})
export class InterceptorService implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return;
}
constructor(private auth: AuthService) { }
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {}
}
But the Dart API does not seem to have an HttpInterceptor class. Does one have to extend the HttpClient class in order to do so ?
I have looked at this S.O question but it dates back from 5 years ago, the way to do it has probably changed quite a bit in the meantime.
Turns out there are a couple ways you can achieve this in Angular Dart / Flutter.
Some third party libraries built on top of dart http provide convenience methods for Http interception (DIO, Http Interceptor). However, both handle interception at the client level.
I decided to go with the Dart Http library, using the recommended method :
class UserAgentClient extends http.BaseClient {
final String userAgent;
final http.Client _inner;
UserAgentClient(this.userAgent, this._inner);
Future<StreamedResponse> send(BaseRequest request) {
request.headers['user-agent'] = userAgent;
return _inner.send(request);
}
}
Goal
I am working in a module similar to SpringActuator (I need /health, /info, /metrics, etc), I need the module to be reused across several apps, but the logic to determine if the app is healthy or to get the app info is defined at the application itself, I want this actuator module to be agnostic of the application. So I need the application to import the module passing a provider that exists already at the application context.
Implementation
I used the ModuleRef (with strict option) to create something like a ServiceLocator, so that any app dependency could be resolved during module initialization.
The actuator module is imported like this
imports: [ActuatorModule.forRoot({ actuatorToken: ApplicationActuator })]
The ApplicationActuator is the class knowing if the app is healthy or not. This provider could have another dependencies internally (nested)
The ActuatorModule uses a factory approach to provide the service locator(FactoryHelper)
providers: [
{
provide: 'ActuatorFactoryHelper',
useFactory: (moduleRef: ModuleRef) => new FactoryHelper(moduleRef, options.actuatorToken),
inject: [ModuleRef],
}
]
And finally my service uses the FactoryHelper like this
get actuator(): Actuator {
return this.factoryHelper.instance;
}
constructor(#Inject('ActuatorFactoryHelper') private readonly factoryHelper: FactoryHelper<Actuator>) {}
This is how the FactoryHelper looks like
export class FactoryHelper<T> implements OnModuleInit {
instance: T;
constructor(
private readonly moduleRef: ModuleRef,
private readonly token: Type<any> | string | symbol,
) {}
onModuleInit() {
this.instance = this.moduleRef.get(this.token, { strict: false
});
}
}
Question
I read in another threads that having a module depending on a provider is a bad practice, Is it a bad practice? what issues could I face? Is it a easier way of doing it?
Notes
I tried using custom providers (useFactory) but nested dependencies got not resolved (they are not visible to the actuator module).
Thanks in advance.
I am shifting from Razor views to Angular 4, and trying to figure out how to pass global constants from the server to Angular without relying on Ajax calls.
So the server constants will be transaction status for example:
Id: 1->Active
Id: 2-> Inactive
Id: 3->Cancelled etc
So these statuses are saved in the db and are used to query various transactions, Thus will be required in lots of components
In Razor views, I used to pass these values together with the viewmodel. But in Angular currently I can see two options:
Make Ajax calls in ngOnInit of each component that requires these constants
Make a static model to hold these values
Option 1 increases the number of server calls by quite a bit -> so I am trying to avoid this.
Option 2 will require me to change status in multiple places in my application if a new status is added for example, which i am also not fond of.
I am looking for a way to send all my constants to Angular as the application loads or page is reloaded for example.
You need to use ReplaySubject
as per rxjs documentation
ReplaySubject:Represents an object that is both an observable sequence as well as an observer. Each notification is broadcasted to all subscribed
Look at this code snippet
export class GlobalConstants{
Status:number[];
}
import { Observable, ReplaySubject } from 'rxjs';
import { GlobalConstants } from '../models/GlobalConstants';
#Injectable()
export class YourService {
//This line will cache the latest list you will get from the server
private dataSubject = new ReplaySubject<GlobalConstants>();
//you will use this observer in your components to subscribe to the getStatus result
yourStatusList$: Observable<GlobalConstants> = this.dataSubject.asObservable();
constructor(private http: Http) {
this.getStatus()
}
getStatus() {
return this.http.get('url').subscribe(res => {
this.dataSubject.next(res);
})
}
export class ExampleComponent {
public statusList;
public constructor(private _yourService: YourService) {
this.getStatus();
}
getStatus(): void {
this._yourService.yourStatusList$.subscribe(
result => {
this.statusList = result;
}
)
}
}
what will happen is when angular create the service it will call getStatus method one time per the app life cycle and then fetch your status list from the server then u will need to subscribe in your components to yourStatusList$ , for each subscrbition you will get latest cached list and if the list changed in your server u just need to call YourService.getStatus then u will fetch the status list again and all component subscribed to this observer will get notified by the new list
let's take your two challenges
1-Make Ajax calls in ngOnInit of each component that requires these constants
-by using this code your app will make one call to the server to fetch status list so u don't need to make Ajax call in ngOnInit of each component
2-Make a static model to hold these values will require me to change status in multiple places in my application if a new status is added
-if new status is added you just need to call YourService.getStatus one time in any place in your code and all components subscribed to your yourStatusList will get notified by the new status list
NOTE: you must n't use providers: [yourService] in your component cause if u used it it will create a new object and will not use the global object , just add your service in #NgModule providers and use component constructor to inject the service object
It may be best to have a service cache the information in a local variable. Then, when you inject the service into your components, and one calls a service function, the service checks the local variable. If something is in the variable, use it, if not, load the data and cache it for later use.
Since the service is a singleton, the data should only load once unless you create some mechanism to timeout the value. So, the first time the service is called, the data will be fetched. After that, the local variable (below called globals) should be used.
Service:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class GlobalsService {
private globals: any;
constructor(private httpClient: HttpClient) { }
getGlobals(): any {
if (this.globals) {
return this.globals;
} else {
// call your API to get global data from DB
this.httpClient.get<any>('...').subscribe((data: any) => {
this.globals = data;
return this.globals;
});
}
}
}
Component using the service:
import { GlobalsService } from './../globals.service';
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-tester',
templateUrl: './tester.component.html',
styleUrls: ['./tester.component.css']
})
export class TesterComponent implements OnInit {
constructor(private globalsService: GlobalsService) { }
ngOnInit() {
// Do something here with the globals from the service
const gbls = this.globalsService.getGlobals();
if (gbls) {
// ... maybe put these in a variable for later use, what ever you need
}
}
}
Doing this will keep you from having to do the Ajax call you mention, and avoid you have to keep code in more than one place. The service pattern offers a nice central place to keep this data for the lifetime of the application. All you need to do is inject the service into the component, or other services, where it is needed.
You can add you constants as attributes on your app element inside you razor view
<app someatt="{ your json data here }">Loading...</app>
then on you app's root component access them like this:
export class AppComponent implements OnInit {
constructor(
private el: ElementRef
) {
}
ngOnInit() {
console.log(this.el.nativeElement.attributes["someatt"].value);
}
}
then you can have a global service with its statuses data set here on ngOnInit and consumed in all your components
I am trying to wire up a basic Angular2 app that uses the Http service. (Most of the tutorials I've seen do this by having a Component consume the Http service, which seems wrong unless the basic philosophy of thin controllers has changed – but that's a different question.)
I would like to create a service that uses Angular's Http service. But I can't figure out how to inject the Http service other than this:
boot.ts:
import {bootstrap} from 'angular2/platform/browser';
import {AppComponent} from './app.component';
import {HTTP_PROVIDERS } from 'angular2/http';
bootstrap(AppComponent, [HTTP_PROVIDERS]);
myService.ts:
import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
#Injectable()
export class aService{
constructor(http:Http){
}
/** do some stuff *//
}
This works, but it seem very wrong to require the user of the service to know the service's dependencies and be required to inject them into the bootstrap process. It seems like there should be a way to directly hand a providers array to a service the same way you can a component, but I can't find it. Am I just missing something?
Update
This way if a parent injector provides an implementation for OtherService this one is used, otherwise OtherServiceImpl is used (default).
#Injectable()
class SomeService {
OtherService _other;
SomeService(Injector injector) {
_other = injector.getOptional(OtherService);
if (_other == null) {
_other = injector.resolveAndCreateChild([
provide(OtherService, useClass: OtherServiceImpl)
]).get(OtherService);
}
_other.doSomething();
}
}
If you provide another one like
bootstrap(AppElement, [
provide(OtherService, useClass: OtherServiceImpl2)
]);
OtherServiceImpl2 is used.
See also https://github.com/angular/angular/issues/5622
Original
You could just make the http service optional (using the #Optional() annotation) and if none is provided just create an instance inside the constructor with new Http().
This way the user doesn't need to know about the services dependencies, but is able to pass alternative implementations if necessary (for example for testing).
If creating the dependeny inside the service requires DI itself, you can inject an injector and use it to get dependencies.
See also optional dependencies in http://blog.thoughtram.io/angular/2015/05/18/dependency-injection-in-angular-2.html
What also could work (not tried myself yet) is just to create a child injector and instruct it to skip self
From the SkipSelfMetadata documentation
class Dependency {
}
#Injectable()
class NeedsDependency {
dependency;
constructor(#SkipSelf() dependency:Dependency) {
this.dependency = dependency;
}
}
var parent = Injector.resolveAndCreate([Dependency]);
var child = parent.resolveAndCreateChild([NeedsDependency]);
expect(child.get(NeedsDependency).dependency instanceof Depedency).toBe(true);
var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]);
expect(() => inj.get(NeedsDependency)).toThrowError();
I don't know yet if this still resolves from "self" if parent can't provide the requested type.