Create an Http injectable in an Angular2 service? - dependency-injection

I'm trying to wrap http in a service, so that all db access is passing through my channel
the issue is, services cannot have injectables passed to their constructor (right?) so I have to construct it all myself. I'm using the code:
import {Http,HTTP_BINDINGS,XHRBackend,BaseRequestOptions} from 'http/http';
import {Injector,bind} from 'angular2/di'
...
var injector = Injector.resolveAndCreate([
BaseRequestOptions,
XHRBackend,
bind(Http).toFactory(
function(backend, defaultOptions) {
return new Http(backend, defaultOptions);
},
[XHRBackend, BaseRequestOptions])
]);
this.http = injector.get(Http);
but when trying to use it by:
this.http.get('./entities.json')
//Get the RxJS Subject
.toRx()
// Call map on the response observable to get the parsed people object
.map(res => res.json())
.subscribe(e => this.entities = e);
I get an error:
Error during instantiation of DBDriver! (Entities -> DBDriver).
angular2.dev.js:22367 ORIGINAL EXCEPTION: No provider for function () {}! (function (_backend, _defaultOptions) { -> function (_browserXHR, _baseResponseOptions) { -> function () {})

Make sure that HTTP is injected in your service
Note #Inject(http) at de service constructor
#Inject(Http) public http: Http
Note that http, at least at angular2 alpha45, is outside angular lib, therefore you have to manually add http lib
<script src="/node_modules/angular2/bundles/angular2.min.js"></script>
<script src="/node_modules/angular2/bundles/http.min.js"></script>
SERVICE
import {Http, Headers, HTTP_BINDINGS} from 'angular2/http';
import {Inject} from 'angular2/angular2'
constructor(#Inject(Http) public http: Http) {}
getQuote(): Promise<any> {
return new Promise((resolve, reject) => {
this.http.get('http://localhost:3001/api/random-quote')
.map(res => res.text())
.subscribe(
data => resolve(data),
err => this.logError(err),
() => console.log("Random Quote Complete")
);
})
}
COMPONENT
import {Component, bootstrap, CORE_DIRECTIVES} from 'angular2/angular2';
import {Http, Headers, HTTP_BINDINGS} from 'angular2/http';
import {ChuckService} from "./chuck-service";
#Component({
selector: 'hello-app',
template: `
{{quoteOfTheDay}}<br />
<button (click) = "getQuote()">Get new quote</button><br /><br />
`,
directives: [CORE_DIRECTIVES],
providers: [ChuckService, HTTP_BINDINGS]
})
export class HelloApp {
quoteOfTheDay: string = '';
constructor(public _chuckService: ChuckService) {
}
getQuote(){
this._chuckService.getQuote().then((resp) => this.quoteOfTheDay = resp);
}
}
bootstrap(HelloApp);
INDEX.HTML
<script src="/node_modules/es6-shim/es6-shim.min.js"></script>
<script src="/node_modules/systemjs/dist/system-csp-production.js"></script>

Related

NestJS E2E tests with Jest. Injected service returns undefined (only tests)

I have a problem with the end-to-end testing of my users module. I want to validate if there is a "companyCode" when a user makes a GET request in /users and sends this code in the query params. This validator searches the database if this company code exists, if it does not exist it returns an error. The problem is that in the test this validation doesn't happen, because "companiesService" returns undefined (only in the test), what's missing?
Possible Solution: something related to useContainer(class-validator).
Thanks.
users.e2e-spec.ts
describe('UsersController (e2e)', () => {
let app: INestApplication;
let repository: Repository<User>;
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [UsersModule, AuthModule, TypeOrmModule.forRoot(ormConfig)],
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
}).compile();
app = module.createNestApplication();
app.useGlobalPipes(new ValidationPipe());
useContainer(app.select(UsersModule), { fallbackOnErrors: true });
repository = module.get('UserRepository');
await app.init();
});
afterAll(async () => {
await app.close();
});
describe('/users (GET)', () => {
it('should return users if requesting user sent "companyCode" in the request body', async (done) => {
return request(app.getHttpServer())
.get('/users')
.auth('admin', 'admin')
.query({ companyCode: '2322661870558778503' }) // should return 200 because companyCode exists but is returning 400
.expect(200)
.then((res) => {
expect(res.body.users).toHaveLength(1);
done();
})
.catch((err) => done(err));
});
});
});
users.module.ts
#Module({
controllers: [UsersController],
providers: [UsersService, UserExistsRule],
imports: [
TypeOrmModule.forFeature([
User,
Person,
Type,
Profile,
UserProfile,
Company,
]),
CompaniesModule,
],
exports: [UsersService],
})
export class UsersModule {}
read-users.dto.ts
export class ReadUsersDto {
#IsOptional()
#IsNotEmpty()
#IsString()
#MinLength(1)
#MaxLength(255)
public name?: string;
#IsOptional()
#IsNotEmpty()
#IsNumberString()
#Type(() => String)
#Validate(CompanyExistsRule)
public companyCode?: string;
}
companies.module.ts
#Module({
providers: [CompaniesService, CompanyExistsRule],
imports: [TypeOrmModule.forFeature([Company, Person])],
exports: [CompaniesService],
})
export class CompaniesModule {}
companies.decorator.ts
#ValidatorConstraint({ name: 'CompanyExists', async: true })
#Injectable()
export class CompanyExistsRule implements ValidatorConstraintInterface {
constructor(private companiesService: CompaniesService) {}
async validate(code: string) {
try {
console.log('companiesService', this.companiesService); // returns undefined on test
await this.companiesService.findOneByCode(code);
} catch (e) {
return false;
}
return true;
}
defaultMessage() {
return `companyCode doesn't exist`;
}
}
I found that I imported useContainer from typeorm instead of the class-validator hahahahha.
// incorrectly imported
import { useContainer } from 'typeorm';
// correctly imported
import { useContainer } from 'class-validator';

