Angular 8: Stripe elements nested inside Nebular Components - html

Problem: I'm running into an issue where I am unable to tokenize the credit card information posted inside of the card stripe element I am mounting to my component.
I am currently using the Nebular stepper component (https://akveo.github.io/nebular/docs/components/stepper/overview#nbsteppercomponent) and have a nested child component with the following ts code:
public async fetchCardInput(): Promise<any> {
let address: Address = this.ccAddress.fetchAddressData();
let name = this.getCardholderName();
let line2 = address.getAddressLine2();
let cardTokenPayload = {
name: name,
address_line1: address.getAddressLine1(),
address_city: address.getCity(),
address_state: address.getState(),
address_zip: address.getZipCode(),
country: this.constants.US,
currency: this.constants.USD
};
if (line2) {
cardTokenPayload['address_line2'] = line2;
}
const { token, error } = await this.stripe.createToken(this.card, cardTokenPayload);
if (error) {
console.log('Something went wrong: ', error);
return null;
} else {
console.log('SUCCESS! Token: ', token);
let data = {
tokenId: token.id,
address: address,
cardholderName: name
}
return data;
}
}
html (pulled out the html and ts from the child component but it's still behaving the same way):
<nb-step label="Payment / Payout">
<div class="heading">{{ constants.PAYMENT_INFORMATION }}</div>
<label class="input-label">{{ constants.CREDIT_CARD_INFORMATION }}</label>
<div class="card-container">
<div id="card-element" #cardElement></div>
</div>
<div *ngIf="error" class="error mt-10">{{ error }}</div>
</nb-step>
The thing is, this implementation works completely fine when it's not nested inside of the Nebular component. Has anyone found a work-around for this issue or at least familiar with Nebular components and what they're doing under the hood?
EDIT: Adding the browser exception:
ERROR Error: Uncaught (in promise): IntegrationError: We could not retrieve data from the specified Element.
Please make sure the Element you are attempting to use is still mounted.
I am using Angular 8 and Nebular 4.5.0.
Thanks in advance!

Related

Angular Get method returns an Undefined instead of object

I am an Angular beginner and I've made a service and made all the set up so that the Get method works on a JSON file and it worked! but the problem is whenever I want to access to the data inside the JSON file it tells me that it is undefined, and I want to use it as an object in the ts file.
It works when using data biding like this {{stats | JSON}}, but I couldn't use it in the TS. can I convert this 'undefined' to a simple object?
stats: any;
constructor(private statsData: StatDataService) {
this.statsData.GetEmbedded().subscribe((data: any) => {
this.stats = data;
});
}
`
for example I can't use stats.preOrders[0]
To better understand the situation, let's create an example.
example.component.ts
interface Statistics {
median: number;
average: number;
}
export class Example {
// stats is undefined until GetEmbedded() fires
stats?: Statistics;
constructor(private statsData: StatDataService) {
this.statsData.GetEmbedded().subscribe((stats: Statistics) => {
this.stats = stats;
});
}
}
example.component.html
<div>The median is: {{ stats.median }}</div>
Why will this example result in an error? Your variable stats initially is undefined, so it will not have any properties. GetEmbedded() will take some milliseconds to fire but in the same time, the template already tries to access stats.median.
One option to solve this problem would be to check if the variable stats is not undefined and if it is not, access the properties you need.
example.component.html
<div *ngIf="stats">The median is: {{ stats.median }}</div>

Grafana JSON Explorer API

I woulk like to construct a panel in Grafana (following this tutorial: https://grafana.com/tutorials/build-a-panel-plugin/) and use this JsonExplorer class (https://grafana.com/docs/grafana/latest/packages_api/ui/jsonexplorer/#constructor-jsonopenconfigkey) for displaying and navigating through a JSON tree.
This class has a render method (which returns a HTMLDivElement) but when I use it as show in the sample code below, it throws the following error:
Type 'HTMLDivElement' is not assignable to type 'ReactNode'.
Type 'HTMLDivElement' is missing the following properties from type 'ReactPortal': key, type, props
Extract of my sample code is:
export const SimplePanel: React.FC<Props> = ({ options, data, width, height }) => {
const traces = '[{"firstName": "John","lastName": "Doe"},{"firstName": "Mark","lastName": "Spree"}]';
const jsonTraces = JSON.parse(traces);
const jsonExplorer = new JsonExplorer(jsonTraces, 0);
const jsonHTML = jsonExplorer.render();
return (
<div>{jsonHTML}</div>
);
};
Does anyone have some sample code that shows the usage of this class for creating a custom panel? Many thanks - Christian

Parsing JSON to display the data in angular

So I have my data stored in database and I'm extracting in angular and I'm getting the data the problem is when I'm trying to display the data I'm getting the following error. I just need to display the question in html using ngFor* .I'm completely new to angular any guidance will be very helpful .
this.selfHelpService.getFgSelfHelpByName("BYOD").subscribe(data => {
// console.log(data);
const BYOD = JSON.parse(data["data"]);
// console.log(BYOD );
});
<div class="cards" *ngFor="let BYOD of BYOD">
<div class="read-1">
{{BYOD.posts.question}}
</div>
</div>
since you are getting the data, you can do the following:
// If this is your response:
{"BYOD":[ ` { "posts": [ {"Question":"BYOD (Android)","answer":"???"}, {"Question":"BYOD (IOS)","answer":"?????"} ] } ] };
this.selfHelpService.getFgSelfHelpByName("BYOD").subscribe(data => {
try {
const BYOD = JSON.parse(JSON.stringify(data["data"]));
} catch(err) {
console.log('Try catch error ==>', err);
}
// Declare a variable in the component.ts above constructor called byodPostsData
// Assign the values to that variable
this.byodPostsData = BYOD[0].posts;
});
// In your HTML now, simply loop through the this.byodPostsData;
<div class="cards" *ngFor="let eachPost of byodPostsData">
<div class="read-1" *ngIf="eachPost && eachPost.question">
{{ eachPost.question }}
</div>
</div>
As mentioned earlier by other commentators here: Am not sure why you are using JSON.parse(). But if you should use it for sure, then try using this:
// Generally its considered good practice to use try-catch for JSON.parse() for data
try {
const BYOD = JSON.parse(JSON.stringify(data["data"]));
} catch(err) {
console.log('Try catch error ==>', err);
}
I am not sure what you are trying to loop, but, if you want to loop over byod,
<div class="cards" *ngFor="let byod of byod.BYOD[0].posts">
<div class="read-1">
{{byod.Question}}
</div>
</div>
Besides in your example code, byod is locally scope in the function, you should make a component variable from that to make it accessable by the HTML.
Feel free to ask if something is unclear.
As you are new to Angular: this is how decided to access the response:
The 0 should come from the fact that the response is a JSON object with a JSON key BYOD which has an array as value: so the BYOD[0] indicates that you are trying to access the first item of the array from BYOD, which is another object with the JSON key "posts", the value of "posts" is an array you want to loop over. If you parse the data from the request to a component property called byot, it should be accessable in the html the following: byod.BYOD[0].posts I hope I made it a bit more clear.
Here is a stackblitz!
https://stackblitz.com/edit/angular-ivy-jcxgfo?file=src/app/app.component.ts

