After doing a POST to Firebase, I have this returned from Firebase
{ "name": "-KBfn7iFNIxSuCL9B-g5" }
How do I use Typescript to read the response returned from the POST request? Only the value -KBfn7iFNIxSuCL9B-g5 is need for routing. My existing incomplete code looks like this:
addHeroGotoHeroDetail() {
let heroId: string;
this._firebaseService.postHero()
.subscribe(response => {
//do something to make the heroId = -KBfn7iFNIxSuCL9B-g5
});
this._router.navigate(['hero-detail', {id: heroId}]);
}
The goal is to have a button in the homepage where user can click on it to create a new hero Id in Firebase then redirect it to the newly created hero detail page, where he can do more detail updating.
Also after doing a GET from Firebase, I have this returned from Firebase
{ "-KBfn-cw7wpfxfGqbAs8": { "created": 1456728705596, "hero": "Hero 1", "...": "..."} }
Should I create an interface for the hero JSON returned by Firebase?
If yes for question 2, how should the interface look?
export interface X {
id: string; //id defined by Firebase e.g. -KBfn-cw7wpfxfGqbAs8
hero: string; //name of the hero e.g. Hero 1
created: number; // The time when the hero was created
}
If yes for question 2, how do I use Typescript to parse the JSON object to the interface? Code sample would be very much appreciated.
The goal is to display the hero detail to the user, and allow the user to add more details to the hero such as Date of Birth, Gender and etc... and then update Firebase with these changes.
1. See below.
2. Yes, you should create an interface that matches the return type from Firebase.
3. Based on your example JSON, this is how I would structure the return type's interface:
export interface FirebaseResponse {
[id: string]: {
created: number;
hero: string;
}
}
4. To parse the response from Firebase into a strongly-typed TypeScript object, do something like this:
let firebaseResponse = <FirebaseResponse>JSON.parse(response);
You can then return only the ID, if you wish, by doing something like this:
// note: this is making some assumptions about your response type.
// You may need a more sophisticated way of returning the ID depending
// on the structure of the response - for example, if the object has
// more than one key.
return Object.keys(firebaseResponse)[0];
Related
Sorry for the question, but I'm newer in Typescript and Ionic and Im a bit confused about how should I proceed.
I have a JSON file with 150 entries based on an interface I'm declared quite simple:
export interface ReverseWords {
id: number;
todo: string;
solution: string;}
On the other hand, I have a service which reads the json file and returns an Observable of this type (ReverseWords)
getReverseWords() {
return this.http.get<ReverseWords>('/assets/data/reves.json');}
On .ts file, I call the service and I have all the content of the JSON file. What I want to do (and Im not able to do) is get only one entry based on a random position.
On .ts file:
reverseWords: Observable<ReverseWords>; // All the JSON content
reverseWordsSelected: Observable<ReverseWords>; // I would like to get one entry here
On ngOnInit():
this.reverseWords = this.dataservice.getReverseWords();
Everything is fine until here, I've got all the content and I can log it in console. I'm using Observables so I need to subscribe to it to get the information. And I use rxjs/operators pipe and filter to try it, but nothing is showing in the chrome developer console (not even an error).
const reverseWordSelectedByPosition = this.reverseWords.pipe(filter(reverseWord => reverseWord.id === randomPosition));
reverseWordSelectedByPosition.subscribe(console.log);
Could anybody help me and tell me what I'm doing wrong?
Other thing I've tested is to do the following in the service:
getReverseWords() {
return this.http.get<ReverseWords[]>('/assets/data/reves.json');}
And then in the .ts file:
reverseWords: Observable<ReverseWords[]>;
But I have the same problem.
Finally, the most weird thing is that if I write in the .ts file this simple test:
const test = from([
{
id: 1,
todo: 'chapter',
solution: 'r-e-t-p-a-h-c'
},
{
id: 2,
todo: 'claustrofobia',
solution: 'a-i-b-o-f-o-r-t-s-u-a-l-c'
},
{
id: 3,
todo: 'keyboard',
solution: 'd-r-a-o-b-y-e-k'
}
]);
Everything is fine and I can see on the log only 1 entry if I choose 2, for example.
Any help or advice??
Thanks and sorry for the long approach!!
As TotallyNewb suggested, I show an example of the JSON file, with only 3 entries:
[
{
"id": 1,
"todo": "chapter",
"solution": "r-e-t-p-a-h-c"
},
{
"id": 2,
"todo": "claustrofobia",
"solution": "a-i-b-o-f-o-r-t-s-u-a-l-c"
},
{
"id": 3,
"todo": "keyboard",
"solution": "d-r-a-o-b-y-e-k"
}
]
Since you are getting the whole array you can use map
this.reverseWords.pipe(
map((items) => items[Math.floor(Math.random() * items.length)]), // Creates a random index based on the array length
)
.subscribe(console.info);
If you want to pass the result to reverseWordsSelected, you can change it to a Subject and pass the value to it from the subscription of reverseWords with .next().
You can check out this stackblitz for a working example
I am using a Service in angular to retrieve JSON responses from a path as given below.
const request = this.http.get<ShoppingCartItem>('assets/json/shopping-cart-test.json');
The above JSON file in the assets folder has an array of JSON objects. I want to convert the response to an array of ShoppingCartItem objects. Following given is the ShoppingCartItem class that I want to map. How to achieve this?
export class ShoppingCartItem {
$key: string;
title: string;
imageUrl: string;
price: number;
quantity: number;
constructor(init?: Partial<ShoppingCartItem>) {
Object.assign(this, init);
}
get totalPrice() { return this.price * this.quantity; }
}
I am not familiar with handling the HTTP response with methods like subscribe, pipe and map. A detailed explanation would be more helpful.
You already had the fundamentals right.
Inside an Angular.component ts file,
We first create a request with final type you expect from the request i.e - ShoppingCartItem similar to what you have done in your code
const shoppingCartItem$: Observable<ShoppingCartItem> = http.get<ShoppingCartItem>('/assets/db.json');
Notice the $sign on shoppingCartItem$, usually used with an Observable, the return type when you make HTTP Requests in Angular.
The subscribing part is done after declaring our Observable in this case. Subscribe method is used to make HTTP Requests, without it no requests are made to the server.
Pipe and map are RXJS operators that allows us to make changes to response. It basically gives you access to data in your observable so you can make changes. They are usually used in conjunction.
In your case, it could be used like
this.shoppingCartItem$:.pipe(
map((items: shoppingCartItem[]) => items.map(items=> items.key))
)
Working Stackblitz is below.
Local JSON subscription example
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. :)
I do not know how to manipulate a formanray
I have with the select input:
example stackblitz
I want the output json like this:
json
{
"comentario": "kkkkkkkkkkkk",
"respuestas": [ { "idusuario": "", "numero": "","iduser":"" } ]
}
I get iduser from localstorage with that I have no problem
I found the how to do it
Update I make this little example but there a bug with select
https://stackblitz.com/edit/angular-kj2ahg?file=src%2Fapp%2Fapp.component.html
Your form module (what you get from Formulario.value) does not need to match your data model.
Consider modeling your data as you need it coming from or getting saved to your data store. So in your example, with your iduser.
Then just map the data from your form data structure (Formulario.value) to your data structure.
Make sense?
For example, my data structure I want to send to my server could look like this:
export interface Product {
id: number;
productName: string;
productCode: string;
iduser: number;
}
I can copy the data from the form to this data structure like this:
const product = { ...this.myForm.value };
Then product contains all of the form fields. But, like in your example, the iduser is not on the form. So I can then use:
product.iduser = myUsersId;
To set the id. I then have a data structure I can pass to my backend.
So I am coming from a background of C# where I can do things in a dynamic and reflective way and I am trying to apply that to a TypeScript class I am working on writing.
Some background, I am converting an application to a web app and the backend developer doesn't want to change the backend at all to accommodate Json very well. So he is going to be sending me back Json that looks like so:
{
Columns: [
{
"ColumnName": "ClientPK",
"Label": "Client",
"DataType": "int",
"Length": 0,
"AllowNull": true,
"Format": "",
"IsReadOnly": true,
"IsDateOnly": null
}
],
Rows:[
0
]
}
I am looking to write an Angular class that extends Response that will have a special method called JsonMinimal which will understand this data and return an object for me.
import { Response } from "#angular/http";
export class ServerSource
{
SourceName: string;
MoreItems: boolean;
Error: string;
ExtendedProperties: ExtendedProperty[];
Columns: Column[];
}
export class ServerSourceResponse extends Response
{
JsonMinimal() : any
{
return null; //Something that will be a blank any type that when returned I can perform `object as FinalObject` syntax
}
}
I know StackOverflow isn't for asking for complete solutions to problems so I am only asking what is one example taking this example data and creating a dynamic response that TypeScript isn't going to yell at me for. I don't know what to do here, this developer has thousands of server-side methods and all of them return strings, in the form of a JSON or XML output. I am basically looking for a way to take his column data and combine it with the proper row data and then have a bigger object that holds a bunch of these combined object.
A usage case here after that data has been mapped to a basic object would be something like this.
Example:
var data = result.JsonMinimal() as LoginResponse; <-- Which will map to this object correctly if all the data is there in a base object.
var pk = data.ClientPK.Value;
I'm not exactly sure I understand, but you may want to try a simple approach first. Angular's http get method returns an observable that can automatically map the response to an object or an array of objects. It is also powerful enough to perform some custom mapping/transformation. You may want to look at that first.
Here is an example:
getProducts(): Observable<IProduct[]> {
return this._http.get(this._productUrl)
.map((response: Response) => <IProduct[]> response.json())
.do(data => console.log('All: ' + JSON.stringify(data)))
.catch(this.handleError);
}
Here I'm mapping a json response to an array of Product objects I've defined with an IProduct interface. Since this is just a "lambda" type function, I could add any amount of code here to transform data.