Angular 8/Ionic 5: "values is null" on POST request

Since yesterday, I'm stuck with an issue and I don't find anything to help me T.T
I'm working on an Angular 8 with Ionic 5 app for Android. I just want to make a POST request to an API. Yep, that's all ^^
I have a register.page.html with a register form, then on submit it calls the submitForm() function in register.page.ts.
This function calls the registerUser() function in user.service.ts. This is where I make the POST request to my API.
But everything goes wrong, and all I got is an error values is null in my console. The request is never send, and when I console.log my variables, everything seems fine.
Here are the files.
register.page.html:
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>Créer un compte</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<form (ngSubmit)="submitForm()">
<ion-item lines="full">
<ion-label position="floating">Nom d'utilisateur</ion-label>
<ion-input [(ngModel)]="user.username" name="username" type="text" required></ion-input>
</ion-item>
<ion-item lines="full">
<ion-label position="floating">Email</ion-label>
<ion-input [(ngModel)]="user.email" name="email" type="email" required></ion-input>
</ion-item>
<ion-item lines="full">
<ion-label position="floating">Mot de passe</ion-label>
<ion-input [(ngModel)]="user.password" name="password" type="password" required></ion-input>
</ion-item>
<ion-row>
<ion-col>
<ion-button type="submit" color="danger" expand="block">Créer un compte</ion-button>
</ion-col>
</ion-row>
</form>
</ion-content>
register.page.ts:
import {Component, OnInit} from '#angular/core';
import {UserService} from '../services/user.service';
import {User} from '../classes/user';
#Component({
selector: 'app-register',
templateUrl: './register.page.html',
styleUrls: ['./register.page.scss'],
})
export class RegisterPage implements OnInit {
user: User;
constructor(
private userService: UserService,
) {
this.user = new User();
}
ngOnInit() {
}
submitForm(): void {
this.userService.registerUser(this.user)
.subscribe(res => {
console.log(res);
}, err => {
console.error(err);
});
}
}
user.service.ts:
import {Injectable} from '#angular/core';
import {User} from '../classes/user';
import {ApiService} from './api.service';
import {HttpClient} from '#angular/common/http';
import {catchError, retry} from 'rxjs/operators';
import {Observable} from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class UserService {
currentUser: User;
constructor(
private http: HttpClient,
private apiService: ApiService
) {
}
registerUser(user: User): Observable<any> {
return this.http
.post<any>(this.apiService.apiUrl + '/users', user, this.apiService.httpOptions)
.pipe(
retry(3),
catchError(this.apiService.handleError)
);
}
}
api.service.ts:
import {Injectable} from '#angular/core';
import {HttpHeaders} from '#angular/common/http';
import {Observable, throwError} from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class ApiService {
apiUrl = 'http://127.0.0.1:3000/api/v1';
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: null,
})
};
token = null;
constructor() {
}
handleError(error): Observable<never> {
let errorMessage = '';
if (error.error instanceof ErrorEvent) {
errorMessage = error.error.message;
} else {
errorMessage = 'Error code: ' + error.status + '\nMessage: ' + error.message;
}
return throwError(errorMessage);
}
}
app.module.ts:
import {NgModule} from '#angular/core';
import {BrowserModule} from '#angular/platform-browser';
import {RouteReuseStrategy} from '#angular/router';
import {IonicModule, IonicRouteStrategy} from '#ionic/angular';
import {SplashScreen} from '#ionic-native/splash-screen/ngx';
import {StatusBar} from '#ionic-native/status-bar/ngx';
import {AppComponent} from './app.component';
import {AppRoutingModule} from './app-routing.module';
import {HttpClientModule} from '#angular/common/http';
#NgModule({
declarations: [
AppComponent,
],
entryComponents: [],
imports: [
BrowserModule,
IonicModule.forRoot(),
AppRoutingModule,
HttpClientModule,
],
providers: [
StatusBar,
SplashScreen,
{
provide: RouteReuseStrategy,
useClass: IonicRouteStrategy,
},
],
bootstrap: [
AppComponent,
],
})
export class AppModule {
}
I don't understand why it doesn't works... If you have any idea, I really appreciate your help!
Thanks!
Finally got it!
In api.service.ts, one header was initialized with a null value like this:
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: null, // The bad guy.
})
};
I have to change it with an empty value:
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: '', // The good guy.
})
};

