How to display data from client service in ngx-wheel Angular - javascript

I want to display the ngx-wheel using api but I'm having trouble displaying the data.
Here my Service :
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class RestServices {
restEndpoint:string = 'https://gorest.co.in/public/v2/users'
constructor(
private httpClient: HttpClient
) { }
async getServiceId() {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
})
}
return this.httpClient.get<any[]>(this.restEndpoint, httpOptions)
}
Here my Component :
private subscription: Subscription | undefined;
items: any = []
ngOnInit(): void {
this.subscription = this._restService.getServices()
.subscribe((res:any)=>{
let item = res
this.items = item.map((v:any) => ({
text: v.name,
id: v.id,
textFillStyle: "white",
textFontSize: "16"
}));
})
}
ngOnDestroy(): void {
this.subscription?.unsubscribe()
}
Here for html
<ngx-wheel #wheel [width]='350' [height]='350' [spinDuration]='8' [disableSpinOnClick]='true' [items]='items'
[innerRadius]='10' [spinAmount]='10' [textOrientation]='textOrientation' [textAlignment]='textAlignment'
pointerStrokeColor='black' pointerFillColor='white' [idToLandOn]='idToLandOn' (onSpinStart)='before()'
(onSpinComplete)='after()'>
I hope to find the answer here. Thank you

First, you don't need await, async and ,toPromise()... remove them and simply return
return this.httpClient.get<any[]>(this.restEndpoint, httpOptions);
inside your component you should use your constructor only for simple data initialization: if you have to consume a rest api it is a better approach to move that piece of code inside the ngOnInit method:
items: any[] = []
constructor(private restService: RestService){}//dependency injection
ngOnInit(): void {
this.restService.getServiceId().subscribe(response => {
console.log('response success: ', response);
this.items = response; //this may change a little based on your api
console.log('items: ', this.items);
}, errorLog => {
console.log('response error: ', errorLog)
});
}
The above solution is valid, you can enrich it by adding a *ngIf="isLoaded" on your html element and set to true the isLoaded INSIDE subscribe method. but if you prefer you can do the following in the component.ts
items$: Observable<any> = EMPTY;
constructor(private restService: RestService){}//dependency injection
ngOnInit(): void {
this.items$ = this.restService.getServiceId();
}
then, in your html it would change to the following:
<ngx-wheel #wheel *ngIf="items$ | async as items" [width]='350' [height]='350' [spinDuration]='8' [disableSpinOnClick]='true' [items]='items'
[innerRadius]='10' [spinAmount]='10' [textOrientation]='textOrientation' [textAlignment]='textAlignment'
pointerStrokeColor='black' pointerFillColor='white' [idToLandOn]='idToLandOn' (onSpinStart)='before()'
(onSpinComplete)='after()'>

Related

Chaining HTTP call in Angular

