How to pass JSON data to a modal in angular 2 - arrays

I am trying to pop up a modal in angular 2 that will display a list of people. The source of the list is a JSON file. I think the data is not being properly bound to the table in the modal. I am new to angular 2 and am not sure what I am missing.
Service to read JSON file:
returns-json-array-service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs';
#Injectable()
export class ReturnsJsonArrayService {
constructor(private http: Http) {}
getPeople(): Observable<any> {
return this.http.request('./people.json')
.do( res => console.log('HTTP response:', res))
.map(res => res.json().payload)
.do(console.log);
//.map(res => res.json());
/*return this.http.get('./people.json')
.map((res:Response) => res.json())
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));*/
}
}
SAmple json file: people.json
{
"id": "1",
"name": "David Martinez Ros",
"email": "info#davidmartinezros.com",
"age": "33"
},
{
"id": "2",
"name": "Paco Roberto Corto",
"email": "paco.roberto.corto#gmail.com",
"age": "51"
},
{
"id": "3",
"name": "Silvia Elegante i Latina",
"email": "silvia.elegante.latina#gmail.com",
"age": "30"
}
]
modal-component.ts
import {Component, Input} from '#angular/core';
import {NgbModal, NgbActiveModal} from '#ng-bootstrap/ng-bootstrap';
import { Observable } from 'rxjs';
import { ReturnsJsonArrayService } from './returns-json-array.service';
#Component({
selector: 'ngbd-modal-content',
providers: [ReturnsJsonArrayService],
template: `
<div class="modal-header">
<h4 class="modal-title">Hi there!</h4>
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
<span aria-hidden="true">×</span>
</button>
<div class="modal-body" *ngFor="let person of peopleData | async" >
<p>One fine body…</p>
<table border=1>
<tr>
<td>
<h3>Id: {{ person.id }}</h3>
</td>
<td>
<h3>name: {{ person.name }}</h3>
</td>
<td>
<h3>email: {{ person.email }}</h3>
</td>
<td>
<h3>age: {{ person.age }}</h3>
</td>
<td>
</tr>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" (click)="activeModal.close('Close click')">Submit</button>
</div>
`
})
export class NgbdModalContent {
#Input() name;
#Input() peopleData: Observable<Array<any>>;
constructor(public activeModal: NgbActiveModal,private peopleService: ReturnsJsonArrayService) {
this.peopleData = this.peopleService.getPeople();
console.log("AppComponent.data:" + this.peopleData);
}
}
#Component({
selector: 'ngbd-modal-component',
templateUrl: './modal-component.html'
})
export class NgbdModalComponent {
constructor(private modalService: NgbModal) {}
open() {
const modalRef = this.modalService.open(NgbdModalContent);
modalRef.componentInstance.name = 'Barb' ;
console.log("Peopledatra on open():" + modalRef.componentInstance.peopleData);
}
}
modal-component.html
<button class="btn btn-primary" (click)="open()">Assign</button>

this.peopleService.getPeople() returns an observable which is cold to activate it and make it hot you must add a subscribe this.peopleService.getPeople().subscribe() the subscribe will take a success method as the first argument like so:
this.peopleService.getPerople().subscribe(
(json) => {
// do something here with the json
}
)
Once the json is returned you can set it to a property within your components scope like so:
this.peopleService.getPerople().subscribe(
(json) => {
this.json = json;
}
)
That property will then be accessible with in the components template.

Related

Creating select all checkbox in VueJS trough loop

I've created some data that are displayed trough a loop as table.
That code seems fine, however in my methods I get an error 'page is not defined'.
Does anyone know how do I define it?
Table:
<tr v-for="page in pages" v-bind:key="page">
<td>
<input type="checkbox" v-model="pageIds" #click="select" :value="page.id">
</td>
<td>{{page.name}}</td>
<td>
<v-tooltip bottom>
<template v-slot:activator="{ on, attrs }">
<span v-bind="attrs" v-on="on"
><v-icon class="icon delete">mdi-delete</v-icon></span
>
</template>
<span>Delete Page</span>
</v-tooltip>
</td>
</tr>
Methods:
<script>
export default {
data: () => ({
pages: [
{"id":"/","name":"/"},
{"id":"/index","name":"/index"},
{"id":"/about-us","name":"/about-us"}
],
selected: [],
allSelected: false,
pageIds: []
}),
methods: {
selectAll: function() {
this.pageIds = [];
if (this.allSelected) {
for (page in this.pages) {
this.pageIds.push(this.pages[page].id.toString());
}
}
},
select: function() {
this.allSelected = false;
}
}
};
</script>
change this in your code. it will resolve your error
from
v-for="page in pages" v-bind:key="page"
to
v-for="(page, index) in pages"
v-bind:key="index"

