angular *ngFor from json - javascript

I am new to angular. Let me first give an overview of what i am trying to achieve because this is a long code I am showing the relevant part.
I have a list display component.
And I have (lets say 2) components animals, zone.
Lets say :
zone has 2 columns zone name and zone code,
animals has 3 columns animal code, animal name, animal zone
and so on (lets say for 10 other components)
each component will generate JSON and send it to display list component.
display list will parse the JSON and display it with ngFor
in short :
each component will make JSON and send it to service , which has behavior subject
service has behavior subject, that will receive that JSON
display component will get the latest json from service's behavior subject
finally display component will parse json and will display them using ngfor
My generating and sending JSON to display list component is ok.
For example, I will show you the JSON of zone component that is send to display component.
I need your help to process the JSON so that I can display it using ngFor on display component html.
Code:
data.service.ts:
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class DataService {
private messageSource = new BehaviorSubject(null);
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: any) {
console.log('changed');
console.log(message);
this.messageSource.next(message);
}
}
for zone (zone.component.ts) : [car.component.ts is as same as zone.component.ts don't get confused]
import { Component, OnInit } from '#angular/core';
import { DataService } from "../../services/data.service";
import { Router } from '#angular/router';
#Component({
selector: 'app-cars',
templateUrl: './cars.component.html',
styleUrls: ['./cars.component.css']
})
export class CarsComponent implements OnInit {
jsonData: any;
data: any;
constructor(private router: Router, private dataService: DataService) {
}
dd() {
this.setData(this.jsonData);
}
ngOnInit(): void {
this.setJsonData();
}
async getJsonData() {
const myurl: string = "http://localhost:3000/zone/get/all";
const response = await fetch(myurl, { headers: { 'Content-Type': 'application/json' } });
return await response.json();
}
async setJsonData() {
this.jsonData = await this.getJsonData();
}
setData(newJsonData: any) {
this.data = Object.entries(newJsonData);
}
navigateToDisplayList(){
this.router.navigateByUrl('display-list');
}
newMessage() {
this.dataService.changeMessage(this.jsonData);
// console.log(this.jsonData);
// console.log(this.data);
this.navigateToDisplayList();
}
}
for display : display-list.component.ts :
import { Component, OnInit } from '#angular/core';
import { DataService } from "../../services/data.service";
#Component({
selector: 'app-display-list',
templateUrl: './display-list.component.html',
styleUrls: ['./display-list.component.css']
})
export class DisplayListComponent implements OnInit {
data: any;
constructor(private dataService: DataService) { }
ngOnInit(): void {
this.dataService.currentMessage.subscribe(message => this.data = message);
}
dd(){
console.log(this.data);
document.body.innerText = this.data.toString();
}
}
A special note :
Please don't think I haven't researched it yet just because I am not posting the display-list.html
LOOK MY ALGORITHM IS SIMPLE :
OUTER LOOP FOR EACH ROW
INNER LOOP FOR EACH COLUMN
THAT IS IT. I DON'T NEED ANYTHING ELSE IN HTML
I have tried this approach :
<tr *ngFor="let x of data">
<td *ngFor="let y of x">
{{y}}
</td>
</tr>
Each time I am getting error: ngFor is not a known property
(which is funny: If I just comment the ngfor error is gone
or
If I just ngfor on a static array like 1,2,3,4,5 no error there )
Some other time : data can not be iterated
(another funny thing: clearly my JSON can be iterated and no quotation or bracket is missing)
I simply don't get it why angular can't iterate this thing
JSON for zone list :
[
{
"zonecode":3,
"zonename":"d"
},
{
"zonecode":4,
"zonename":"d"
},
{
"zonecode":15,
"zonename":"kk"
}
]
Another very special note :
You don't need to post an answer or comment if you are suggesting capture the JSON in a variable then just loop on console.log(object.zonename) .
Because I have no control over JSON, I have lets say 30 other components where no zonename is there. I have to display in HTML directly from JSON

updating my answer based on comments....I understand that you want to access the key value pairs inside the object and this can be done as below
<tr *ngFor="let x of data">
<td *ngFor="let y of x | keyvalue">
{{y.key}}:{{y.value}}
</td>
</tr>

Related

