Trying to get some position of JSON using Observables - json

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

Related

angular getting Observable<HttpEvent<any[]>> inside my html from ts file

I have started working on angular again after almost 2 years and i thought i could grasp from my previous knowledge but i am kind of stuck over here and cannot really understand what i can do.
I am using efc for getting data and in services i have the following method
shared-service.ts
getHotelDetails(val:any){
return this.http.get<any>(this.APIUrl+'Hotels/GetHotel/',val);
}
In ts i am getting the correct data by using the below code
mycomponent.ts
hotel$!:Observable<HttpEvent<any[]>>;
constructor(private service:SharedService, private _route:ActivatedRoute) {
}
ngOnInit(): void {
var Id = this._route.snapshot.params['id'];
this.hotel$ = this.service.getHotelDetails(Id);
console.log(this.hotel$);
}
mycomponent.html
I am trying to use the hotel object in html but cannot iterate or find the attributes inside. The api call is giving a json like the below for api:
https://localhost:44372/api/Hotels/GetHotel/1
{
"hotelId": 1,
"name": "pizza",
"addresses": [],
"comments": [],
"hotelFoods": []
}
I just need the code in which i can fetch the name from hotel$ attribute.
I tried ngfor and directly accessing hotel$.id
What you get back from the service is an Observable, you need to subscribe to it in order to access the data. The easiest way to do this in a html template is to use the async pipe.
You need something like this:
{{ (this.hotel$ | async).hotelId }}

NextJs Webpack asset/source returns JSON as a string

Looking for some help to understand what is going on here.
The Problem
We are using a translation service that requires creating JSON resource files of copy, and within these resource files, we need to add some specific keys that the service understands so it knows what should and should not be translated.
To do this as simple as possible I want to import JSON files into my code without them being tree shaken and minified. I just need the plain JSON file included in my bundle as a JSON object.
The Solution - or so I thought
The developers at the translation service have instructed me to create a webpack rule with a type of assets/source to prevent tree shaking and modification.
This almost works but the strange thing is that the JSON gets added to the bundle as a string like so
module.exports = "{\n \"sl_translate\": \"sl_all\",\n \"title\": \"Page Title\",\n \"subtitle\": \"Page Subtitle\"\n}\n";
This of course means that when I try and reference the JSON values in my JSX it fails.
Test Repo
https://github.com/lukehillonline/nextjs-json-demo
NextJs 12
Webpack 5
SSR
Steps To Reproduce
Download the test repo and install packages
Run yarn build and wait for it to complete
Open /.next/server/pages/index.js to see the SSR page
On line 62 you'll find the JSON object as a string
Open .next/static/chunks/pages/index-{HASH}.js to see the Client Side page
If you format the code you'll find the JSON object as a string on line 39
Help!
If anyone can help me understand what is going wrong or how I can improve the webpack rule to return a JSON object rather than a string that would be a massive help.
Cheers!
The Code
next.config.js
module.exports = {
trailingSlash: true,
productionBrowserSourceMaps: true,
webpack: function (config) {
config.module.rules.push({
test: /\.content.json$/,
type: "asset/source",
});
return config;
},
};
Title.content.json
{
"sl_translate": "sl_all",
"title": "Page Title",
"subtitle": "Page Subtitle"
}
Title.jsx
import content from "./Title.content.json";
export function Title() {
return <h1>{content.title}</h1>;
}
pages/index.js
import { Title } from "../components/Title/Title";
function Home({ dummytext }) {
return (
<div>
<Title />
<p>{dummytext}</p>
</div>
);
}
export const getServerSideProps = async () => {
const dummytext = "So we can activate SSR";
return {
props: {
dummytext,
},
};
};
export default Home;

How do I loop through a single JSON file for each object to appear on Ionic tinder cards?

