I made some autocomplete feature, and it required some array method. I want to use array json as my API instead of object json.
Since filter method only works for array. I have to use array json[] as my API url, but my API is an object json{} file. How to make it as an array?
I tried with some array json API. The code works with array json, but it doesn't work with object json.
HTML:
<mat-autocomplete #auto="matAutocomplete" [displayWith]="getOptionText">
<mat-option *ngFor="let option of (filteredOptions | async)" [value]="option">
{{ option.make }}
</mat-option>
</mat-autocomplete>
Service ts:
#Injectable({
providedIn: 'root'
})
export class Service {
constructor(private http: HttpClient) { }
opts = [];
getData() {
return this.opts.length ?
of(this.opts) :
this.http.get<any>('https://api.myjson.com/bins/15psn9').pipe(map(data => this.opts = data));
}
}
Component ts:
constructor(private service: Service) { }
ngOnInit() {
this.filteredOptions = this.searchForm.controls['customerId'].valueChanges.pipe(
startWith(''),
debounceTime(100),
switchMap(value => value.length >= 3 ? this.doFilter(value) : [])
);
}
doFilter(value) {
return this.service.getData()
.pipe(
map(response => response.filter((option: { make: { toLowerCase: () => { indexOf: (arg0: any) => number; }; }; }) => {
return option.make.toLowerCase().indexOf(value.toLowerCase()) === 0;
}))
);
}
getOptionText(option) {
return option.make;
}
I expected API JSON is array or autocomplete feature works.
In your Service try using: from instead of. Check this docs.
Here is a working stackblitz based on your code.
Related
I'm trying to print a JSON response that I get from a RESTful API request like that:
products:Observable<any>;
constructor(public navCtrl: NavController, private backgroundGeolocation: BackgroundGeolocation, public zone: NgZone, private auth: AuthService, public httpClient: HttpClient)
{
this.products = this.httpClient.get('http://127.0.0.1:8000/product');
}
It works fine, indeed if I print result in console:
this.products
.subscribe(data => {
console.log('my data: ', data);
});
the data is right.
But now, I don't know how to print them out onto a HTML page. I've tried this but it doesn't work:
<ion-list>
<ion-item *ngFor="let p of (products | async)?.results">{{ p.productName}}
</ion-item>
</ion-list>
Are there other ways to resolve the problem?
My JSON response is like that:
0: Object { idProduct: "1", productName: "Pasta", purchased: "0" }
1: Object { idProduct: "2", productName: "latte", purchased: "0" }
I have resolved the trouble. I want to post the solution to help other users in this bad situation.
Solution is so simple. I created a new typescript file called: 'rest-service' made up by:
#Injectable()
export class RestServiceProvider {
constructor(public http: HttpClient) {
console.log('Hello RestServiceProvider Provider');
}
getUsers() {
return new Promise(resolve => {
this.http.get('http://127.0.0.1:8000/product').subscribe(data => {
resolve(data);
}, err => {
console.log(err);
});
});
}
}
Now, in home.ts I've done that:
getUsers() {
this.restProvider.getUsers()
.then(data => {
this.products = data;
console.log(this.products);
});
}
And then, in the constructor, that:
this.getUsers();
In HTML side instead, the solution is very very simple:
<ion-item *ngFor="let p of products"> {{ p.productName }}
However, thanks to all
please try to convert products to array object. That may fix your problem.
this.products = this.products.json();
UPDATE
Looks like you found the solution. Normally I don't prefer doing *ngFor directly at a function
<!-- DON'T DO THIS -->
<ion-item *ngFor="let item of someFunction()">
but rather define a variable above the constructor, and then assign data. A separate file wouldn't be necessary, but can be useful if you are doing the same request over and over.
TypeScript
items: Array<any> = [];
constructor() {
// ...
}
ionViewDidLoad() {
this.http.get('https://someurl.com/data').subscribe((data: any) => {
this.items = data;
}).catch(err => console.error('Something went wrong: ', err));
}
HTML
<ion-item *ngFor="let item of items">
{{ item.name }}
</ion-item>
Old answer
What happens if you just do following?
<ion-item *ngFor="let p of products">
Any reason why you are trying to access .results on the products-array?
If you get any errors in the console, please share them with us.
I want to place a marker on Google Maps using Angular Google Maps my coordinates are from JSON file which when parses in Angular it's become a string.
Angular Google Maps does not support string for coordinates number. So It needs to be number.
marker.json
[
{
"id":"1",
"lat":"13.751814",
"lon":"100.501060"
},
{
"id":"2",
"lat":"13.738445",
"lon":"100.516805"
},
{
"id":"3",
"lat":"13.730209",
"lon":"100.779991"
}
]
maps.componenet.ts
import { Component, OnInit } from '#angular/core';
import {Http, Response} from '#angular/http';
#Component({
selector: 'app-maps',
templateUrl: './maps.component.html',
styleUrls: ['./maps.component.css']
})
export class MapsComponent implements OnInit {
private data;
constructor(private http:Http){
}
ngOnInit(){
this.getData();
}
getData(){
this.http.get('/localhost/marker.json')
.subscribe(res => this.data = res.json());
}
}
maps.component.html
<agm-map [latitude]="13.359665" [longitude]="101.035913" [zoom]="6">
<ng-container *ngFor="let list of data">
<agm-marker [latitude]="list.lat" [longitude]="list.lon"></agm-marker>
</ng-container>
</agm-map>
I have tried using parseFloat like this (Yes it's not working)
<agm-marker [latitude]="parseFloat(list.lat)" [longitude]="parseFloat(list.lon)"></agm-marker>
I'm thinking of using parseInt inside maps.component.ts file but I'm not sure where to put this.
May someone helps me or guide me how to solve this.And please let me know that am I providing sufficient information or not.
Thanks!
A cleaner and more performant way is to process data once and provide it in view in a way that doesn't need any other preparations:
getData(){
this.http.get('/localhost/marker.json')
.map(res => res.json())
.map((data: any[]) => data.map(
({ lon, lat, ...props }) => ({ lon: +lon, lat: +lat, ...props })
))
.subscribe(data => {
this.data = data;
});
}
If marker.json doesn't have other props except listed ones, ...props can be replaced with known id property.
Global functions like parseFloat are not allowed in Angular templates (see the Angular documentation).
You can convert strings to numbers with the + operator:
<agm-marker [latitude]="+list.lat" [longitude]="+list.lon"></agm-marker>
or define a method in your component:
convertCoordinate(str: string): number {
return parseFloat(str);
}
and call it in the template:
<agm-marker [latitude]="convertCoordinate(list.lat)" [longitude]="convertCoordinate(list.lon)"></agm-marker>
+lat won't work in Angular template, it's invalid syntax and will throw an error, as well as parseFloat or parseInt, in here you can parse the JSON in the request like #estus pointed out OR you can multiply the string by 1:
<agm-marker [latitude]="list.lat * 1" [longitude]="list.lon * 1"></agm-marker>
This will transform the string into a number but beware that if it's NOT a number there will be error and you won't be able to catch it.
I have developed angular2 application using ngrx/effects for making http calls. I have used GIT as reference application. Once the response come from http, i am not able to display it on screen. Its showing [object Object]. Here is my code.
HTML page linked to component.html
<div class="container">
<div class="left-container cf">
<mat-tab-group>
<mat-tab label="Configuration">{{jsons}}</mat-tab>
<mat-tab label="Captured Output">
</mat-tab>
</mat-tab-group>
</div>
</div>
Component.ts
export class ExperimentDetailsComponent implements OnInit {
jsons: Observable<any>;
isLoading: Observable<any>;
constructor(
private store: Store<fromStore.State>
) {
this.isLoading = store.select(fromStore.getIsLoading);
this.jsons = store.select(fromStore.getJson);
console.log(this.jsons)
}
ngOnInit() {
this.store.dispatch(new jsonAction.GetJson());
// this.jsons = this.store.select(fromStore.getJson);
}
}
Effects.ts
export class GetJsonEffects {
#Effect() json$ = this.actions$.ofType(Act.GET_JSON)
.map(toPayload)
.withLatestFrom(this.store$)
.mergeMap(([ payload, store ]) => {
return this.http$
.get(`http://localhost:4000/data/`)
.map(data => {
return new Act.GetJsonSuccess({ data: data })
})
.catch((error) => {
return Observable.of(
new Act.GetJsonFailed({ error: error })
);
})
});
constructor(
private actions$: Actions,
private http$: HttpClient,
private store$: Store<fromStore.State>
) {}
}
As you see, the result of store.select() is an observable. You cannot data bind to it directly.
You can either:
Use the async pipe to make the UI subscribe to the observable for you and extract the data, like:
<mat-tab label="Configuration">{{jsons | async}}</mat-tab>
Or subscribe yourself to the observable.
export class ExperimentDetailsComponent implements OnInit {
jsonSubscription = store.select(fromStore.getJson)
.subscribe(jsons => this.jsons = jsons);
ngOnDestroy() {
this.jsonSubscription.unsubscribe();
}
jsons: any;
// ...
}
That's one thing:
If you are using Http service (from #angular/http module):
The other thing is that you are returning the Response object not the JSON extracted from it. The map() in your effect needs to call data.json(). Like:
return this.http$
.get(`http://localhost:4000/data/`)
.map(data => {
return new Act.GetJsonSuccess({ data: data.json() })
})
Or, as I like, add another map() to make things clear:
return this.http$
.get(`http://localhost:4000/data/`)
// You could also create an interface and do:
// `response.json() as MyInterfaceName`
// to get intellisense, error checking, etc
.map(response => response.json())
.map(data => {
return new Act.GetJsonSuccess({ data: data })
})
If you are using HttpClient service (from #angular/common/http module):
(Available in Angular v4.3+)
In this case you don't need to call .json() yourself, it does it for you, so you don't need that first .map() I suggested.
You can also tell TypeScript about the type you expect the JSON to match by calling the get() like this:
return this.http$
.get<MyInterfaceName>(`http://localhost:4000/data/`)
.map(data => {
return new Act.GetJsonSuccess({ data: data.json() })
})
The get<MyInterfaceName>() bit will make Angular tell TypeScript that the JSON object matches the MyInterfaceName, so you'll get intellisense and error checking based on this (at compile time only, none of this affects runtime in anyway).
HttpClient Documentation
I have a component that populates an Object array in its ngInit() method from a service which I then use the contents of in my HTML template.
My problem is I can use this data fine in the HTML template but if I try to use this same Object array in my TypeScript file I will get an undefined error.
Below is a simplified code example of my problem:
#Component({
selector: 'booking',
template: `
<div *ngFor="let r of requestedBookings">
<label>Requested on {{r.created | date: 'd MMM H:mm'}}</label>
</div>
`
})
export default class BookingComponent {
requestedBookings: Object[];
constructor(private bookingService: BookingService) {
}
ngOnInit() {
this.getRequestLog();
// Cannot read property 'length' of undefined error
// console.log(this.requestedBookings.length);
}
private getRequestLog(): void {
this.bookingService.getRoomRequestBooking(1,1,1)
.subscribe(data => this.requestedBookings = (data as any))
.results, err => {
console.log(err);
}
}
Why is it in the above example I can use the requestedBookings array as expected in the HTML template but inside the TypeScript file I receive undefined errors?
IMHO the correct way should be something like:
ngOnInit() {
this.getRequestLog();
}
private getRequestLog(): void {
this.bookingService.getRoomRequestBooking(1,1,1)
.subscribe((data)=>{
this.requestedBookings = data;
console.log(this.requestedBookings.length);
})
.results, err => {
console.log(err);
}
}
As explained before, the call to getRoomRequestBooking is async, so you should not expect it will finish before calling the console.log. Instead, you should use the requestedBookings.length value in a place where you do know it will exist. Hope it helps!!
I fixed this issue by using this constructor from the subscribe method. the complete parameter event happens after successful completion.
subscribe(next?: (value: T) => void,
error?: (error: any) => void,
complete?: () => void): Subscription;
Code is as follows:
ngOnInit() {
this.getRequestLog();
}
private getRequestLog() {
this.bookingService.getRoomRequestBooking(this.date, this.level, this.room)
.subscribe(
data => this.requestedBookings = (data as any).results,
err => {
console.log(err);
},
() => console.log(this.requestedBookings.length));
}
I have the following code (based on the Angular2 Hero example) and I am trying to get a JSON API call (AWS) converted into a TypeScript class.
The code returns no errors, and I can see that the object is there when I look at response.json() but the class product remains undefined, any ideas?
Code in the service file...
getProduct(id: number): Promise<Product> {
const url = `${this.ProductsUrl}/${id}`;
console.log(url);
return this.http.get(url)
.toPromise()
.then(response => response.json().data as Product)
.catch(this.handleError);
}
Code in the component file...
ngOnInit(): void {
this.route.params
.switchMap((params: Params) => this.productsService.getProduct(+params['id']))
.subscribe(product => this.product = product);
}
The class...
export class Product {
Instock: boolean;
Description: string;
CategoryId: number;
Id: number;
ColourOptions: string[];
Name: string;
}
The JSON returned from the API call...
{
"Instock":true,
"Description":"This is a test description.",
"CategoryId":1,"
Id":1,
"ColourOptions":["Red","Gold","Green"],
"Name":"Test"
}
switchMap'callback must return an Observable not a Promise, so edit getProduct to return a Observable instead:
getProduct(id: number): Observable<Product> {
const url = `${this.ProductsUrl}/${id}`;
console.log(url);
return this.http.get(url)
.map(response => response.json().data as Product)
.catch(this.handleError);
}