I'm trying to get Google Places with their API and get a response body that contains a "photo_reference" property. With this property, I want to get the Google Place Image and call a second API.
I have an input field that can search for places. By entering a string and clicking on the search button, this method will get called:
#Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.css']
})
export class SearchComponent implements OnInit {
private lng: number;
private lat: number;
private place_id: string;
private listOfPoi: GoogleLocation[];
constructor(
private gs: GoogleService,
private ds: DataService,
private router: Router
) { }
ngOnInit(): void {
this.ds.getLocation().subscribe(loc => this.listOfPoi = loc);
}
searchPoi(location: string): void {
this.router.navigate(['/poi']);
this.gs.getPoi(location).pipe(
map((response) => response)
).subscribe((data: GoogleResponse) => {
if(data.status === "OK") {
this.ds.setLocation(data.results); //data.results is an array of locations
}
});
}
In my other component, I try to get the locations and map them into a new object with the property imgUrl.
I want to save them in an array.
export class SightseeingViewComponent implements OnInit {
listOfPoi: LocationWithImage[];
constructor(private ds: DataService, private gs: GoogleService) {}
ngOnInit(): void {
this.ds.getLocation().subscribe(
(loc) =>
(this.listOfPoi = loc.map((loc) => {
return {
...loc,
imgUrl:
this.gs.getImage(loc.photos[0].photo_reference).toString(),
};
}))
);
}
}
But my problem is the img src="[object Object]" it should be the string!
My problem is this line this.gs.getImage(loc.photos[0].photo_reference).toString()
I tried to replace this line with a random hardcoded img Url and it works. The image are shown but I cannot retrieve the URL as string with this method.
Of course toString() is not working but I have no clue what else I can do?
This is the google location model:
//The model of a google location object from the API response (getLocationInfo)
export class GoogleResponse {
constructor(
public results: GoogleLocation[],
public status: string
) {}
}
export class GoogleLocation {
constructor(
public formatted_address: string,
public geometry: Locations,
public icon: string,
public name: string,
public photos: PhotoInfo[],
public place_id: string,
public reference: string,
public type: string[]
) {}
}
export interface LocationWithImage extends GoogleLocation {
imgUrl?: string;
}
...
EDIT:
This is my service:
With this, I make a call to my backend which is fetching the photo_reference from Google via its API
#Injectable({
providedIn: 'root',
})
export class GoogleService {
url: string = 'http://localhost:3000/';
constructor(private http: HttpClient) {}
getLocationInfo(location: string): Observable<GoogleResponse> {
console.log(location);
const headers = new HttpHeaders({
'Content-Type': 'application/json',
});
return this.http.post<GoogleResponse>(
this.url + 'googleplace/',
{ name: location },
{ headers: headers }
);
}
getPoi(location: string): Observable<GoogleResponse> {
console.log(location);
const headers = new HttpHeaders({
'Content-Type': 'application/json',
});
return this.http.post<GoogleResponse>(
this.url + 'googlepoi/',
{ name: location },
{ headers: headers }
);
}
getImage(photoRef: string): Observable<string> {
console.log(photoRef);
const headers = new HttpHeaders({
'Content-Type': 'application/json',
});
return this.http.post<string>(
this.url + 'googlephoto/',
{ photoRef: photoRef },
{ headers: headers }
);
}
}
Here is my other service which is needed to send and retrieve data from other components
#Injectable({
providedIn: 'root'
})
export class DataService {
private googleLocationSource = new Subject<GoogleLocation[]>();
constructor() { }
public getLocation(): Observable<GoogleLocation[]> {
return this.googleLocationSource.asObservable();
}
public setLocation(gl: GoogleLocation[]) {
return this.googleLocationSource.next(gl);
}
My problem is this line this.gs.getImage(loc.photos[0].photo_reference).toString()
Yes, calling toString() on an observable isn't going to work ! :-)
You need to subscribe to that observable to receive its result. There are a few "Higher Order Mapping Operators" that can do this for you, so you don't have to deal with nested subscriptions. In this case, we can use switchMap.
However, it's a little more complex because you want to make a call for each element in the returned array. We can map each location to an observable call to get the image and use forkJoin to create a single observable that emits an array containing the results of the individual observables:
this.ds.getLocation().pipe(
switchMap(locations => forkJoin(
locations.map(loc => this.gs.getImage(loc.photos[0].photo_reference))
)
.pipe(
map(imgUrls => imgUrls.map(
(imgUrl, i) => ({ ...locations[i], imgUrl })
))
))
)
.subscribe(
locations => this.listOfPoi = locations
);
The flow here is:
switchMap receives the locations and subscribes to observable created by forkJoin
forkJoin creates an observable that will emit an array of the results (image urls) from all the individual getImage() calls.
map receives the array of image urls and maps it to an array of locations that include the image url
subscribe simply receives the final array

Angular PayPal Processing, Childcomponent and Lifecycle

I am currently working on the PayPal integration in my own shop (Angular Frontend, dotnet core WebApi backend) and apparently I have a problem with the life cycle between the main component (CheckoutProcessComponent) and the child component (PaymentPayPalComponent) and with the use of the necessary PayPal javascript.
Basically the complete PayPal workflow works as long as I use static variables. And this is exactly where my problem starts.
CheckoutProcessComponent.ts
import { Component, OnInit } from '#angular/core';
import { CartService } from 'src/app/_services/shop/cart.service';
import { ISubscriptionCollection } from 'src/app/_models/misc/ISubscriptionCollection';
import { serialUnsubscriber } from 'src/app/shared/utility.module';
import { CheckoutService } from 'src/app/_services/checkout/checkout.service';
import { ICheckoutOrderData } from 'src/app/_models/shop/ICheckoutOrderData';
import { IOrderProcessSummary } from 'src/app/_models/shop/IOrderProcessSummary';
#Component({
selector: 'app-checkout-process',
templateUrl: './checkout-process.component.html',
styleUrls: ['./checkout-process.component.css']
})
export class CheckoutProcessComponent implements OnInit {
subscriptions: ISubscriptionCollection = {};
checkoutOrderData = {} as ICheckoutOrderData;
orderSummaryFromServer: IOrderProcessSummary;
orderCreated: boolean = false;
constructor(public checkoutService: CheckoutService, public cartService: CartService) { }
ngOnInit() {
this.checkoutOrderData.Order = this.checkoutService.orderData;
this.checkoutOrderData.Cart = this.cartService.shoppingCart;
// Create Order
this.subscriptions['createOrder'] = this.checkoutService.createOrder(this.checkoutOrderData)
.subscribe((res: IOrderProcessSummary) => {
this.checkoutService.checkoutSummary = res;
this.orderSummaryFromServer = res;
console.log('Order created: ' + res);
}, error => {
console.log('Error');
});
}
ngOnDestroy(): void {
serialUnsubscriber(...Object.values(this.subscriptions));
}
nextStep(step: number) {
this.checkoutService.updateCheckoutStep(step);
}
...
}
CheckoutProcessComponent.html
<app-payment-paypal [orderSummary]="orderSummaryFromServer" *ngIf="checkoutService.orderData.paymentMethod === 'PM_PAYPAL'"></app-payment-paypal>
<app-payment-stripe *ngIf="checkoutService.orderData.paymentMethod === 'PM_STRIPE'"></app-payment-stripe>
<app-payment-moneytransfer *ngIf="checkoutService.orderData.paymentMethod === 'PM_TRANSFER'"></app-payment-moneytransfer>
PaymentPayPalComponent.ts
import { Component, OnInit, AfterViewChecked, Input } from '#angular/core';
import { environment } from 'src/environments/environment';
import { IOrderProcessSummary } from 'src/app/_models/shop/IOrderProcessSummary';
import { CheckoutService } from 'src/app/_services/checkout/checkout.service';
declare let paypal: any;
#Component({
selector: 'app-payment-paypal',
templateUrl: './payment-paypal.component.html',
styleUrls: ['./payment-paypal.component.css']
})
export class PaymentPaypalComponent implements OnInit, AfterViewChecked {
#Input() orderSummary: IOrderProcessSummary;
paypalClientId = environment.paymentPayPal.clientId;
addScript: boolean = false;
scriptTagElement: HTMLScriptElement;
constructor(private checkoutService: CheckoutService) { }
ngOnInit() {
}
ngOnDestroy(): void {
if (this.scriptTagElement) {
document.body.removeChild(this.scriptTagElement);
}
}
ngAfterViewChecked(): void {
if (!this.addScript) {
this.addPayPalScript().then(() => {
paypal.Buttons({
createOrder: function() {
return fetch('https://localhost:5021/api/payments/paypal-create-order', {
method: 'post',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
orderId: 1234
})
}).then(function(res) {
return res.json();
}).then(function(data) {
return data.id; // Use the same key name for order ID on the client and server
});
},
onApprove: function(data, actions) {
console.log('onApprove - transaction was approved, but not authorized', data, actions);
actions.order.get().then(details => {
console.log('onApprove - you can get full order details inside onApprove: ', details);
});
return fetch('https://localhost:5021/api/payments/paypal-capture-order', {
method: 'post',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
token: data.orderID,
payerID: data.payerID,
})
});
},
onClientAuthorization: (data) => {
console.log('onClientAuthorization - you should probably inform your server about completed transaction at this point', data);
// this.showSuccess = true;
},
onCancel: (data, actions) => {
console.log('OnCancel', data, actions);
// this.showCancel = true;
},
onError: err => {
console.log('OnError', err);
// this.showError = true;
}
}).render('#paypal-button-container');
})
}
}
addPayPalScript() {
return new Promise((resolve, reject) => {
this.scriptTagElement = document.createElement('script');
this.scriptTagElement.src = 'https://www.paypal.com/sdk/js?currency=EUR&client-id=' + this.paypalClientId;
this.scriptTagElement.onload = resolve;
document.body.appendChild(this.scriptTagElement);
this.addScript = true;
})
}
}
PaymentPayPalComponent.html
<div class="text-center" id="paypal-button-container"></div>
The processing flow is as follows:
CheckoutProcessComponent > creates the order on the server with the cart data and stores the order in the database (general function, non PayPal related)
The result from 1. is of IOrderProcessSummary which holds the order Id and the total of the order
Depending on the selected payment method, the corresponding payment-Child Component will be displayed. In this case the PaymentPayPalComponent.
Then I use the PayPal-feature "CreatePayPalOrderOnServer" which uses the PayPal Api in my backend to create an order on the PayPal server. But for that I need the specific orderId because I need to fill the PayPal-Order data with the specific order data from my system (in the example I have a fixed 1234 order Id).
For step 4. I tried it with Input()-parameter and with injecting the CheckoutService to the PaymentPayPalComponent but I am not able to work with any kind of dynamic variables within the PayPal-Button-Function (JavaScript). In the example I need to change the "orderId: 1234" with a value from the Input() orderSummary or the same value from the CheckoutService, but both of them stay undefined.
The PayPal-Javascript is loaded correctly and the PayPal-Buttons show up and work as intended technically.
How do I bring my dynamic data into the PaymentPayPalComponent and into the PayPal.Buttons-Function?
Thanks for your tips!