So I'm following this tutorial on how to build tinder cards for Ionic 2. The tutorial makes use of randomuser.me's API, but I'd like to use my own JSON file.
Below is my typescript file (though I have omitted some snippets of code that are irrelevant), and the bottom-most function is what I have changed to try retrieving my own JSON data, but it is not working properly :( I think there is something wrong with how I'm trying to loop through the array in my JSON file?
export class HomePage {
#ViewChild('myswing1') swingStack: SwingStackComponent;
#ViewChildren('mycards1') swingCards: QueryList<SwingCardComponent>;
cards: Array<any>;
stackConfig: StackConfig;
recentCard: string = '';
constructor(public http: Http) {
this.stackConfig = {
throwOutConfidence: (offsetX, offsetY, element) => {
return Math.min(Math.abs(offsetX)/(element.offsetWidth/2),1);
},
transform: (element, x, y, r) => {
this.onItemMove(element, x, y, r);
},
throwOutDistance: (d) => {
return 800;
}
};
}
ngAfterViewInit(){
this.swingStack.throwin.subscribe((event: DragEvent) => {
event.target.style.background = '#ffffff';
});
this.cards = [{email: ''}];
this.addNewCards(1);
}
voteUp(like: boolean) {
let removedCard = this.cards.pop();
this.addNewCards(1);
if (like){
this.recentCard = 'You liked: ' + removedCard.email;
} else{
this.recentCard = 'You disliked: ' + removedCard.email;
}
}
addNewCards(count: number){
this.http.get('../../assets/data/pics.json').map(data => data.json().results).subscribe(result => {
for (let val of result){
for (var count = count; count<pic.length; count++){
this.cards.push(val);
}
}
})
}
I have also tried making individual JSON files for the objects and retrieve it this way, but have also failed. I tried console logging the JSON data to see what it retrieves, but is always only the second object. No card appears in the stack after that.
The output for both methods I did were just a single card and an "undefined" string appearing below the card after swiping it.
I'm new to using Ionic and Typescript, by the way (and I'm sort of rushing), so any help or suggestions would be appreciated!
(also here is what my JSON file looks like, if it helps:)
{
"pics": [
{
"name": "balls",
"pic": "assets/img/pics/01.jpg"
},
{
"name": "snowy field",
"pic": "assets/img/pics/02.jpg"
},
{
"name": "hotel bedroom",
"pic": "assets/img/pics/03.png"
},
{
"name": "apartments",
"pic": "assets/img/pics/04.jpg"
}
]
}
You should make sure to understand what a piece of code is actually doing, before you make the required changes to it.
Here is the code you are using to add new cards:
addNewCards(count: number){
this.http.get('https://randomuser.me/api/?results=' + count).map(data => data.json().results).subscribe(result => {
for (let val of result){
for (var count = count; count<pic.length; count++){
this.cards.push(val);
}
}
})
}
Looking at this code, there are a few things that stand out, which we'll break down into steps:
If you want to read your own JSON file, why are you making the request to the randomuser.me API (this.http.get('https://randomuser.me/api/?results' + count)? Here you should enter the path to your own JSON file (this.http.get('../../assets/data/pics.json').
Next, look at what the code does with the results (.map(data => data.json().results)). If we take a look at the JSON that is returned from the API (https://randomuser.me/api/?results=1) we see that the actual results are in the "results" section of the response, so .map(data => data.json().results) is actually saying: take what was returned, convert it to JSON, and give me the results. Since your JSON file does not have such a "results" section, it doesn't make sense to ask for that part of the file. You could change it to .map(data => data.json().pics) to get a list of all the pics.
Now I'm assuming you want to add "count" new cards, so if count is 3 you want to add 3 new cards. First note that this works for the API, because it can create infinite results, but if you want to add 10 cards from your own JSON, you will have to make sure that there actually are at least 10 entries in your JSON.
Let's look at the actual body of the function, step by step: for (let val of result){ loops through every entry returned by the request, so for you, it would loop through all pics. Inside this loop, you are then creating a new loop, which has a few different issues on its own (var "count" has the same name as parameter "count", "pic" is not defined anywhere), but even if the loop was correct, you still loop through all results, so you're not filtering out an amount of results. If you make sure that the length of your list of pics is at least the same amount as count, you could do something more like this:
for (let i = 0; i < count; i++) {
this.cards.push(result[i[);
}
So in the end that would leave us with:
addNewCards(count: number) {
this.http.get('../../assets/data/pics.json')
.map(data => data.json().pics)
.subscribe(result => {
for (let i = 0; i < count; i++) {
this.cards.push(result[i]);
}
})
}
Good luck, and the main lesson here is: understand the code you're using before trying to change it.
Take a look at my ionic-soundboard project as well to see how I use my own JSON files to populate a list of entries.
In addition to Rosco's answer above that fixes my loop, Disqus user JB has provided a good solution to making each object from the JSON file appear on each card.
The card-stack div and ion-card in the HTML file should be appended like so (the zIndex and marginTop properties were modified at the end):
<div swing-stack #myswing1 [stackConfig]="stackConfig" (throwoutleft)="voteUp(true)" (throwoutright)="voteUp(false)" id="card-stack" [style.zIndex]="-1000">
<ion-card #mycards1 swing-card *ngFor="let c of cards; trackBy:trackByCards; let i=index;" [style.zIndex]="-1 * i" [style.marginTop]="i === 0 ? '0px' : '12px'">
And this for the Typescript file, in the voteUp() function:
let removedCard = this.cards.shift();
The difference is the use of shift() (instead of pop()) as this removes the first element from the array in the JSON file. And that's it. Thank you Rosco and JB!!
Note: once you've gone through all the elements in your array it returns to the first element again; it's a endless loop of cards

TypeScript / Angular 2 creating a dynamic object deserializer

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.

Reading JSON using Typescript in Angular2

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];