I have a polling set up to make AJAX calls on an interval using an Observable. Here's my code:
import { Injectable } from '#angular/core';
import { Http, Response } from "#angular/http";
import { Observable } from "rxjs/Rx";
import 'rxjs/add/operator/map';
#Injectable()
export class HttpAPIService {
metrics: Object;
constructor(private http: Http) {
// https://thinkster.io/tutorials/angular-2-http
this.getMetrics('https://jsonplaceholder.typicode.com/posts/1/comments').subscribe(data => this.metrics = data);
}
getMetrics(url: string) {
return Observable.interval(5000)
.switchMap(() => this.http.get(url))
.map((res: Response) => res.json());
}
}
The code waits 5 seconds before doing the first AJAX call. How can I change it so that the first AJAX call happens immediately? Also, if I use the following command in another module, will the subscription make my variable data-binded to the API call that is being called every 5 seconds?
this.variableIWantToBind = this.httpAPIService.getMetrics('https://jsonplaceholder.typicode.com/posts/1/comments').subscribe(data => console.log(data));
Use .startWith(0) as #Zircon pointed out
Related
Hope you're doing well in this difficult time. Trust all is well in your end.
I am trying to make a local JSON call but after compiling the code, the json response which I am receiving is, is at the end of the execution of all the functions below. Here in the code, I am calling my loadService function in the app component. I don't want to import the JSON file in the head and use rather want to use the http service because I have to make serveral JSON calls based on conditions.
Function call in the App.component.ts
this.myservice.loadService();
The output is:
function 1
function 2
function 3
http response data
Expected:
http response data
function 1
... etc.
import { Injectable } from '#angular/core';
import { HttpClient, } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class InjectService {
constructor(private http: HttpClient) {
}
loadService(){
//logic
this.jsoncall()
}
jsonCall(){
this.http.get("assets/abc.json").subscribe(data => {
console.log(data);
})
}
function1(){
console.log('function 1');
this.function2();
}
function2(){
console.log('function 2')!
this.function3();
}
function3(){
console.log('function 3');
}
}
Any help would be appreciated. Thank you. :)
The JSON file is obtained asynchronously. So the dependent statements need to async as well. You could pipe in the successive function calls using the RxJS tap operator. You could find more info about async data here.
If you're triggering the HTTP call from the component, I'd assume you have dependent statements over there as well. It's standard practice to return the observable from the service and subscribe to it in the component.
Try the following
Service
jsonCall(): Observable<any> { // <-- return the observable here
return this.http.get("assets/abc.json").pipe(
tap(_ => this.function())
);
}
Component
ngOnInit() {
this.injectService.jsonCall().subscribe(
res => {
console.log(res);
// other statements that depend on `res`
},
err => {
// handle error
}
);
}
loadService(){
//logic
this.jsoncall()
}
jsonCall(){
this.http.get("assets/abc.json").subscribe(data => {
console.log(data);
this.function1();
})
}
function1(){
console.log('function 1');
this.function2();
}
function2(){
console.log('function 2')!
this.function3();
}
function3(){
console.log('function 3');
}
Call the function 1 from inside of the service one then it will run line wise
On Load, I'm calling the function getData() which will read & map the response to Data[] model and returns back the result. But the function returning undefined object though it's value is assigned.
import { HttpClient} from '#angular/common/http';
import {Data} from "../model";
export class Service {
constructor(private http: HttpClient) {}
getData():Data[]{
var data: Data[];
this.http.get(url,{ responseType: 'blob'}).subscribe(response => {
response.text().then(value => {
data = <Data[]>JSON.parse(value);
console.log(data); ----> {Printing the values as expected i.e., Data Array}
});
});
console.log(data); ----> {Printing Undefined}
return testData;
}
onLoad(){
var data:Data[] = this.getData();
console.log(data) ---------> {Printing Undefined}
}
}
Data Class :
export class Data{
id:number;
name:string;
value:string;
}
Http calls are async calls. Subsequent call will get executed and will not wait for the promise to be resolve.
Your dependent code should be written once it resolved.
I'll just show what I would like to do, here's a little bit reconstruction for your codes:
import {from, Observable} from 'rxjs'; // <= several imports you might care
import {map, switchMap} from 'rxjs/operators'; // <= several imports you might care
export class Service {
constructor(private http: HttpClient) {}
getData(): Observable<Data[]> {
return this.http.get(someurl, {responseType: 'blob'})
.pipe(
switchMap((blob: Blob) => {
return from(blob.text());
}),
map(text => {
return JSON.parse(text) as Data[];
})
);
}
onLoad(): void {
this.getData().subscribe((data: Data[]) => {
console.log(data); // <=== this is where you can log data
});
}
}
The basic idea is you should return a aync(aka Observable) result for your http request, and pipe the first result(type of Blob) to another Observable(which is built from a promise) of string value, then map it to a Data array at last, in onLoad you should subscribe the async result and check the data in subscription. In a word, you should keep the data flow asynchronously until it is been resolved
I am learning TypeScript and currently I am trying to use google API for books. Issue that I have, is that at the time when expect to get get a response I have null. However, I am logging response and data is presented console. Next strange this that, this data is logged and the end of all my code (like the last thing, but not when I am actually doing logging method).
Is there a way to wait for response? My code parts:
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { OrderPage } from '../order/order';
import { BooksApiProvider } from '../../providers/books-api/books-api';
#Component({
selector: 'page-book',
templateUrl: 'book.html'
})
export class BookPage {
books:any;
constructor(public navCtrl: NavController, public booksProvide: BooksApiProvider) {
}
moveToOrder() {
debugger;
this.books = this.booksProvide.getBook();
}
}
import { Injectable } from '#angular/core';
import 'rxjs/add/operator/map';
#Injectable()
export class BooksApiProvider {
constructor() {
}
getBook(){
fetch('https://www.googleapis.com/books/v1/volumes?q=isbn:9780439139601')
.then(res => res.json())
.then(data => console.log(data))
}
}
Your getBook() method doesn't return anything, therefore, this.books will always be null.
As you're using fetch here, you can return the promise in your getBook() method allowing you to perform any action with the data retrieved.
getBook(): Promise<any> {
return fetch('https://www.googleapis.com/books/v1/volumes?q=isbn:9780439139601')
.then(res => res.json())
.then(data => {
console.log(data);
return Promise.resolve(data);
});
}
Then you can call .then on getBook() to do whatever you want with the data.
(Assign the data to this.books for example)
this.booksProvide.getBook()
.then(data => this.books = data);
You're using Angular thought, you should take a look at the HttpClient. It's pretty much the same as fetch unless it deals with RxJS and Observables which are widely used in Angular.
I am facing a problem with I have no clue how to solve it beacause I am so noobie with testing in front.
Right now I am testing a service that have this code:
import { Injectable } from '#angular/core';
import { EndpointLocatorService } from './endpointLocator.service';
import { Http, Headers } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
#Injectable()
export class CancelAppService {
constructor(private readonly http: Http,
private readonly _endPointLocator: EndpointLocatorService) { }
getScopeSignature(signatureToken: string) {
const url = this._endPointLocator.locate('cancelApp');
const language = sessionStorage.getItem('languageSession');
const headers = new Headers({
'Content-Type': 'application/json',
'Accept-Language': language,
'X-B3-TraceId': sessionStorage.getItem('requestID')
});
const body = JSON.stringify({ 'signatureToken': signatureToken });
return this.http.post(url, body, { headers }).map(data => {
return data.json();
}).catch((error: any) => {
return Observable.throw(new Error(error.status));
});
}
}
The testing file contains this:
import { TestBed } from '#angular/core/testing';
import { CancelAppService } from './cancelApp.service';
import { HttpModule } from '#angular/http';
import { EndpointLocatorService } from './endpointLocator.service';
import { AppConfig } from '../app.config';
import 'jasmine';
fdescribe('CancelAppService', () => {
let cancelService: CancelAppService; // Add this
let endLocator: EndpointLocatorService;
const mockData = {
"signature_token": "returnedToken"
};
const body = JSON.stringify({ 'signatureToken': 'givenToken' });
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpModule
],
providers: [
CancelAppService,
AppConfig,
EndpointLocatorService
]
});
cancelService = TestBed.get(CancelAppService); // Add this
endLocator = TestBed.get(EndpointLocatorService);
});
it('should be created', () => { // Remove inject()
expect(cancelService).toBeDefined();
});
it('should call the service', () => {
spyOn(endLocator, 'locate').and.returnValue('someUrl');
spyOn(cancelService, 'getScopeSignature').and.callThrough();
cancelService.getScopeSignature(body);
expect(cancelService.getScopeSignature).toHaveBeenCalled();
});
it('should return a object', () => {
(done: DoneFn) => {
spyOn(endLocator, 'locate').and.returnValue('someUrl');
spyOn(cancelService, 'getScopeSignature').and.returnValue(mockData);
cancelService.getScopeSignature(body).subscribe(data => {
expect(data).toEqual(mockData);
done();
});
}
});
});
The problem is that, when I try to test the returned data map, appears me like a success test, but the coverage says that I have no coveraged the lines of map, and catch.
Any idea what am I doing wrong? and how to solve it?
Thank you so much!!
You are mocking the service being tested, you should mock the service being used by your service EndpointLocatorService and maybe even HttpClient to minimize mocking. You can verify that by stepping through your code.
Let me explain in the code why those lines are not hit.
// In this example, the lines are hit, but after the test exited.
it('should call the service', () => {
spyOn(endLocator, 'locate').and.returnValue('someUrl');
spyOn(cancelService, 'getScopeSignature').and.callThrough();
// Here you are calling a method yourself and checking that it was
// called, it doesn't make sense, this test should make sure
// You should check that your endLocator was called.
// Note that because you are not waiting for the promise to resolve,
// The test finishes before either callback is handled.
cancelService.getScopeSignature(body);
expect(cancelService.getScopeSignature).toHaveBeenCalled();
});
// In this example the lines in your test are never hit because you
// mocked it
it('should return a object', () => {
(done: DoneFn) => {
spyOn(endLocator, 'locate').and.returnValue('someUrl');
// Here you are mocking getScopeSignature completely so it doesn't
// Run any of your lines that you are trying to test
// It just tests that you mocked it correctly
spyOn(cancelService, 'getScopeSignature').and.returnValue(mockData);
cancelService.getScopeSignature(body).subscribe(data => {
expect(data).toEqual(mockData);
done();
});
}
});
I can't rewrite your test so it actually tests it because it depends on how the EndpointLocatorService works and you may choose to mock it at that level also. I typically choose to mock at the HttpClient level since I can copy paste responses from the network tab.
The point is that you need to mock services used by your service, not the service itself. You have the option to mock the direct services used by your component, or you can mock at a deeper level. For example, your EndpointLocatorService may need to call HttpClient and you could choose to mock either service.
See https://angular.io/guide/testing#httpclienttestingmodule and https://alligator.io/angular/testing-httpclient/
I understand using observable I can execute a method when the request is completed, but how can i wait till a http get is completed and return the response using in ng2 http?
getAllUser(): Array<UserDTO> {
this.value = new Array<UserDTO>();
this.http.get("MY_URL")
.map(res => res.json())
.subscribe(
data => this.value = data,
err => console.log(err),
() => console.log("Completed")
);
return this.value;
}
the "value" will is null when its returned because get is async..
your service class: /project/app/services/sampleservice.ts
#Injectable()
export class SampleService {
constructor(private http: Http) {
}
private createAuthorizationHeader() {
return new Headers({'Authorization': 'Basic ZXBossffDFC++=='});
}
getAll(): Observable<any[]> {
const url='';
const active = 'status/active';
const header = { headers: this.createAuthorizationHeader() };
return this.http.get(url + active, header)
.map(
res => {
return res.json();
});
}
}
your component: /project/app/components/samplecomponent.ts
export class SampleComponent implements OnInit {
constructor(private sampleservice: SampleService) {
}
ngOnInit() {
this.dataset();
}
dataset(){
this.sampleservice.getAll().subscribe(
(res) => {
// map Your response with model class
// do Stuff Here or create method
this.create(res);
},
(err) => { }
);
}
create(data){
// do Your Stuff Here
}
}
By looking at the angular source (https://github.com/angular/angular/blob/master/packages/http/src/backends/xhr_backend.ts#L46), it is apparent that the async attribute of the XMLHttpRequest is not getting used. The third parameter of XMLHttpRequest needs to be set to "false" for synchronous requests.
Please find code for your problem
Below is component and service file.And Code is Working fine for synchornize
import { Component, OnInit } from '#angular/core';
import { LoginserviceService } from '../loginservice.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
model:any={};
constructor(private service : LoginserviceService) {
}
ngOnInit() {
}
save() {
this.service.callService(this.model.userName,this.model.passWord).
subscribe(
success => {
if(success) {
console.log("login Successfully done---------------------------- -");
this.model.success = "Login Successfully done";
}},
error => console.log("login did not work!")
);
}
}
Below is service file..
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import { UserData } from './UserData';
import 'rxjs/add/operator/map'
import 'rxjs/add/operator/toPromise'
import {Observable} from 'rxjs/Rx'
#Injectable()
export class LoginserviceService {
userData = new UserData('','');
constructor(private http:Http) { }
callService(username:string,passwrod:string):Observable<boolean> {
var flag : boolean;
return (this.http.get('http://localhost:4200/data.json').
map(response => response.json())).
map(data => {
this.userData = data;
return this.loginAuthentication(username,passwrod);
});
}
loginAuthentication(username:string,passwrod:string):boolean{
if(username==this.userData.username && passwrod==this.userData.password){
console.log("Authentication successfully")
return true;
}else{
return false;
}
}
}
Another solution would be to implement a priority queue of sort.
From what I understand http requests do not get executed until you add subscribers. Therefore, you can do something like this:
Observable<Response> observable = http.get("/api/path", new RequestOptions({}));
requestPriorityQueue.add(HttpPriorityQueue.PRIORITY_HIGHEST, observable,
successResponse => { /* Handle code */ },
errorResponse => { /* Handle error */ });
This assumes that requestPriorityQueue is a service injected into your component. The priority queue would store entries in an array in the following format:
Array<{
observable: Observable<Response>,
successCallback: Function,
errorCallback: Function
}>
You would have to decide how the elements are added to your array. Finally, the following will happen in the background:
// HttpPriorityQueue#processQueue() called at a set interval to automatically process queue entries
The processQueue method would do something like this:
protected processQueue() {
if (this.queueIsBusy()) {
return;
}
let entry: {} = getNextEntry();
let observable: Observable<Response> = entry.observable;
this.setQueueToBusy(); // Sets queue to busy and triggers an internal request timeout counter.
observable.subscribe()
.map(response => {
this.setQueueToReady();
entry.successCallback(response);
})
.catch(error => {
this.setQueueToReady();
entry.errorCallback(error);
});
}
If you are able to add new dependencies you could try using the following NPM package: async-priority-queue
I looked and I couldn't find any way to make an HTTP call sync instead of async.
So the only way around this: wrap your call in a while loop with a flag. Don't let the code continue until that flag has "continue" value.
Pseudo code as follows:
let letsContinue = false;
//Call your Async Function
this.myAsyncFunc().subscribe(data => {
letsContinue = true;
};
while (!letsContinue) {
console.log('... log flooding.. while we wait..a setimeout might be better');
}
as you see, first callback waiting for a data from request and
there you can go on with your logic (or use the third one)
example:
.. subscribe( data => {
this.value = data;
doSomeOperation;
},
error => console.log(error),
() => {console.log("Completed");
or do operations here..;
}
});
How about to use $.ajax(of jQuery) or XMLHttpRequest.
It can use as asynchornize.
You should not try to make http calls behave synchronously. Never a good idea.
Coming to your getAllUser implementation it should return an observable from the function and the calling code should subscribe instead of you creating a subscription inside the method itself.
Something like
getAllUser(): Observable<UserDTO> {
return this.http.get("MY_URL")
.map(res => res.json());
}
In you calling code, you should subscribe and do whatever you want.