Best approach to deal with JSON responses in the Front End/Angular when subscribing it as a forkJoin?

I have read multiple answers to these kind of issues, and each answer has its own response;
In my case I am not getting any of those as my interfaces simply don't map the json like I want it to. I have tried multiple solutions, since working with Root-object and nested interfaces, but here I am, asking which is the best approach to deal with these kind of JSON objects in the front end, how to map it this particular one (a fork-Join). and I wanted to ask what are the real benefits of using the interfaces/classes/ maps besides the Intellisense? It has to do with data propagation?
The json structure in question:
{
Title: "",
Year: "",
Rated: "",
Released: "",
Runtime: "", 
…}
Simple as it is. But back in my service I call it with a forkjoin:
getMovies(name: string, year?: string): Observable<any> {
let shortPlot = this.http.get(
"https://www.omdbapi.com/?t=" +
name +
"&plot=short&y=" +
year +
"&apikey=[my key]"
);
let fullPlot = this.http.get(
"https://www.omdbapi.com/?t=" + name + "&plot=full&apikey=[my key]"
);
return forkJoin([shortPlot, fullPlot]);
}
The subscription in the component:
getMovie() {
this.spinner = true;
this.movieService
.getMovies(this.name.value)
.subscribe((dataList: any) => {
this.movies = Array.of(dataList[0]);
this.spinner = false;
let error: any = this.movies.map(error => error.Error);
if (error[0]) {
this.notfound = error[0];
this.error = true;
} else {
this.error = false;
this.movieRate = this.movies.map(rating => rating.imdbRating.toString());
}
})),
error => console.log(error);
}
And in the HTML I render the data like this:
<div *ngFor="let m of movies">
<h5 class="mt-0">{{m.Title}}, {{m.Year}}</h5>
</div>
So as you can see I am not working with an interface and I should. Anyone can sort me out?
Thank you
EDIT: the log after subscribe:
let's break it down,
what are the real benefits of using the interfaces/classes/ maps besides the Intellisense?
Using interfaces and classes will not just give you intellisense but will also provide static type safety for your code. Why this is important, let's say you have a interface with following structure,
export interface Demo {
field: string;
}
// in some other file 1
demo.field.substring(1, 2);
// in some other file 2
demo.field.lenght;
You are using this interface in many places in your code. Now, for some reason you get to know that the property should be number not string. So here typescript will give you all the errors at compile time only.
export interface Demo {
field: number;
}
// in some other file 1
demo.field.substring(1, 2); // error
// in some other file 2
demo.field.lenght // error
Also, after typescript transpiles it will generate javascript files, now as javascript is interpreted language, your code will not be tested until the javascript run-time actually executes the problematic line, but in typescript you will get errors in compilation stage only.
You can get away with using any everywhere, but with that you will be missing the static typings.
With interfaces and classes, you also get OOP features, such as inheritance etc.
It has to do with data propagation?
Your frond-end is never aware what type of data will be received from api. So it's developers responsibility that the received data should be mapped to some interface.
Again as mentioned above, if somehow back-end changes type of some field in received json, then it will again be caught in compile time.
In case of forkJoin which combines output of two jsons you can have two different types.
Demo
export interface Demo1 {
field1: string;
}
export interface Demo2 {
field2: number;
}
// in service layer
getData(): Observable<[Demo1, Demo2]> {
const res1 = this.http.get(...);
const res2 = this.http.get(...);
return forkJoin([res1, res2]);
}
// in component
this.dataService.getData().subscribe(res => {
// you will get type safety and intellisense for res here
console.log(res[0].field1)
})
I am not working with an interface and I should.
Yes, you should use interfaces, if you are not using using features of typescript then whats the point using it. :)

