I've built an Angular app that replicates : https://jobs.github.com/positions.api
Unfortunately, they provide only 50 available positions per page,
so in order to fetch the next 50 jobs, I have to add "?page=X" to another GET request.
Therefore, my GET response returns only the first page.
Is there any way to fetch the total amount of positions by using only 1 GET request or by any other way?
Much Appreciated!
My code:
Service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class PositionserviceService {
// uri = 'https://jobs.github.com/positions'
constructor(public http: HttpClient) { }
getAllPositions(){
return this.http.get('https://cors-anywhere.herokuapp.com/https://jobs.github.com/positions.json');
}
getNextPage4(page){
return this.http.get(`https://cors-anywhere.herokuapp.com/https://jobs.github.com/positions.json?page=${page}`);
}
}
Componnent.ts file:
import { Component, OnInit } from '#angular/core';
import { PositionserviceService } from '../services/positionservice.service';
#Component({
selector: 'app-showallpositions',
templateUrl: './showallpositions.component.html',
styleUrls: ['./showallpositions.component.css']
})
export class ShowallpositionsComponent implements OnInit {
positions: Object
positions4: Object
isCollapsed = true;
constructor(public positionService: PositionserviceService) { }
ngOnInit() {
this.positionService.getAllPositions().subscribe(data => {
this.positions = data;
console.log(this.positions)
})
// this.getPage4()
}
// getPage4(){
// this.positionService.getNextPage4().subscribe(data4 => {
// this.positions4 = data4;
// console.log("Page 4:", this.positions4)
// })
// }
}
The docs don't say anything about this. I think a loop that fetches all the jsons and merges them would be a possible solution, even though I wouldn't recommend making so many calls to the api.
If you're looking for just the number of positions, you could make the loop request the list for each page, until it crashes. If you add a counter to the loop and multiply its result by 50, you're getting pretty close to the actual number.
I found some extra info here. This user suggests that you can get the url for the last page from your request.
You can also just follow this link and it will show the total amount of jobs on top.
Related
I am creating Login page and want to call /login and /send-otp api together by a single function.
I have already created a login and registration page.
My code :
user-data.service.ts :
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class UserdataService {
url= 'http://localhost:9197/register';
url2= 'http://localhost:9197/login';
url3= 'http://localhost:9197/send-otp';
constructor(private http:HttpClient) {}
saveRegistration(data:any){
return this.http.post(this.url, data);
}
loginDone(ldata:any){
return this.http.post(this.url2, ldata);
return this.http.post(this.url3, ldata);
}
}
How to call multiply api ??
loginDone(ldata:any){
return this.http.post(this.url2, ldata);
return this.http.post(this.url3, ldata);
}
Apply TypeScript alias definition to your code:
Here in a more simplificated way, you have a multiple return with an alias structure.
type multi={
x:PelotaAzul,
y:PelotaRugosa,
}
function multiple():multi{
let x:PelotaAzul=new PelotaAzul("jj",5);
let y:PelotaRugosa=new PelotaRugosa("s",3,6)
let ab : multi ={x,y};
return ab;
}
You can also return an array of the both parameters like this
loginDone(ldata:any){
return [this.http.post(this.url2, ldata),this.http.post(this.url3, ldata)];
}
I find creating aliases more elegant but for faster results do the second one.
You are probably miss thinking your problem.
First of all. A return statement will always stop the function execution, doesn't matters if theres 100 lines after it.
Second, your logic probably will be: call login first then after (using or not the response) call the send-otp.
Or, your logic might be: call login and send-otp concurrently, but the loginDone method should only return some valid state if both requests run fine.
That's why other answers are completely wrong.
You must cascade your observables using pipe and switchmap for example:
return of([this.url2, this.url3]).pipe(
switchMap((url) => this.http.post(url, ldata))
)
If you want to return multiple values, you can return like this.
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class UserdataService {
url= 'http://localhost:9197/register';
url2= 'http://localhost:9197/login';
url3= 'http://localhost:9197/send-otp';
constructor(private http:HttpClient) {}
saveRegistration(data:any){
return this.http.post(this.url, data);
}
loginDone(ldata:any){
return {url1: this.url, url2: this.url2, url3: this.url3};
}
}
If you want to call multiple api, check this one .
https://medium.com/bb-tutorials-and-thoughts/how-to-make-parallel-api-calls-in-angular-applications-4b99227e7458
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I've been rewriting an Angular app which previously omitted the use of http requests. For this reason, lots of logic is contained in a service component and the http request was simulated by working with an array of the following format:
[{"name": "somename1", "subset1": [
{"name": "somesubname1", "subset2": [
{"somemetric1": 0, "somemetric2: 1, "somemetric3": 2}]}]}]
This is exactly the format which is also displayed on some web api (https://localhost:5001/api/values e.g.).
My service looks like this:
import { Injectable } from '#angular/core'
import { Subject, Observable } from 'rxjs';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class StrategyService{
data:any
constructor(private httpClient: HttpClient) {
}
getData() {
this.data = this.httpClient.get("https://localhost:5001/api/values")
}
If I console.log(this.data) in e.g. getData() and call this function when running my app, "undefined" is returned in the console. When I would replace the body of getData() with
this.httpClient.get("https://localhost:5001/api/values").subscribe(data => {data = console.log(data)})
the console does return the array in the format as desired (and displayed in the first block). Console logging is in fact not what I need since I need to be able to access the data in other functions inside my service component which would need array-specific methods such as .find etc.
I feel like this is a simple thing to do, but I've had no succes so far. I've tried
import { Injectable } from '#angular/core'
import { Subject, Observable } from 'rxjs';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class StrategyService{
dat:any
constructor(private httpClient: HttpClient) {
}
getData() {
this.httpClient.get("https://localhost:5001/api/values").subscribe(data => {this.dat = data})
}
console.log(this.dat) still returns "undefined".
This is a classic starting problem everyone has with async code. Have a research of rxjs + how async code works in Typescript.
The solution to your problem is to check the data within the subscribe block as that happens after the response from your api is back.
this.httpClient.get("https://localhost:5001/api/values").subscribe(data => console.log(data))
public myFunction() {
this.httpClient.get("https://localhost:5001/api/values").subscribe(data => {
this.data = data;
console.log(this.data); // data is defined here
})
console.log(this.data); // data will be undefined here
}
Links to useful resources:
https://www.learnrxjs.io/
https://angular.io/guide/http
I have an component where i am adding a new object called customer by calling the api like this:
public onAdd(): void {
this.myCustomer = this.customerForm.value;
this.myService.addCustomer(this.myCustome).subscribe(
() => { // If POST is success
this.callSuccessMethod();
},
(error) => { // If POST is failed
this.callFailureMethod();
},
);
}
Service file:
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Observable, Subject } from 'rxjs';
import {ICustomer } from 'src/app/models/app.models';
#Injectable({
providedIn: 'root',
})
export class MyService {
private baseUrl : string = '....URL....';
constructor(private http: HttpClient) {}
public addCustomer(customer: ICustomer): Observable<object> {
const apiUrl: string = `${this.baseUrl}/customers`;
return this.http.post(apiUrl, customer);
}
}
As shown in component code, i have already subscribed the api call like this:
this.myService.addCustomer(this.myCustome).subscribe(
() => { // If POST is success
.....
},
(error) => { // If POST is failed
...
},
);
But,I want to subscribe the results in another component, I have tried like this:
public getAddedCustomer() {
this.myService.addCustomer().subscribe(
(data:ICustomer) => {
this.addedCustomer.id = data.id; <======
}
);
}
I am getting this lint error: Expected 1 arguments, but got 0 since i am not passing any parameter.
What is the right approach to subscribe the api call in other components? after POST operation.
Because i want to get added object id for other functionality.
Well it totally depends on the design of your application and the relation between components. You can use Subjects for multicasting the data to multiple subscribers.
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Observable, Subject } from 'rxjs';
import { ICustomer } from 'src/app/models/app.models';
#Injectable({
providedIn: 'root',
})
export class MyService {
private baseUrl : string = '....URL....';
private latestAddedCustomer = new Subject();
public latestAddedCustomer$ = this.latestAddedCustomer.asObservable()
constructor(private http: HttpClient) {}
public addCustomer(customer: ICustomer): Observable<object> {
const apiUrl: string = `${this.baseUrl}/customers`;
return this.http.post(apiUrl, customer).pipe(map((data) => this.latestAddedCustomer.next(data)));
}
}
and subscribing to the subject as follows
this.latestAddedCustomer$.subscribe()
should get you the latest added customer details. Even though i would not do this the way its written. I would basically write a seperate service to share the data between the components or would write a cache service if its used across the application. But the idea here is to use the concept of Subjects. You can read more about it Here
I have a DataServive, that fetches content from an API:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
import { map, catchError, retry } from 'rxjs/operators';
import { environment } from 'src/environments/environment';
#Injectable()
export class DataService {
this.request = {
count: 10
}
constructor(private http: HttpClient) { }
private handleError(error) {
console.log(error);
}
public getData(count): Observable<any> {
this.request.count = count;
return this.http.post<any>(environment.api + '/path', this.request).pipe(
map(response => {
return response;
}),
catchError(error => {
this.handleError(error);
return [];
})
);
}
}
This DataServie is consumned by a component like this:
ngOnInit() {
const subscriber = this.dataService.getData(this.count).subscribe((data) => { this.data = data; });
}
And it works fine.
However the user is able to change the variable this.count (how many items should be displayed) in the component. So I want to get new data from the server as soon as this value changes.
How can I achieve this?
Of course I could call destroy on this.subscriber and call ngOnInit() again, but that dosn't seem like the right way.
Easiest ways is just to unsubscribe:
subscriber: Subscription;
ngOnInit() {
this.makeSubscription(this.count);
}
makeSubscription(count) {
this.subscriber = this.dataService.getData(this.count).subscribe((data) => { this.data = data; });
}
functionInvokedWhenCountChanges(newCount) {
this.subscriber.unsubscribe();
makeSubscription(newCount);
}
But because count argument is just a one number it means HTTP always asks for data from 0 to x. In this case, you can better create another subject where you can store previous results (so you don't need to make useless HTTP requests) and use that subject as your data source. That needs some planning on your streams, but is definitely the preferred way.
When the user changes count, call getData(count) again with the updated count value. Need to see your html file, but having a button with (click)="getData(count)" may help.
When angular receive the data from the API via HTTP call then how to check some property has an image or not.
Here is how to solve this real world example in a very easy way.
organaization.component.ts
import { Component, OnInit } from '#angular/core';
export class OrganizationComponent implements OnInit {
organizationObj:any = {
logo: 'http://someurl.com/assets/images/'
image: 'http://someurl.com/assets/images/someimg.jpg'
};
imgNoExt:boolean = false;
// here is the simple image extension check function
private urlImgHasExt(url){
return(url.match(/\.(jpeg|jpg|gif|png)$/) != null);
}
ngOnInit() {
this.imgNoExt = this.urlImgHasExt(this.organizationObj.logo);
console.log(this.imgNoExt) //this will return false value
this.imgNoExt = this.urlImgHasExt(this.organizationObj.image);
console.log(this.imgNoExt) //this will now return true value
}
}