V-for not working when building array from external JSON file

Javascript:
// List of Products
const productsJSON = 'json/products.json';
// Component - Product Select
app.component('product-select', {
data() {
return {
selected: '',
options: []
}
},
template: `
<p v-for="(option, index) in options">test</p>
<div class="ui fluid labeled multiple search selection dropdown">
<input type="hidden"
name="products"
v-model="selected"
#change="selectProducts">
<i class="dropdown icon"></i>
<div class="default text">Select Products</div>
<div class="menu">
<div v-for="(option, index) in options"
class="item"
v-bind:data-value="option.name">
{{ option.name }}
</div>
</div>
</div>
`,
methods: {
selectProducts(event) {
this.selected = event.target.value.split(',');
console.log(this.selected);
}
},
beforeMount() {
const jsonResults = [];
this.options = jsonResults;
$.getJSON(productsJSON, function (data) {
jsonResults.push(...data);
});
console.log(jsonResults);
console.log(this.options)
}
});
I'm simply trying to populate the options: [] array with the array of objects returned from the JSON file in the $.getJSON function. Here is what the JSON file looks like:
[
{
"name": "White Gummy",
"value": "White Gummy"
},
{
"name": "Red Gummy",
"value": "Red Gummy"
},
{
"name": "Blue Gummy",
"value": "Blue Gummy"
}
]
My v-for is returning absolutely nothing, and the results of my two console.log functions are as follows:
Does anyone have any idea on what I'm doing wrong or if there is a better way to populate my array with the external .json file?
#Luckyfella provided a solution, which can be found in the created() lifecycle hook below:
// Component - Product Select
app.component('product-select', {
data() {
return {
selected: '',
options: null
}
},
template: `
<div class="ui fluid labeled multiple search selection dropdown">
<input type="hidden"
name="products"
v-model="selected"
#change="selectProducts">
<i class="dropdown icon"></i>
<div class="default text">Select Products</div>
<div class="menu">
<div v-for="(option, index) in options"
class="item"
v-bind:data-value="option.name">
{{ option.name }}
</div>
</div>
</div>
`,
methods: {
selectProducts(event) {
this.selected = event.target.value.split(',');
console.log(this.selected);
}
},
created: function () {
fetch(productsJSON)
.then(r => r.json())
.then(options => {
this.options = options;
});
}
});

Angular 4 -> Push Objects to an Array