How can I make the product I`m clicking on to get deleted?

I need some help with this product getting deleted, can`t quite figure this out.
I know what has to be done on the back-end, I need some help with Angular to make this button work only with the product it has been clicked on
Thanks
This is how I intend to delete it on the back end service :
async function deleteProduct(_id){
return Product.findByIdAndDelete(_id)
}
Nothing on the controller yet :
productController.get(`/profile/delete`, async(req,res) => {
})
This is angular component :
import { Component, OnInit } from '#angular/core';
import { IProduct } from 'src/app/interfaces/products';
import { IUser } from 'src/app/interfaces/user';
import { ProfileService } from './profile.service';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
user: IUser|undefined
products: IProduct[] | any
isEmpty : boolean = false
constructor(private profileService: ProfileService){
}
ngOnInit(): void {
this.user = undefined
this.products = undefined
this.profileService.getUserDetails().subscribe({
next: (user) => {
this.user = user
this.products = user.products
if(this.products.length == 0){
this.isEmpty = true
}
}
})
}
deleteProduct(){}
}
As you can see most of it is empty, because I dont have any ideas. I dont want to load the page in detailed view or anything. I`d like to have this button working on this page
I assume you have in the template an ngFor (because on the image you have multiple products) and I assume product is an item of the products array. If so, well, you are almost there. You have just to pass the id parameter to the deleteProduct method like this:
deleteProduct(id: number)
{
// call the delete method
}
And don't forget to remove this product from the products array (or reload the array from the backend).

Can't get deeper into the response data object in subscribe's callback function. Why?

I'm fetching data from RandomUser api with Angular HttpClient. I've created a method in a service calling GET, mapping and returning a Observable. Then I subscribe on this method in a component importing this service and in subscribe's callback I am trying to store the response data in a local variable. The problem is I can't get "deeper" into this response object than:
this.randomUser.getNew().subscribe(data => {
this.userData = data[0];
})
If I'm trying to reach any further element of that response object, and log it to console it I get "undefined". To be precise I cant reference to, for example:
this.randomUser.getNew().subscribe(data => {
this.userData = data[0].name.first;
})
If I store the "data[0]" in a variable first I can get into these unreachable properties. What is the reason of it? Please, help. Let me know what important piece of fundamental JS (or Angular) knowledge I'm not aware of. As far as I know I should be able to do what I am trying to do :)
service looks like these
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { map } from 'rxjs/operators';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class RandomUserService {
url: string = " https://randomuser.me/api/ "
constructor(private http: HttpClient) { }
public getNew(): Observable<any> {
return this.http.get(this.url)
.pipe(map(responseData => {
const returnDataArray = [];
for (const key in responseData) {
returnDataArray.push(responseData[key])
}
return returnDataArray;
}))
}
}
component looks like these:
import { Component, OnInit } from '#angular/core';
import { RandomUserService } from 'src/app/shared/random-user.service';
import { Observable } from 'rxjs';
#Component({
selector: 'app-single-character',
templateUrl: './single-character.component.html',
styleUrls: ['./single-character.component.scss']
})
export class SingleCharacterComponent implements OnInit {
userData: object;
fname: string;
constructor(private randomUser: RandomUserService) {
this.randomUser.getNew().subscribe(data => {
this.userData = data[0];
})
}
ngOnInit(): void {
}
}
You are not parsing the returned data correctly in getNew().
The returned data looks like this:
So you need to access the user data like:
this.randomUser.getNew().subscribe(data => {
this.userData = data[0][0]; // note 2nd [0]
})
or for first name:
this.randomUser.getNew().subscribe(data => {
this.userData = data[0][0].name.first;
})
See stackblitz here: https://stackblitz.com/edit/so-http-parse?file=src/app/app.component.ts

Angular: How to get value from one component's frontend (app.compont.html) to another component's backend (other.component.ts)

Consider a simple crud scenario. I have a lot of input fields and buttons in app.component.html. When i press a button from app.component.html, it will send html field value to 'other.component.ts' component and will display the result back in app.component.html after processing (like add, subtract or other).
Here is app.component.html
<a routerLink="posts/">Show Posts</a>
<input type="number" [(ngModel)]="get-one-post-id">
<a routerLink="/post-by-id">Show One Posts</a>
<router-outlet>
</router-outlet>
post-by-id-component.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../data.service';
import { Observable } from 'rxjs';
#Component({
selector: 'app-post-by-id',
templateUrl: './post-by-id.component.html',
styleUrls: ['./post-by-id.component.css']
})
export class PostByIdComponent implements OnInit {
posts: object;
constructor(private dataService: DataService) { }
ngOnInit(): void {
// const id = ??
this.GetPost(1);
}
async GetPost(id: number)
{
const response = await this.dataService.Get_A_Post(id);
const dataService = await response.json();
this.posts = dataService;
}
}
post-by-id-component.html
<div *ngFor="let post of posts">
<h3>{{post.title}}</h3>
<p>{{post.body}}</p>
</div>
I just want to get value from the field called get-one-post-id from app.component.html to post-by-id-component.ts [where I commented // const id = ??]. But i can't find a way to import it.
To share Data between Angular Components exists 4 different ways:
Parent to Child: Sharing Data via Input
Child to Parent: Sharing Data via ViewChild
Child to Parent: Sharing Data via Output() and EventEmitter
Unrelated Components: Sharing Data with a Service
You can read this useful article to see how it works.

Format httpclient response for *ngFor?

Hi I was wondering if anyone could help me solve a small problem.
I am received data from my rest api which is returned as an array with objects inside.
Once I get it to my service I try to transform the data and push it to a subject so that it can inform my component that the data is here or updated.
When i console.log the data I get
0:{code: "AUH", name: "Abu Dhabi"}
1:{code: "ALY", name: "Alexandria"}
2:{code: "LTS", name: "Altus"}
3:{code: "ANK", name: "Ankara"}
4:{code: "AIY", name: "Atlantic City"}
5:{code: "BAK", name: "Baku"}
6:{code: "BKK", name: "Bangkok"}
7:{code: "EAP", name: "Basel"}
8:{code: "BJS", name: "Beijing"}
So when I try and use my *ngFor I get [object]p[Object]
How can I format this to work with *ngFor?
city-list.component.html
import { CityService } from "./services/city-list.service";
import { Component, OnInit, OnDestroy } from "#angular/core";
import { City } from "../cities/models/city";
import { Subscription } from "rxjs";
#Component({
selector: "<app-cities></app-cities>",
templateUrl: "./city-list.component.html"
})
export class CityListComponent implements OnInit, OnDestroy {
cities: City[];
private citiesSub: Subscription; // so as to unsubscribe if page changes/ memory leak
constructor(public cityService: CityService) {}
ngOnInit() {
this.cityService.getCities();
this.citiesSub = this.cityService
.getCityUpdateListener()
.subscribe((cities) => {
this.cities = cities;
});
// 1st value: when data emit 2nd value: error emit, 3rd value function for when no more data is available
}
ngOnDestroy() {
this.citiesSub.unsubscribe();
}
}
// subject is an observable but you can call next on them to emit a change when you want
"service"
import { Subject } from 'rxjs';
import {Injectable} from '#angular/core';
import {HttpClient} from '#angular/common/http';
import { map } from "rxjs/operators";
import {City} from '../models/city';
#Injectable()
export class CityService {
cities: City[] = [];
private updatedCities = new Subject<City[]>();
constructor(private http: HttpClient) {}
getCities() {
this.http.get<{message: string; cities: City[]}>('http://localhost:3000/cities')
.pipe(
map((cityData)=>{
return cityData.cities.map(city=>{
return{
code: city.code,
name: city.name
};
});
})
)
.subscribe((transCity) => {
this.cities = transCity;
console.log(this.cities);
this.updatedCities.next([...this.cities]);
});
}
getCityUpdateListener() {
return this.updatedCities.asObservable();
}
}
You can just use the json pipe:
<div *ngFor="let item of response">{{ item | json }}</div>
If you want to display it in "pretty" instead of as json, you need to access the individual fields of the item and format it in the desired way.
try as below , first get keys form reponse object you are receiving from http call and then go through each key in html , might resole your issue
in ts file
//response is data you received after making http call, list of cities in your case
keys = Object.keys(response);
in html file
<div *ngFor="let key of keys">
{{response[key].code }} {{response[key].name }}
</div>
this should work based on response you are getting from server
It looks like the issue here is that you're not actually returning an array of City, instead you're returning a dictionary or Map<City>. You'll probably want to iterate over your response and map it to the correct type.
this.citiesSub = this.cityService
.getCityUpdateListener()
.subscribe((cityMap) => {
this.cities = [ ...cityMap.values() ]
});
Asuming you are using httpClient(new released in angular5) then there is no need of the map() and pipe() functions, results are mapped to json by default you just have to subscribe to the service
this is how it would look your new service class
import { Subject } from 'rxjs';
import {Injectable} from '#angular/core';
import {HttpClient} from '#angular/common/http';
import { map } from "rxjs/operators";
import {City} from '../models/city';
#Injectable()
export class CityService {
cities: City[] = [];
private updatedCities = new Subject<City[]>();
constructor(private http: HttpClient) {}
getCities() {
return this.http.get<City[]>('http://localhost:3000/cities')//http.get<any> also work but for type safety i am asuming City[] array have the same structure.
}
getCityUpdateListener() {
return this.updatedCities.asObservable();
}
}
Then in your component you would have to subscrive to that service and use it
constructor(public cityService: CityService) {
this.cityService.getCities().subscribe(cities => {
this.cities = cities;
console.log(cities);
}, error=> {console.log(error)});//handling errors
}
ngOnInit() { } // just moved the service call to the constructor of the component
I hope this solve your problem,
Thanks

How to load this JSON data into Angular2

Im new to Angular2, I want to load this Json data and display in a page, Im not sure how to do..? From all sources I learnt I made a code and attached it below, But its not running because of some errors, can anyone help in fixing or writing a new code for me so that i can learn from it..
Thanks in advance for the help.
My code file - student.json
[
{
"name": "John",
"id_number": "12",
"attendance": "276 days",
"grade": "A"
},
],
this is my students.service.ts code
import {Injectable} from '#angular/core';
import { Http, Response } from '#angular/http';
#Injectable()
export class StudentsService {
constructor(private http:Http)
{
}
getStudents() {
return this.http.get('./students.json')
.map((response: Response) => {
console.log("mock data" + response.json());
return response.json();
}
}
and, this is my students.component.ts file
import {Component} from '#angular/core';
import { Http, Response } from '#angular/http';
import {StudentsService} from './students.service';
import 'rxjs/add/operator/map'
import 'rxjs/Rx';
#Component({
selector: 'students',
templateUrl: 'students.html',
styleUrls: ['./students.scss']
})
export class students {
public students;
constructor( private _studentsService:StudentsService, private http:Http)
{
this.students = this._studentsService.getStudents();
}
ngOnInit() {
this._load();
}
private _load() {
this.students = this._studentsService.getStudents();
}
}
You can write a service to load your html from json file and available all over your application like below.
#Injectable()
export class ConfigService {
public config: any;
private configObs: Observable<any>;
constructor(private http: Http) {
}
public load(filename: string): Observable<any> {
if ( this.config ) {
return Observable.of(this.config);
} else {
this.configObs = this.http.get(filename).map((res) => {
this.config = this.config || res.json() || {};
return this.config;
});
}
return this.configObs;
}
}
You can also put your data in typescript class format if that option is available referance answer
If you have JSON data and you want to show it in page.
Use Data Table to show It.
Here is the example you can see how to show on page.
Please click Here
Assign our json to a varible
myData = [{
"name": "John",
"id_number": "12",
"attendance": "276 days",
"grade": "A"
},
...
...
],
In your Html
<ul>
<li *ngFor="let data of myData">
<div>{{data.name}}</div>
<div>{{data.id_number}}</div>
<div>{{data.attendance}}</div>
<div>{{data.grade}}</div>
</li>
</ul>
Hope it helps
What you are dealing with is an Observable students, either you need to manually subscribe to that observable, or use the async pipe in the template which handles the subscribing for your.
Also you are now performing the request twice, in the constructor and in your OnInit. Remove one of those, I'd remove the one in the constructor, since I like to keep everything away from the constructor, that does not need to be there, like mentioned here: https://stackoverflow.com/a/35763811/6294072
Back to the subscribing... either do:
this.students = this._studentsService.getStudents();
<div *ngFor="let student of students | async">
<p>{{student.name}}</p>
<!-- ... -->
</div>
or:
this._studentsService.getStudents()
.subscribe(students => {
this.students = students;
})
<div *ngFor="let student of students">
<p>{{student.name}}</p>
<!-- ... -->
</div>

Categories

Resources