How do use geolocation service in multiple ionic 4 pages?

I have built a geolocation service that I wish to use the getUserPosition() method on the home.page
location.service.ts
import { Injectable } from '#angular/core';
import { Geolocation, GeolocationOptions, Geoposition, PositionError } from '#ionic-native/geolocation/ngx';
#Injectable({
providedIn: 'root'
})
export class LocationService {
options: GeolocationOptions;
currentPos: Geoposition;
loc: any;
constructor( private geolocation: Geolocation ) { }
getUserPosition() {
return new Promise((resolve, reject) => {
this.options = {
maximumAge: 3000,
enableHighAccuracy: true
};
this.geolocation.getCurrentPosition(this.options).then((pos: Geoposition) => {
this.currentPos = pos;
const location = {
lat: pos.coords.latitude,
lng: pos.coords.longitude,
time: new Date(),
};
console.log('loc', location);
resolve(pos);
}, (err: PositionError) => {
console.log("error : " + err.message);
reject(err.message);
});
});
}
}
I access the service in my home.page
home.ts
import { Component, OnInit } from '#angular/core';
import { LocationService } from '../../services/geolocation/location.service';
#Component({
selector: 'app-home',
templateUrl: './home.page.html',
styleUrls: ['./home.page.scss'],
})
export class WorkalonePage implements OnInit {
getPosition: any;
constructor(
private LocationService: LocationService
) {}
ngOnInit() {
this.getPosition = this.LocationService.getUserPosition;
}
}
home.html
<ion-content>
<ion-button expand="full" color="primary" (click)="getUserPosition()">Get Location Sevice</ion-button>
</ion-content>
but when I click on (click)="getUserPosition()" on my html I get the error getUserPosition() is not a function. I have have been looking online for answers, but all answers involve using the geolocation in the home.ts file. Any help would be greatly appreciated.
From what I see in home.ts, you haven't defined a function named getUserPosition.
Add something along the lines of what's below to home.ts:
getUserPosition() {
this.LocationService.getUserPosition().then((pos) => {
this.getPosition = pos;
});
}
You can directly call your service method in html like this
LocationService.ts
getUserPosition() {
this.LocationService.getUserPosition().then((pos) => {
this.getPosition = pos;
});
}
Define service object in home.ts
constructor(public ls:LocationService){}
At home.html
<ion-button expand="full" color="primary" (click)="ls.getUserPosition()">
Get Location Sevice
</ion-button>