I'm having an issue with pushing a new object to an existing array.
What I am trying to do ->
I am trying to push a new card drawn into the existing array/object
app.component.ts
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
deck: any;
cards = [];
hand = 0;
cardCount: number;
constructor(private _data: DataService){
console.log("Data Service ready to query api");
};
newDeck(){
this._data.newDeck().subscribe(res => this.deck = res.json());
}
deal(cardCount){
this._data.deal(cardCount, this.deck.deck_id).subscribe(response => this.cards = response.json());
if(this.cards){
this.cards.push(this.cards);
}
}
app.component.html
<h1>New Game</h1>
<button (click)="newDeck()">Start</button>
<p>{{ deck.remaining }} cards left!</p>
<div>
<h2>Your Hand</h2>
<button (click)="deal(2)">Deal</button> <button (click)="deal(1)">Hit</button>
<img width="150" src="{{cards.cards[0].image}}">
<img width="150" src="{{cards.cards[1].image}}">
<img width="150" src="{{cards.cards[2].image}}">
<img width="150" src="{{cards.cards[3].image}}">
</div>
<!-- <div *ngFor="let card of cards">
<img src="{{card.cards.image}}" width="150px" style="padding: 50px; margin: 10px;">
</div> -->
Yes - My *ngFor is broken because it doesn't seem to believe the cards are stored in an array.
ERROR -> Error trying to diff '[object Object]'. Only arrays and iterables are allowed
Nevertheless, I attached a screenshot of what is happening. I can select the Deal button, but when I click Hit, the counter does only pull 1 card but it is not stored in the array. Instead, it acts as a new object and displays as card.cards[0].image instead of card.cards[2].image, since there are already 2 objects in the array after clicking the deal button.
Any ideas on how to push the new cards to into the cards array? Image
It may help to see how the cards are received when called ->
{
"remaining": 50,
"deck_id": "1omsivg9l9cu",
"success": true,
"cards": [
{
"image": "http://deckofcardsapi.com/static/img/KS.png",
"images": {
"svg": "http://deckofcardsapi.com/static/img/KS.svg",
"png": "http://deckofcardsapi.com/static/img/KS.png"
},
"suit": "SPADES",
"value": "KING",
"code": "KS"
},
{
"image": "http://deckofcardsapi.com/static/img/AH.png",
"images": {
"svg": "http://deckofcardsapi.com/static/img/AH.svg",
"png": "http://deckofcardsapi.com/static/img/AH.png"
},
"suit": "HEARTS",
"value": "ACE",
"code": "AH"
}
]
}
You can directly use the card.cards object in your ngFor
<span *ngFor="let item of data.cards">
<img src="{{item.image}}" width="150px" style="padding: 50px; margin: 10px;">
</span>

Angularjs 2 ng-repeat not working

I am using ng-repeat in angularjs 2 with custom template but it's not working
html
<tr ng-repeat="data in itemList">
<td>{{data.test}}</td>
</tr>
.ts
import {Component, View} from "angular2/core";
#Component({
selector: 'my-app',
templateUrl: 'app/hello.component.html'
})
export class AppComponent {
public itemList = [
{name:"Apple", test:"1"},
{name:"Orange", test:"2"},
{name:"Grapes", test:"3"},
];
}
I don't know what is the problem. It's working in Angularjs 1 but Angularjs 2 not working
You should use *ngFor instead of ng-repeat. There is no ng-repeat with angular2.
Change it as,
<tr *ngFor="let data of itemList">
<td>{{data.test}}</td>
</tr>
DEMO
Here is full syntax of code.
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
template: `
<p>List:</p>
<ul>
<li *ngFor="let item of itemList">
{{ item.name }} {{item.test}}
</li>
</ul>
`,
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
public carsList: Array<string>;
public itemList = [
{ name: "Apple", test: "1" },
{ name: "Orange", test: "2" },
{ name: "Grapes", test: "3" },
];
}

Displaying Nested Objects from JSON file in Angular2