Pass value to extends plugin ng2 smart table

I've checked the document and source code for pagination implementation (advanced-example-server.component.ts).
And found that the ServerDataSource it used had only implemented pagination via HTTP GET (_sort, _limit, _page, etc parameters expose in URL)..... as my current project worked on required to use POST to send front-end parameters to back-end Restful APIs,
using extends to HTTP post call implement, I don't know how to add the extra parameters in pagination request. I Need To pass the request_server to extendsplugin.ts
import { Observable } from 'rxjs/internal/Observable';
import { ServerDataSource } from 'ng2-smart-table';
export class PostServerDataSource extends ServerDataSource {
protected requestElements(): Observable<any> {
let httpParams = this.createRequesParams();
return this.http.post(this.conf.endPoint, request_server, { observe: 'response' });
}
}
anotherComponent.ts
swiftListTable() {
const request_server = { "userType": this.currentUser.role, "authName": this.currentUser.username }
this.source = new PostServerDataSource(this.http,{endPoint: this.service.apiURL + 'swift/pagination', dataKey: 'content', pagerLimitKey:"_limit",
pagerPageKey:"_page",
sortDirKey: "pageable",
sortFieldKey: "pageable",
totalKey:'totalElements'});
}
There are two ways you can handle it,
one way is attaching the params in query string and append to url like,
this.service.apiURL + 'swift/pagination?param1=p&param2=q'
Other way could be handling it in requestElements and swiftListTable functions like below.
swiftListTable() {
const request_server = {
"userType": this.currentUser.role,
"authName": this.currentUser.username
}
this.source = new PostServerDataSource(http,
{ endPoint: url, dataKey: 'content', pagerLimitKey:'_limit'}, request_server);
export class PostServerDataSource extends ServerDataSource {
params: any;
constructor(http: HttpClient, config: any, params?: any) {
super(http, config);
this.params = params;
}
protected requestElements(): Observable<any> {
let httpParams = this.createRequesParams();
if (this.params) {
let keys = Object.keys(this.params);
keys.forEach((key) => {
httpParams = httpParams.set(key, this.params[key]);
});
}
return this.http.post(this.conf.endPoint, httpParams, { observe: 'response' });
}
}

Why i get error with Interfaces and Observable?

I build ToDoApp and connect firebase to my project but i have error.
ERROR in src/app/todo-component/todo-component.component.ts(25,7): error TS2740: Type 'DocumentChangeAction<{}>[]' is missing the following properties from type 'Observable<ToDoInterface[]>': _isScalar, source, operator, lift, and 5 more.
Here my ToDOInterface:
export interface ToDoInterface {
id?: string;
title?: string;
completed?: boolean;
}
My ToDoService:
import { Injectable } from '#angular/core';
import {ToDoInterface} from './ToDoInterface'
import {AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument} from "#angular/fire/firestore";
import {Observable} from "rxjs";
#Injectable({
providedIn: 'root'
})
export class ToDoService {
public toDoArray:ToDoInterface[] = [
{
id: "sdfsdf",
title: 'Todo one',
completed: false
},
{
id: "13dsfsdf",
title: 'Todo two',
completed: false
},
{
id: "safsdf",
title: 'Todo third',
completed: false
}
];
ToDoCollection: AngularFirestoreCollection<ToDoInterface>;
ToDo: Observable<ToDoInterface[]>;
ToDoCollectionName: string = "ToDo";
constructor(private firestore: AngularFirestore) {
}
getToDos(){
return this.toDoArray;
}
getToDosFirebase(){
return this.firestore.collection(this.ToDoCollectionName).snapshotChanges();
}
changeToDos(index,property,value){
this.toDoArray[index][property] = value;
}
deleteToDos(index){
this.toDoArray.splice(index,1);
}
deleteToDosFirebase(index){
// this.firestore.collection("ToDO").doc(index).delete();
}
addToDos(obj: ToDoInterface){
this.toDoArray.push(obj);
}
addToDosFirebase(obj: ToDoInterface){
return new Promise<any>(((resolve, reject) => {
this.firestore
.collection("ToDo")
.add(obj)
.then(res => console.log('data send!'), err => reject(err))
}))
}
}
And my function what i call in ngOnInit
getToDo(){
this._toDoService.getToDosFirebase().subscribe(items => {
this.toDosFirebase = items;
console.log(items);
});
Maybe i dont know some about rxjs but here data from that func if toDosFirebase: Observable<any[]>; and how i can see it normal for my Interface
In service i hardcode data and all works fine, and my hardcoded data by types equal data from firebase.
Do like in official documentation:
private ToDoCollection: AngularFirestoreCollection<ToDoInterface>;
ToDo: Observable<ToDoIdInterface[]>;
ToDoCollectionName: string = "ToDo";
constructor(private readonly firestore: AngularFirestore) {
this.ToDoCollection = firestore.collection<ToDoInterface>(this.ToDoCollectionName);
this.ToDo = this.ToDoCollection.snapshotChanges().pipe(
map(a => {
const data = a.payload.doc.data() as ToDoInterface; //ERROR
const id = a.payload.doc.id;
return {id, ...data}
}
));
console.log(this.ToDo);
}
In your "getToDosFirebase" method you are calling a ".snapshotChanges();" method.
Actions returned by snapshotChanges are of type DocumentChangeAction
and contain a type and a payload. The type is either added, modified
or removed and the payload contains the document's id, metadata and
data.
In other words you need to map the received data something like this:
.snapshotChanges()
.map(actions => {
return actions.map(a => {
//Get document data
const data = a.payload.doc.data() as Task;
//Get document id
const id = a.payload.doc.id;
//Use spread operator to add the id to the document data
return { id, …data };
});
});
For more information see these links: link1, link2
update
In your "to-do-service.service.ts"
change this line:
ToDo: Observable<ToDoIdInterface[]>;
to this:
ToDo: Observable<any>;
also change this:
map(a => {
to this:
map((a: any) => {
After this the project should build correctly.

Merge objects in Angular 2 [duplicate]

This question already has answers here:
How to return value from an asynchronous callback function? [duplicate]
(3 answers)
Closed 6 years ago.
Im having trouble merging 3 objects array. My objective is to merge 3 arrays to one and send HTTP POST to the server.
I have tried using concat but Im getting this error:
EXCEPTION: Error in ./PreviewEmailPage class PreviewEmailPage_Host - inline template:0:0 caused by: Cannot read property 'concat' of undefined
These are my codes:
import { Component } from '#angular/core';
import { NavParams, NavController, LoadingController } from 'ionic-angular';
import { Validators, FormGroup, FormControl } from '#angular/forms';
import { Http, Headers} from '#angular/http';
import 'rxjs/add/operator/map';
import { Storage } from '#ionic/storage';
#Component({
selector: 'preview-email-page',
templateUrl: 'preview-email.html',
})
export class PreviewEmailPage {
headers: Headers;
loading: any;
url: string;
preview: any[];
FirstArray: any[];
SecondArray: any[];
ThirdArray: any[];
PostData: any[];
constructor(
public nav: NavController,
public navParams: NavParams,
public loadingCtrl: LoadingController,
public localStorage: Storage,
public http: Http,
) {
this.localStorage.get('FirstArray').then((value) => {
this.FirstArray= value;
})
this.localStorage.get('SecondArray').then((value) => {
this.SecondArray= value;
})
this.localStorage.get('ThirdArray').then((value) => {
this.ThirdArray= value;
})
this.PostData = this.FirstArray.concat(this.SecondArray);
this.PostData = this.PostData.concat(this.ThirdArray);
this.loading = this.loadingCtrl.create();
}
ionViewWillEnter(){
this.headers = new Headers();
this.headers.append("Content-Type", "application/x-www-form-urlencoded");
console.log(this.PostData);
this.getPreview();
}
getPreview(){
this.loading.present();
this.url = 'https://domain.com/REST/getpreview.php';
this.http.post(this.url, this.PostData, {headers: this.headers}).map(res => res.json()).subscribe(res => {
console.log(res);
this.preview = res;
}, err => {
console.log('error');
})
}
}
Since this.localStorage.get is an async operation your this.FirstArray is not defined untill the then is executed.
What you can do is run them all in parallel with Promise.all:
Promise.all([this.localStorage.get('FirstArray'), this.localStorage.get('SecondArray'), this.localStorage.get('ThirdArray')]).then(values => {
this.FirstArray= values[0];
this.SecondArray= values[1];
this.ThirdArray= values[2];
this.PostData = this.FirstArray.concat(this.SecondArray);
this.PostData = this.PostData.concat(this.ThirdArray);
});

Categories