I'm trying to make a request from an external API in angular 2.
I want to manage the data request in 2 files and display the result as json.
My data-output component looks like this:
import {Component} from '#angular/core'
import {DataService} from './datavisualisation.service'
#Component({
selector: 'app-datavisualisation-output',
template: `
`
})
export class DatavisualisationOutput {
constructor(dataservice: DataService) {
dataservice.data
.subscribe(
data => this.data = data,
console.error,
() => console.log('Look at this:' + data)
);
}
}
My second file for the service looks like this:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class DataService {
constructor(http:Http) {
this.data = http.get('http;//...API')
.map(response => response.json());
}
}
...but the console shows the following error:
components/datavisualisation/dataservices/datavisualisation.output.service.ts:12:26
Property 'data' does not exist on type 'DataService'.
components/datavisualisation/dataservices/datavisualisation.output.service.ts:14:29
Property 'data' does not exist on type 'DatavisualisationOutput'.
components/datavisualisation/dataservices/datavisualisation.output.service.ts:16:43 Cannot find name 'data'.
components/datavisualisation/dataservices/datavisualisation.service.ts:8:13
Property 'data' does not exist on type 'DataService'.
What am i doing wrong here?
You should define the dataproperty on your DatavisualisationOutput component:
export class DatavisualisationOutput {
public data: any; //this one
constructor(dataservice: DataService) {
dataservice.data
.subscribe(
data => this.data = data,
console.error,
() => console.log('Look at this:' + data)
);
}
}
and on your DataService:
#Injectable()
export class DataService {
public data: any;
constructor(http:Http) {
this.data = http.get('http;//...API')
.map(response => response.json());
}
}
and on DatavisualisationOutput... Just.. always define any property you access with this.
As #PierreDuc already said you should define that variable inside component class to make it available inside Class context.
Also you should create a method inside a service which would be responsible for data. Just make an call same method from another component and it will return the data which has retrieved last time.
Code
#Injectable()
export class DataService {
data: any;
constructor(http:Http) {
}
getData(){
if(this.data) return Observable.of(this.data)
else
return http.get('http;//...API')
.flatMap(response => {
this.data = response.json();
return Observable.of(this.data)
);
}
}
Component
export class DatavisualisationOutput {
myData: any;
constructor(private dataservice: DataService) {
dataservice.data
.subscribe(
data => this.data = data,
console.error,
() => console.log('Look at this:' + data)
);
}
ngOnInit(){
dataservice.getData().subscribe(
data => myData = data
)
}
}
Related
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import 'rxjs/add/operator/map';
#Injectable()
export class LocationsProvider {
data: any;
constructor(public http: HttpClient) {
}
load() {
if (this.data) {
return Promise.resolve(this.data);
}
return new Promise(resolve => {
this.http.get('assets/data/locations.json').subscribe(data => {
this.data = this.applyHaversine(data.locations);
this.data.sort((locationA, locationB) => {
return locationA.distance - locationB.distance;
});
resolve(this.data);
});
});
}
enter image description here
i am pretty new here, and pretty new to ionic, i'll probably requires detailed solution, i cant seems to make ionic read a json file
You are getting a compile time error in data.locations specifically locations is not defined on the data property.
Fix
Tell TypeScript that it is e.g. use an assertion:
this.data = this.applyHaversine((data as any).locations);
If you know the type of your response, you can add a generic to http.get<T>() to type data.
interface SomeInterface {
locations: Location[]
}
this.http.get('assets/data/locations.json')<SomeInterface>.subscribe(data => {
this.data = this.applyHaversine(data.locations);
...
});
or if you don't want to create an interface for it (not recommended)
this.http.get('assets/data/locations.json')<SomeInterface>.subscribe((data: any) => {
this.data = this.applyHaversine(data.locations);
...
});
For some reason the response JSON is not mapping correctly
Here is my html.
profile-search.component.html
<h3>Enter Username</h3>
<input (keyup)="search($event.target.value)" id="name" placeholder="Search"/>
<ul>
<li *ngFor="let package of packages$ | async">
<b>{{package.name}} v.{{package.repos}}</b> -
<i>{{package.stars}}</i>`enter code here`
</li>
</ul>
Here is component that the html pulls from.
profile-search.component.ts
import { Component, OnInit } from '#angular/core';
import { Observable, Subject } from 'rxjs';
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
import { NpmPackageInfo, PackageSearchService } from './profile-search.service';
#Component({
selector: 'app-package-search',
templateUrl: './profile-search.component.html',
providers: [ PackageSearchService ]
})
export class PackageSearchComponent implements OnInit {
withRefresh = false;
packages$: Observable<NpmPackageInfo[]>;
private searchText$ = new Subject<string>();
search(packageName: string) {
this.searchText$.next(packageName);
}
ngOnInit() {
this.packages$ = this.searchText$.pipe(
debounceTime(500),
distinctUntilChanged(),
switchMap(packageName =>
this.searchService.search(packageName, this.withRefresh))
);
}
constructor(private searchService: PackageSearchService) { }
toggleRefresh() { this.withRefresh = ! this.withRefresh; }
}
Service that component pulls from.
profile-search.service.ts
import { Injectable, Input } from '#angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '#angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { HttpErrorHandler, HandleError } from '../http-error-handler.service';
export interface NpmPackageInfo {
name: string;
}
export const searchUrl = 'https://api.github.com/users';
const httpOptions = {
headers: new HttpHeaders({
'x-refresh': 'true'
})
};
function createHttpOptions(packageName: string, refresh = false) {
// npm package name search api
// e.g., http://npmsearch.com/query?q=dom'
const params = new HttpParams({ fromObject: { q: packageName } });
const headerMap = refresh ? {'x-refresh': 'true'} : {};
const headers = new HttpHeaders(headerMap) ;
return { headers, params };
}
#Injectable()
export class PackageSearchService {
private handleError: HandleError;
constructor(
private http: HttpClient,
httpErrorHandler: HttpErrorHandler) {
this.handleError = httpErrorHandler.createHandleError('HeroesService');
}
search (packageName: string, refresh = false): Observable<NpmPackageInfo[]> {
// clear if no pkg name
if (!packageName.trim()) { return of([]); }
// const options = createHttpOptions(packageName, refresh);
// TODO: Add error handling
return this.http.get(`${searchUrl}/${packageName}`).pipe(
map((data: any) => {
return data.results.map(entry => ({
name: entry.any[0],
} as NpmPackageInfo )
)
}),
catchError(this.handleError('search', []))
);
}
}
I have tried to alter
return this.http.get(`${searchUrl}/${packageName}`).pipe(
map((data: any) => {
return data.results.map(entry => ({
name: entry.any[0],
} as NpmPackageInfo )
)
to
login: data.login, and login: entry.login but keep getting the below error.
http-error-handler.service.ts:33 TypeError: Cannot read property 'map'
of undefined
at MapSubscriber.project (profile-search.service.ts:49)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/map.js.MapSubscriber._next
(map.js:75)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next
(Subscriber.js:93)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/map.js.MapSubscriber._next
(map.js:81)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next
(Subscriber.js:93)
at FilterSubscriber.push../node_modules/rxjs/_esm5/internal/operators/filter.js.FilterSubscriber._next
(filter.js:85)
at FilterSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next
(Subscriber.js:93)
at MergeMapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber.notifyNext
(mergeMap.js:136)
at InnerSubscriber.push../node_modules/rxjs/_esm5/internal/InnerSubscriber.js.InnerSubscriber._next
(InnerSubscriber.js:20)
at InnerSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next
(Subscriber.js:93)
results in data.results is probably undefined, check that the data object matches the schema you're expecting it to.
map working on array but this.http.get(${searchUrl}/${packageName}) return object not array.
so data.results is undefined.
This is how I converted my object into an array, if anyone has a better way of doing please let me know.
return this.http.get(`${searchUrl}/${packageName}`).pipe(
map((data: any) => {
console.log(data);
var profile = Object.keys(data).map(function(key) {
return [(key) + ': ' + data[key]];
}
);
console.log(profile);
data = profile;
return data;
}),
catchError(this.handleError<Error>('search', new Error('OOPS')))
);
}
}
I fixed this issue by eliminating ".results"
from
.map((data: any) => this.convertData(data.results))
to
.map((data: any) => this.convertData(data))
To avoid the error, change
map((items) => items.map
to
map((items) => items?.map
Then set your result set as an empty array:
this.list = data ?? [];
PS: Used with Angular 14. In older versions you may need to change last one to data ? data : []
I want to get data from Riot API and display it in html view.
However, i can not "hold" this data in my variable. Console log show empty array.
I can see json data only in function scope.
I guess, i didn`t use observable function corretly, am i wrong?
Here is my component.
import { Component, OnInit } from '#angular/core';
import { FRIEND } from '../../services/_friends/mock-friends';
import { APIKEY } from '../../services/_lolapi/apikey';
import { Http, Response } from '#angular/http';
import { KeysPipe } from '../../pipes/key';
import { JsonPipe } from '#angular/common';
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Observable';
#Component({
selector: 'app-friends',
templateUrl: './friends.component.html',
styleUrls: ['./friends.component.css']
})
export class FriendsComponent implements OnInit {
friends = FRIEND;
apikey = APIKEY;
nick: string[];
query: string;
private apiUrl =
'https://eun1.api.riotgames.com/lol/summoner/v3/summoners/by-name/';
data: Array<string> = [];
constructor(private http: Http) {
}
getFriendData(query) {
return this.http.get(query)
.map((res: Response) => res.json());
}
getContacts() {
this.getFriendData(this.query).subscribe(data => {
this.data = data;
console.log(this.data);
});
}
ngOnInit() {
for (let i of this.friends) {
this.query = `${this.apiUrl}${i.nick}${this.apikey}`;
this.getFriendData(this.query);
this.getContacts();
console.log(i.nick);
}
}
}
You don't need this.getFriendData(this.query) in ngOnInit as in the next line you call getContacts that wraps getFriendData.
Now, your API returns SummonerDTO - a complex object and you are trying to store it as an Array? That doesn't seem right.
Additionally, it think you want to store every result in an array, right?
In that case you should rather use:
this.data.push(data);
I am trying to get some JSON data by API that I have created, but it does not receive it. I have used the following Angular code:
getBook(id: string){
return this._http.get(this.url + 'books/' + id)
.map(res => {
console.log(res.json()); //It does not show anything
return res.json();
})
However the getBooks() method has no problems getting the data. There are no errors in the browser console.
This is the whole service code:
import { Injectable } from '#angular/core';
import { Http } from "#angular/http";
import 'rxjs/add/operator/map';
import { Observable } from "rxjs/Observable";
#Injectable()
export class LibrosService {
url: string = "http://localhost/API/public/index.php/api/";
constructor(private _http: Http) { }
getBooks(){
return this._http.get(this.url + 'books')
.map(res => res.json()); //it works
}
getBook(id: string){
return this._http.get(this.url + 'books/' + id)
.map(res => {
console.log(res.json()); //it does not work
return res.json();
})
}
Sorry for my English if it is not very good and thank you for your help.
In Service
getHeroes(): Observable<Hero[]> {
return this.http.get(this.heroesUrl)
.map(this.extractData)
.catch(this.handleError);
}
In Component
getHeroes() {
this.heroService.getHeroes()
.subscribe(
heroes => this.heroes = heroes,
error => this.errorMessage = <any>error);
}
Fortunately, a friend helped me find the solution because the most frustrating thing was console did not show any errors. And the problem was not in service, it was in component.
Here is my solution:
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from "#angular/router";
import { BooksService } from "app/services/books.service";
import { Subscription } from "rxjs/Subscription";
#Component({
selector: 'app-book',
templateUrl: './book.component.html'
})
export class BookComponent implements OnInit {
public book: any =[];
private sub: Subscription;
public errorMessage: string;
constructor( private _activatedRoute: ActivatedRoute,
private _booksService: BooksService ) {}
ngOnInit() {
this.sub = this._activatedRoute.params
.subscribe(params => {
let id = +params['id'];
this.getBok(id);
});
}
getBok(id){
this._booksService.getBook(id)
.subscribe(book => {
this.book = book,
error => this.errorMessage = <any>error
});
}
}
Thanks all of you for your help.
I need to generate sanitized css property to use with my component template to set the background image of the div:
<div *ngFor="let Item of Items"
[style.background-image]="Item.imageStyle
(click)="gotoDetail(Item.iditems)">
</div>
using data obtained through a data service. The component is:
import { Component } from '#angular/core';
import { Router } from '#angular/router';
import { DomSanitizer } from '#angular/platform-browser';
import { OnInit } from '#angular/core';
import { Item } from '../models/Item';
import { CollectionDataService } from '../services/CollectionData.service';
#Component({
selector: 'mainpage',
templateUrl: 'app/mainpage/mainpage.component.html',
styleUrls: ['app/mainpage/mainpage.component.css']
})
export class MainpageComponent implements OnInit {
Items: Item[];
ngOnInit() {
this.collectionDataService.getItems().subscribe(
Items => this.Items = Items
);
// Generates and sanitizes image links
this.Items.map(
(LItem) => LItem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+LItem.iditems+".jpg)")
)
}
constructor(
private router: Router,
private sanitizer: DomSanitizer,
private collectionDataService: CollectionDataService
) {
}
gotoDetail($iditems: number): void {
this.router.navigate(['/viewer', $iditems]);
}
}
But it doesn't work because the statement that generates the sanitized property
this.Items.map(
(LItem) => LItem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+LItem.iditems+".jpg)")
)
doesn't find the loaded data. The error that I'm seeing in the browser console is:
core.umd.js:3070 EXCEPTION: Uncaught (in promise): Error: Error in ./MainpageComponent class MainpageComponent_Host - inline template:0:0 caused by: Cannot read property 'map' of undefined
TypeError: Cannot read property 'map' of undefined
The data service is:
import { Injectable } from '#angular/core'
import { Http } from '#angular/http'
import { Item } from '../models/Item';
import { DomSanitizer } from '#angular/platform-browser';
#Injectable()
export class CollectionDataService {
constructor(
private http: Http,
private sanitizer: DomSanitizer
) { }
getItems() {
return this.http.get('app/mocksdata/items.json').map(
response => <Item[]>response.json().items
)
}
}
And the provided items.json:
{
"items": [{
"iditems": 1,
"imageStyle": ""
}, {
"iditems": 2,
"imageStyle": ""
}]
}
If I set static data in the component, instead of using the data service, everything works:
export class MainpageComponent implements OnInit {
Items: Item[];
ngOnInit() {
this.Items = [{
"iditems": 1,
"imageStyle": ""
}, {
"iditems": 2,
"imageStyle": ""
}]
// Generates and sanitizes image links
this.Items.map(
(LItem) => LItem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+LItem.iditems+".jpg)")
)
}
How can I force the sanitizer statement to wait that the async data are fully loaded? Alternatively how can I generate sanitized properties directly in the service?
EDIT
The best answer comes from PatrickJane below:
Items: Item[] = [];
ngOnInit() {
this.collectionDataService.getItems().subscribe(Items => {
this.Items = Items;
this.Items.map(LItem => LItem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+LItem.iditems+".jpg)"))}
});
}
I also solved this problem working directly in the service method (credits), but it is more verbose:
return this.http.get('app/mocksdata/items.json')
.map( (responseData) => {
return responseData.json().items;
})
.map(
(iitems: Array<any>) => {
let result:Array<Item> = [];
if (iitems) {
iitems.forEach((iitem) => {
iitem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+iitem.iditems+".jpg)");
result.push(<Item>iitem);
});
}
return result;
}
)
The subscribe function is async so your map function called before the subscribe function run. So in this phase the array is undefined because you doesn't set any initial value.
The solution is to do this inside the subscribe function and to initialize the Items with empty array.
Items: Item[] = [];
ngOnInit() {
this.collectionDataService.getItems().subscribe(Items => {
this.Items = Items;
this.Items.map(LItem => LItem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+LItem.iditems+".jpg)"))}
});
}