I have the following code which is a simple service that goes back to the server to fetch some data:
import { Injectable } from '#angular/core';
import { Action } from '../shared';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { Authenticated } from '../authenticated';
import 'rxjs/Rx';
#Injectable()
export class ActionsService {
private url = 'http://localhost/api/actions';
constructor(private http: Http, private authenticated : Authenticated) {}
getActions(search:string): Observable<Action[]> {
let options = this.getOptions(false);
let queryString = `?page=1&size=10&search=${search}`;
return this.http.get(`${this.url + queryString}`, options)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(response: Response) {
let body = response.json();
return body || { };
}
private handleError (error: any) {
let errMsg = (error.message) ? error.message : error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg); // log to console instead
if (error.status == 403) {
this.authenticated.logout();
}
return Observable.throw(errMsg);
}
private getOptions(addContentType: boolean) : RequestOptions {
let headers = new Headers();
if (addContentType) {
headers.append('Content-Type', 'application/json');
}
let authToken = JSON.parse(localStorage.getItem('auth_token'));
headers.append('Authorization', `Bearer ${authToken.access_token}`);
return new RequestOptions({ headers: headers });
}
}
Everything works as expected except for handleError. As soon as getActions receives an error from the server it goes into the this.handleError method which again works fine up until the section where this.authenticated.logout() should be called. this.autenticated is undefined and I am not sure if it is because "this" is referring to another object or if ActionSerivce's local variables are made null when and http exception occurs. The authenticated local variable is properly injected (I did a console.log in the constructor and it was there).
The problem is that you are not binding the this context in your callback function. You should declare your http call like this for example:
return this.http.get(`${this.url + queryString}`, options)
.map(this.extractData.bind(this)) //bind
.catch(this.handleError.bind(this)); //bind
Another option could be to pass an anonymous function and call the callback from there:
return this.http.get(`${this.url + queryString}`, options)
.map((result) => { return this.extractData(result)})
.catch((result) => { return this.handleError(result}));
And yet another option is to declare your callback functions a little differently, you can keep your http call the way you had it before:
private extractData: Function = (response: Response): any => {
let body = response.json();
return body || { };
}
private handleError: Function = (error: any): any => {
//...
}
Related
I have a class called PostController, and I trying to test the following function create:
class PostController {
constructor(Post) {
this.Post = Post;
}
async create(req, res) {
try {
this.validFieldRequireds(req);
const post = new this.Post(req.body);
post.user = req.user;
...some validations here
await post.save();
return res.status(201).send(message.success.default);
} catch (err) {
console.error(err.message);
const msg = err.name === 'AppError' ? err.message :
message.error.default;
return res.status(422).send(msg);
}
}
My test class is:
import sinon from 'sinon';
import PostController from '../../../src/controllers/posts';
import Post from '../../../src/models/post';
describe('Controller: Post', async () => {
it.only('should call send with sucess message', () => {
const request = {
user: '56cb91bdc3464f14678934ca',
body: {
type: 'Venda',
tradeFiatMinValue: '1',
... some more attributes here
},
};
const response = {
send: sinon.spy(),
status: sinon.stub(),
};
response.status.withArgs(201).returns(response);
sinon.stub(Post.prototype, 'save');
const postController = new PostController(Post);
return postController.create(request, response).then(() => {
sinon.assert.calledWith(response.send);
});
});
});
But I'm getting the following error:
Error: Timeout of 5000ms exceeded. For async tests and hooks, ensure
"done()"
is called; if returning a Promise, ensure it resolves.
(D:\projeto\mestrado\localbonnum-back-end\test\unit\controllers\post_spec.js)
Why?
Most probably it's because misuse of sinon.stub.
You've
sinon.stub(Post.prototype, 'save');
without telling what this stub will do, so in principle this stub will do nothing (meaning it returns undefined).
IDK, why you don't see other like attempt to await on stub.
Nevertheless, you should properly configuture 'save' stub - for example like this:
const saveStub = sinon.stub(Post.prototype, 'save');
saveStub.resolves({foo: "bar"});
My server is developed on Node.js. It is a long-polling service (e.g. chat): it gives the following API:
join() //listening for new events
align(fromId) //retrieving events from an id
send(data) //creating an event
The long-polling is implemented by the join(): it sends a request and the server answers when there is a new event.
Front end with Ionic2
There are 2 pages: Page1 and Page2. Where Page2 is the viewer of my events, where the long-polling communication is running.
So I start from Page1 and then I push() the second page Page2. Until now everything works fine; but if I pop() the Page2 and then push() again the Page2 then I can see that there is still running the join() of the previous instance of my Page2. This behaviour creates duplicated join(): if I push/pop Page2 many times I will have many long-polling communication with the server.
So I'm trying to find a way to kill the join() instance, which is a HTTP.get request, when leaving the page.
Let's see now my code.
This is the provider of my Ionic2 in charge of the communication with the server
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class MyProvider {
...
constructor(private http: Http) {
this.token_access = null;
this.token_room = null;
}
...
join(){
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('x-access-token',this.getToken());
return Observable.create(observer =>{
this.http.get('/localhost/chat/'+this.room,{headers : headers})
.map(res => res.json())
.subscribe(
data=>{
observer.next(data);
},
(err) =>{
observer.error(err);
}
);
})
}
send(message){
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('x-access-token',this.getToken());
headers.append('x-chat-token',this.getRoomToken());
return Observable.create(observer =>{
this.http.post('/localhost/chat/'+this.room+'/send', JSON.stringify({
event: message
}),{headers : headers})
.map(res => res.json())
.subscribe(
data=>{
observer.next(data);
},
(err) =>{
observer.error(err);
}
);
})
}
align(from){
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('x-access-token',this.getToken());
headers.append('x-chat-token',this.getRoomToken());
return Observable.create(observer =>{
this.http.post('/localhost/chat/'+this.room+'/align', JSON.stringify({
fromId: from
}),{headers : headers})
.map(res => res.json())
.subscribe(
data=>{
observer.next(data);
},
(err) =>{
observer.error(err);
}
);
})
}
}
The Page1 just push the Page2 with a button that calls the the following code (page1.ts):
...
export class Page1 {
...
constructor(public navCtrl: NavController, public myProviderService: MyProvider) {
}
.....
toPage2(){
this.navCtrl.push(Page2);
}
And my Page2 is implemented by the following code:
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { MyProvider } from '../../providers/myprovider';
import { Event } from '../../components/event';
#Component({
selector: 'page-chat',
templateUrl: 'chat.html'
})
export class ChatPage {
eventsList: Array<Event>;
message: any;
last_event: any;
msg: any;
constructor(public navCtrl: NavController, public myProviderService: MyProvider) {
this.last_event = -1;
this.join();
this.eventsList= new Array();
}
join(){
this.myProviderService.join().subscribe(
(data)=>{
if(data.success){
this.last_event = this.last_event + 1;
if(this.last_event == data.event.id){
//up to now all events are correctly received
this.eventsList.push(data.event);
}else{
//some events are missing
this.last_event = this.last_event - 1;
this.align();
}
this.join();
}else{
this.message=data.message;
//TBD sleep....
//this.join();
}
},
(err) => {
this.message="Connectivity with server Lost...";
//TBD sleep....
//this.join();
});
}
align(){
this.myProviderService.align(this.last_event + 1).subscribe((data)=>{
if(data.success){
for (var i=0;i<data.events.length;i++) {
this.eventsList.push(new Event(data.events[i].id,data.events[i].data,data.events[i].user));
this.last_event = this.last_event + 1;
};
}else{
this.message=data.message;
}
},
(err) => {
this.message="Failure receiving messages";
});
}
send(): void{
this.myProviderService.send(this.msg).subscribe((data)=>{
if(data.success){
this.msg='';
}else this.message=data.message;
},
(err) => {
this.message="Error while authenticating";
})
}
ionViewDidLoad() {
}
ionViewDidEnter() {
}
}
So coming back to my question:
How can I kill the join() (kill the HTTP.get request) instance of my Page2 when this is not used, in order to prevent duplicated join()?
I think that if you have a provider that is globally added to Providers section of your app (meaning that it can act as a Singleton service), then you can use the following:
Every time Page 2 calls the join() method of your provider check a hasAlreadyJoined boolean variable in your provider.
This variable is set to true every time the join() method is called.
If the join() has not been called, call it and update the variable accordingly.
So, even though every time Page 2 calls the join() method of MyProvider, this method does the actual http request only if hasAlreadyJoined is false.
For you to be sure that every time a MyProvider instance is initiated it's variables are "static", the provider should be declared at the global Providers section of your app module file, not at the page's providers section.
I'm trying to call a service HTTP method and eventually return an error message but after a week of trying many things (Promises, Observables, ...) I can't get it to work. I hope anybody can help me out?
I'm kind of new to Angular2 and working alone on this project, with no one else around me with any Angular expertise. I did get a 3-day training course.
Component
#Component({
templateUrl: 'build/pages/login/login.html'
})
export class LoginPage {
error: string;
constructor(private navController: NavController, private auth: AuthService) {
}
private login(credentials) {
// Method calling the login service
// Could return an error, or nothing
this.error = this.auth.login(credentials);
// If there is no error and the user is set, go to other page
// This check is executed before previous login methode is finished...
if (!this.error && this.auth.user) {
this.navController.setRoot(OverviewPage);
}
}
}
AuthService
#Injectable()
export class AuthService {
private LOGIN_URL: string = "http://localhost:8080/rest/auth";
private USER_URL: string = "http://localhost:8080/rest/user";
private contentHeader: Headers = new Headers({
"Content-Type": "application/json"
});
errorMessage: string;
user: User;
constructor(private http: Http) {
}
login(credentials) {
let contentHeader = new Headers({
"Content-Type": "application/json"
});
this.http.post(this.LOGIN_URL, JSON.stringify(credentials), { headers: contentHeader })
.map(res => res.json())
.catch(this.handleError)
.subscribe(
data => this.handleLogin(data),
err => this.handleError
);
// could return an errorMessage or nothing/null
return this.errorMessage;
}
private handleLogin(data) {
let token = data.token;
this.getAccount(token);
}
private getAccount(token) {
let authHeader = new Headers({
"Content-Type": "application/json",
"X-Auth-Token": token
});
this.http.get(this.USER_URL, { headers: authHeader })
.map(res => res.json())
.catch(this.handleError)
.subscribe(
data => this.setUser(data),
err => this.errorMessage = err
);
}
private setUser(data) {
this.user = new User(data.naam, data.voornaam);
}
private handleError(error) {
// this.errorMessage is not saved?
if (error.status === 401) {
this.errorMessage = '401';
} else if (error.status === 404) {
this.errorMessage = '404';
} else {
this.errorMessage = 'Server error';
}
return Observable.throw(error.json() || 'Server error');
}
}
I think your problem is that your login method is returning a flat value (errorMessage). Since the login method is making an asynchronous request that value will not be initialized, it will always return null. If I were to set this up I would have the login method return an Observable.
Then to make things a bit more complicated it appears you want to make a consecutive call after login to get the logged in user. If you don't want your login method to emit until you've completed both calls you have to combine them somehow. I think switch can do this.
#Injectable()
export class AuthService {
private LOGIN_URL: string = "http://localhost:8080/rest/auth";
private USER_URL: string = "http://localhost:8080/rest/user";
private contentHeader: Headers = new Headers({
"Content-Type": "application/json"
});
user: User;
constructor(private http: Http) {
}
login(credentials) {
let contentHeader = new Headers({
"Content-Type": "application/json"
});
let response:Observable<Response> = this.http.post(this.LOGIN_URL, JSON.stringify(credentials), { headers: contentHeader });
//Take response and turn it into either a JSON object or
//a string error.
//This is an Observable<any> (any is returned by json())
let jsonResponse = response.map(res => res.json())
.catch(err => this.handleError(err));
//Take JSON object and turn it into an Observable of whatever the
//login request returns
//This is an Observable<Observable<any>> (Observable<any> is returned
//by handleLogin
let userResponse = jsonResponse.map(
data => this.handleLogin(data)
);
//Switch to the observable of the login request
//This is an Observable<any>, we will switch to the Observable<any>
//returned by handleLogin
let finalResponse = userResponse.switch();
//Hide actual response value from user. This will return an
//observable that will emit null on success and an error message
//on error
//Again, an Observable<any> since we're mapping to null
return finalResponse.map(res => null);
}
//We need to return this call as an observable so we can wire it into
//our chain
private handleLogin(data) {
let token = data.token;
return this.getAccount(token);
}
private getAccount(token) {
let authHeader = new Headers({
"Content-Type": "application/json",
"X-Auth-Token": token
});
let loginResponse = this.http.get(this.USER_URL, { headers: authHeader })
.map(res => res.json())
.catch((err) => this.handleError(err));
loginResponse.subscribe(
data => this.setUser(data)
);
return loginResponse;
}
private setUser(data) {
this.user = new User(data.naam, data.voornaam);
}
private handleError(error) {
let errorMessage = "Uninitialized";
if (error.status === 401) {
errorMessage = '401';
} else if (error.status === 404) {
errorMessage = '404';
} else {
errorMessage = error.json() || 'Server error';
}
return Observable.throw(errorMessage);
}
}
Now in your login component you will need to listen asynchronously to the response. This won't happen immediately (probably pretty quick with localhost, but may take a while in the real world) so I've added a loginDisabled that you can use to prevent the user from hitting the login button twice while waiting for the login request to be fulfilled.
#Component({
templateUrl: 'build/pages/login/login.html'
})
export class LoginPage {
error: string;
loginDisabled:boolean = false;
constructor(private navController: NavController, private auth: AuthService) {
}
private login(credentials) {
// Method calling the login service
// Could return an error, or nothing
this.loginDisabled = true;
this.auth.login(credentials).subscribe(
rsp => {
//On success, navigate to overview page
this.navController.setRoot(OverviewPage);
}, err => {
//On failure, display error message
this.error = err;
this.loginDisabled = false;
});
}
}
No promises this is all correct (I don't have anything to test it against) but it should be the right general direction.
I have a little problem with my Angular2 app. I want to get some data from server for my user login, but my code is going ahead and I have a lot of bugs with it. I want to wait for answer from server, then do something with my data.
This is my code:
import { Injectable } from '#angular/core';
import { Http, Response, Headers } from '#angular/http';
import { Observable } from 'rxjs/Rx';
import { User } from './user';
#Injectable()
export class UserService {
public usersTmp: Array<Object> = new Array<Object>();
public users: Array<User>;
public user: User = new User();
public noteToSend;
constructor(private http: Http) { }
getUsers() {
var headers = new Headers();
headers.append('Accept', 'q=0.8;application/json;q=0.9');
this.http.get('/AngularApp/api/users', { headers: headers })
.map((res: Response) => res.json())
.subscribe(
data => {
console.log(data);
this.usersTmp = data;
},
err => console.error(err),
() => console.log('done')
);
this.users = new Array<User>();
for (var i = 0; i < this.usersTmp.length; i++) {
this.user = new User();
this.user.id = this.usersTmp[i]["userId"];
this.user.name = this.usersTmp[i]["userName"];
this.user.email = this.usersTmp[i]["userEmail"];
this.user.pass = this.usersTmp[i]["userPassword"];
this.users.push(this.user);
}
return this.users;
}
As I noticed my code is going to the for loop until I get answer from server, so I return just empty array. Anyone can help me with that?
In the service, you should return the Observable that your component can subscribe to. It cannot work they way you do it due to the asynchronous mode of the get request.
As a proposal, your service could look similar to this
getUsers() {
let headers = new Headers();
headers.append('Accept', 'q=0.8;application/json;q=0.9');
return this.http.get('/AngularApp/api/users', { headers: headers })
.map((res: Response) => res.json());
}
And the relevant part of your component like this:
constructor(private userService:UserService) {
this.userService.getUsers().subscribe(
data => this.iterateOverUsers(data));
}
iterateOverUsers(data) {
// here comes your for loop
}
1.I can't setup a proper request, with useCredentials.Can anyone tell me what's wrong with the code?
import { Injectable } from '#angular/core';
import { Http, Response,Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Observable';
#Injectable ()
export class StructureRequestService {
result: Object;
constructor (private http: Http) {
2.use XHR object - I think the problem is here.
let _build = (<any> http)._backend._browserXHR.build;
(<any> http)._backend._browserXHR.build = () => {
let _xhr = _build();
_xhr.withCredentials = true;
return _xhr;
};
}
private myUrl = 'http://manny.herokuapp.com/audit/get/structure';
//create an http request
sendRequest() {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({
headers: headers
// , withCredentials: true
});
return this.http.post(this.myUrl,options)
.map((res: Response) => res.json())
.subscribe(res => {this.result = res;});
}
}
You are missing the post body parameter in your post request. It must be a string
return this.http.post(this.myUrl, body, options)
Body
Despite the content type being specified as JSON, the POST body must actually be a string. Hence, we explicitly encode the JSON hero content before passing it in as the body argument.
ref: https://angular.io/docs/ts/latest/guide/server-communication.html