Can't bind to 'errorStateMatcher' since it isn't a known property of 'input'

I get an error while running npm test
Can't bind to 'errorStateMatcher' since it isn't a known property of 'input'. ("dth">
<input matInput placeholder="Product Name" formControlName="prod_name"
[ERROR ->][errorStateMatcher]="matcher">
My Spec file is as follows
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { CUSTOM_ELEMENTS_SCHEMA } from '#angular/core';
import { RouterTestingModule } from '#angular/router/testing';
import { ReactiveFormsModule } from '#angular/forms';
import { ProductAddComponent } from './product-add.component';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '#angular/forms';
describe('ProductAddComponent', () => {
let component: ProductAddComponent;
let fixture: ComponentFixture<ProductAddComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule, ReactiveFormsModule],
declarations: [ ProductAddComponent ],
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProductAddComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
How do I provide errorStateMatcher to test unit?
I think you need to import MatInputModule.
I had to add matInput attribute to the input element.
If You are getting this error while testing make sure you mock matInput aswell. That was my Issue. I imported MatInputModule in spec file, but that caused more errors later.

Cannot resolve all parameters for 'TypeDecorator'(?) .Make sure that typedecarator is decaorated with #injectable

I'm getting this error after fixing on how to import folders added to the app.
My app structure
My "app.ts" includes
import { bootstrap } from "#angular/platform-browser-dynamic";
import { Component,Inject } from "#angular/core";
import {JsonService} from "services";
import {Http,HTTP_PROVIDERS} from "#angular/http";
import {provide} from '#angular/core';
#Component({
selector: 'hello-world',
template: `
<div>
Hello world
</div>
`,
})
class HelloWorld {
constructor(#Inject(JsonService) private service :JsonService){
}
}
bootstrap(HelloWorld,[Http,HTTP_PROVIDERS,JsonService]);
my service
import {Injectable,Inject} from "#angular/core";
import {Http} from "#angular/http";
#Injectable
export class JsonService {
constructor(#Inject(Http) private http: Http) {
}
}
my system.js.config file is as follows
var map = {
'app': 'app',
'rxjs': 'node_modules/rxjs',
'#angular': 'node_modules/#angular',
'services': 'services',
'models': 'models'
};
// packages tells the System loader how to load when no filename and/or no extension
var packages = {
'rxjs': { defaultExtension: 'js' },
'models': { main: 'index.js', defaultExtension: 'js' },
'services': { main: 'index.js', defaultExtension: 'js' }
};
var packageNames = [
'#angular/common',
'#angular/compiler',
'#angular/core',
'#angular/http',
'#angular/platform-browser',
'#angular/platform-browser-dynamic',
'#angular/router',
'#angular/router-deprecated',
'#angular/testing',
'#angular/upgrade',
'services',
'models'
];
at the first i got 404 error that my services is missing after modifying the system.js and adding services an models ,i am getting this error
still getting this 404
Error: Cannot resolve all parameters for 'TypeDecorator'(?). Make sure that all the parameters are decorated with Inject or have valid type annotations and that 'TypeDecorator' is decorated with Injectable
link to complete code https://1drv.ms/u/s!Ai1EHDfZ96E2wij-MtwLHdCI-hVI
What resolved that error for me was the comment by JGW96 in this thread that in Angular 2 you cannot inject a service into a service. I was creating a view module for some JSON data and had split the subscription off into a view module. Once I moved it back to the main service module, and then included that main service as a provider in the view component, the decorator error resolved.

Resources