Learning how to display nested data using Angular 2. There are lots of similar questions on the web but I haven't seen anything broken down into a simple object I can follow and repeat.
I have a list that shows a JSON list of heroes:
Basic List & Detail for Superman and Batman
My current goal is to show a list of accounts of the selected hero.
My problem is code here related to a list where I'd like to show accounts based on the hero chosen.
My JSON is broken where I can see sub-nested detail but I can't seem to get that level of detail into a list.
hero-detail.component.html
<main class="col-sm-9">
<div *ngIf="hero">
<h2>{{hero.user[0].firstName}} {{ hero.user[0].lastName }}</h2>
<div>
<label>First Name: </label>
<input [(ngModel)]="hero.user[0].firstName" placeholder="First Name">
</div>
<div>
<label>Last Name: </label>
<input [(ngModel)]="hero.user[0].lastName" placeholder="Last Name">
</div>
<ul class="list-group">
<li class="list-group-item" *ngFor="let hero of heroes">
<div *ngFor="let account of accounts">
{{ account[0].accountName }}
</div>
</li>
</ul>
</div>
</main>
hero-detail.component.ts
import { Component, Input } from '#angular/core';
import { Hero } from './hero';
#Component({
selector: 'my-hero-detail',
templateUrl: './heroes-detail.component.html'
})
export class HeroesDetailComponent {
#Input()
hero: Hero;
}
hero-component.html
<aside class="col-sm-3">
<div class="list-group">
<button class="list-group-item"
*ngFor="let hero of heroes"
[class.selected]="hero === selectedHero"
(click)="onSelect(hero)">
{{ hero.user[0].firstName }} {{ hero.user[0].lastName }}
</button>
</div>
</aside>
<my-hero-detail [hero]="selectedHero"></my-hero-detail>
heroes.component.ts
import { Component, OnInit } from '#angular/core';
import { Hero } from './hero';
import { HeroService } from './hero.service';
#Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
providers: [ HeroService ]
})
export class HeroesComponent implements OnInit {
title = 'Tour of Heroes';
heroes: Hero[];
selectedHero: Hero;
constructor(
private heroService: HeroService) { }
getHeroes(): void {
this.heroService
.getHeroes()
.then(heroes => this.heroes = heroes);
}
ngOnInit(): void {
this.getHeroes();
}
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
}
mock-heroes.ts
import { Hero } from './hero';
export const HEROES: Hero[] =
[
{
"heroId": 1001,
"alias": "Superman",
"user": [
{
"firstName": "Clark",
"lastName": "Kent",
"email": "clark.kent#dailyplanet.com"
}
],
"accounts": [
{
accountNum: "1234567890",
accountName: "Personal Checking",
type: "checking",
balance: 1500.00
},
{
accountNum: "2345678901",
accountName: "Personal Savings",
type: "savings",
balance: 2000.00
}
]
},
{
"heroId": 1002,
"alias": "Batman",
"user": [
{
"firstName": "Bruce",
"lastName": "Wayne",
"email": "bruce.wayne#wayne.com"
}
],
"accounts": [
{
accountNum: "1234567890",
accountName: "Personal Checking",
type: "checking",
balance: 7500000.00
},
{
accountNum: "2345678901",
accountName: "Personal Savings",
type: "savings",
balance: 20000000.00
}
]
}
]
You have to do like this:
<ul class="list-group">
<li class="list-group-item" *ngFor="let hero of heroes">
<div *ngFor="let account of hero.accounts">
{{ account[0].accountName }}
</div>
</li>
</ul>
You have to use second *ngFor with hero.accounts,
and make your template structure better, you are using hero in top div element, how you are getting that:
<div *ngIf="hero">
So check out first based on your requirement.
Hope this help you.
This issue was solved using the Elvis operator (?.) from here: Angular2 ngFor Iterating over JSON
hero.ts
export class Hero {
id: number;
name: string;
firstName: string;
lastName: string;
accounts: Array<Account>;
}
export interface Account {
bank: string;
type: string;
balance: number;
}
heroes-detail.component.html
<main class="col-sm-9">
<div *ngIf="hero">
<h2>{{hero.firstName}} {{ hero.lastName }}</h2>
<div>
<label>id: </label>{{hero.id}}
</div>
<div>
<label>First Name: </label>
<input [(ngModel)]="hero.firstName" placeholder="First Name">
</div>
<div>
<label>Last Name: </label>
<input [(ngModel)]="hero.lastName" placeholder="Last Name">
</div>
<table class="table table-hover">
<thead>
<tr>
<th>Bank Name</th>
<th>Account Type</th>
<th>Balance</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let account of hero?.accounts">
<td>{{ account.bank }}</td>
<td>{{ account.type }}</td>
<td>{{ account.balance }}</td>
</tr>
</tbody>
</table>
</div>
</main>
mock-heroes.ts
import { Hero } from './hero';
export const HEROES: Hero[] =
[
{
"id": 11,
"name": "Superman",
"firstName": "Clark",
"lastName": "Kent",
"accounts": [
{
"bank": "Chase",
"type": "Checking",
"balance": 1000.00
},
{
"bank": "Chase",
"type": "Savings",
"balance": 2000.00
}
]
},
{
"id": 12,
"name": "Batman",
"firstName": "Bruce",
"lastName": "Wayne",
"accounts": [
{
"bank": "Bank of Gotham",
"type": "Checking",
"balance": 1000000000.00
},
{
"bank": "Bank of Gotham",
"type": "Savings",
"balance": 2000000000.00
}
]
},
{
"id": 13,
"name": "Wonder Woman",
"firstName": "Diana",
"lastName": "Prince",
"accounts": [
{
"bank": "",
"type": "",
"balance": 0.00
},
{
"bank": "",
"type": "",
"balance": 0.00
}
]
}
]

Resources