How to render angular variables using innerHTML - Angular2

In my application their is some text which is coming from a constant file which is declared like this:
export const EmpStrings = {
data: "Welcome {{employee.name}}"
}
And In my component file there is an object called employee.
public employee = { name: 'xyz', dept: 'EE' }
Now In my HTML I want to use it like this:
<div class='e-data' [innerHTML] = "EmpStrings.data"></div>
But this didn't seems to be working.
I tried various variations:
[inner-html] = "EmpStrings.data"
[innerHTML] = {{EmpStrings.data}}
[innerHTML] = "{{EmpStrings.data}}"
But none of them seems to be working.
If you don't want to use JitCompiler then you can parse string yourself
component.ts
ngOnInit() {
this.html = EmpStrings.data.replace(/{{([^}}]+)?}}/g, ($1, $2) =>
$2.split('.').reduce((p, c) => p ? p[c] : '', this));
}
template
<div [innerHTML]="html"></div>
Plunker Example
use ${employee.name} to bind angular variable to innerHTML
"data": `Welcome ${employee.name}`
Angular doesn't interpolate strings like this, as far as I know one of the reason is security. The const doesn't have the context where your employee is in hence you see the error saying employee is not defined.
You could do this:
Change your constant:
export const EmpStrings = {data: "Welcome "};
then:
<div class='e-data'>{{EmpStrings.data}}{{employee.name}}</div>
But if for some reason you must use [innerHTML]:
In your component class, add a method:
getWelcomeMessage(){return EmpStrings.data + this.employee.name;}
in the component view:
<div class='e-data' [innerHTML] = "getWelcomeMessage()"></div>