Push object into new array with interfaces in Angular2 - arrays

Im retrieving a list of objects from a service in Angular2.
On click I want to put the object from my ngFor onto a new array (with the same interface).
I'm able to display the list with items just fine. I'm also able to display the item I selected in the console, but I'm not able to push this object into a new array with the same interface.
My interface:
export interface Item {
id: string,
name: string,
icon_url: string,
exterior: string,
type: string,
tag: string,
addedClass: string,
selected: boolean
}
My component:
import {Component, OnInit, DoCheck} from 'angular2/core';
import {Item} from './../interfaces/interfaces.ts';
import {ItemService} from './../services/item.service.ts';
#Component({
template: `
<h2>Add item</h2>
<div class="itemlist">
<ul class="items">
<li
*ngFor="#item of items"
[class.selected]="item === selectedItem"
(click)="onSelect(item)"
>
<span class="item">
{{item.name}}
</span>
</li>
</ul>
</div>
`
})
export class AddTradeComponent implements OnInit {
public items: Item[];
public selectedItems: Item[];
constructor(private _itemService: ItemService) { }
getUserItems() {
this._itemService.getUserItems().then(userItems => this.items = userItems);
}
ngOnInit() {
this.getUserItems();
}
onSelect(item) {
console.log('items ', item);
this.selectedItems.push(item);
}
}
Errors that I'm getting:
Error during evaluation of "click"
TypeError: Cannot read property 'push' of undefined
Logging the object works fine, I just cannot push it onto this.selectedItems.

You didn't initialize your arrays. Either initialize the arrays when you declare your member variables:
public items: Item[] = [];
public selectedItems: Item[] = [];
Or initialize them in your constructor:
constructor() {
this.items = [];
this.selectedItems = [];
}

Related

how to receive the data from an API to a component model in angular

I get the data from an API with the service game in game.component.ts I get the data I can show in cards the array of objects games but i am geting this error:
core.js:6241 ERROR Error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays.
at NgForOf.ngDoCheck (common.js:4406)
also the model Game{name:"",image:"",releaseOn:Array(0)......} it is empty the model is not being used.
export class GamesService {
public url;
public game: Game[];
constructor(
private http: HttpClient
) { }
getListGames():Observable<any>
{
return this.http.get(`https://api.rawg.io/api/games?&dates=2010-01-01,2020-12-31&page_size=20`);
}
to the component games.component.ts
export class GamesComponent implements OnInit {
public games: Game;
//public game: Game;
public genres;
public platforms;
public pcIcon: string = '../assets/pc.png';
public PC;
public Xbox: string = '../assets/xbox.png';
public plat: string;
public name: string;
public title = 'Games component';
//public carouselExampleControls: String;
//public index: number;
public rating;
public page: number = 1;
public totalPages;
public next;
public prev;
public number_pages;
public count;
public nextpage;
constructor(
private gamesService:GamesService,
private router: Router,
)
{
this.games = new Game('', '', [], '', [], 1 );
console.log(this.games);
}
ngOnInit(): void {
this.gamesService.getListGames().subscribe( (data: any) =>{
this.games = data.results;
console.log(data.results[0].platforms[0]);
data.results.forEach((game) => {
game.platforms.forEach((platform)=> {
//your code here
console.log(platform);
});
});
});
}
How to get the object game from the api using the model game.ts
game.ts
export class Game{
id: any;
constructor(
public name: string,
public image: string,
public releaseOn: Array<ReleaseOn>,// do it an array of releaseOn platform
public releaseDate: string,
public genre: Array<Genres>,// do it an array of genres
public rating: number,
){}
}
export class Genres {
genre: string
}
export class ReleaseOn {
releaseOn: string
}
How to get the data from API with game.ts model in game.component
this.games = new Game('', '', [], '', [], 1 );
game-card.component.ts
<div class="card">
<h5 class="card-title">
{{ games.name}}
</h5>
<img class="card-img-top" [src]="games.background_image" alt="Card image cap">
<div class="card-body">
<!-- <p class="card-text"><span class="black">Release on</span> {{ game.platform.name}}</p> -->
<p class="card-text"><span class="black">Release on</span> <span class="platform" *ngFor="let platforms of games.platforms"><img class="img-icon" *ngFor="let platforms of games.platforms.name"> {{ platforms.platform.name }}</span></p>
<p class="card-text"><span class="black">Release date</span> {{ games.released }}</p>
<p class="card-text"><span class="black">Genere </span><span class="platform" *ngFor="let genres of games.genres">{{ genres.name}}</span></p>
<p class="card-text"><span class="black">Rating <star-rating [value]="games.rating" totalstars="5" checkedcolor="yellow" uncheckedcolor="black" size="24px" readonly="false"></star-rating></span></p>
<p class="card-text">
<span class="badge badge-primary">
{{ games.user_game }}
</span>
</p>
<div>
object game from api
As data model and view model are different, You need to map each property.
Try this. I have not tested it but it should work.
this.gamesService.getListGames().subscribe( (data: any) =>{
this.games = data.results.map(item=>{
const gameItem: Game = {
id: item.id,
name: item.name,
image: item.background_image,
releaseDate: item.released,
rating: item.rating,
genre: item.genres ? item.genres.map(g => ({ genre: g.name })) : [],
releaseOn: item.platform
? item.platform.map(p => ({ releaseOn: p.name }))
: []
};
return gameItem;
});
});

Angular 4 Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays

Here is the code below:
component.ts
by selecting match geting id in routing, then taking this id from URL
export class MatchComponent implements OnInit {
_postArrayMatch: match[];
constructor(public router:Router, private matchService: MatchService,
private route: ActivatedRoute) {}
ngOnInit(){
this.getMatchId();
}
getMatchId() :void {
this.route.params.forEach((params: Params)=> {
let id = +params['id'];
this.matchService.getMatch(id).subscribe(
resultArray => this._postArrayMatch = resultArray,
error => console.log("Error ::" + error))
})
}
component.html
just basic interpolation by doing ngFor loop
<div *ngFor="let post of _postArrayMatch">
<p>{{post.team1.name}}</p>
</div>
service.ts
passing the dynamic id
getMatch(id:number): Observable<match[]>{
return this.http.get<match[]>(`http://localhost:3000/match/${id}`)
}
interface
export interface match{
team1:{
name:string,
id:number
}
team2:{
name:string,
id:number
}
}
Try something like this where you create the object in your response
component.ts
export class MatchComponent implements OnInit {
_postArrayMatch: match[];
newArr: Array<Object> = [];
constructor(public router:Router, private matchService: MatchService,
private route: ActivatedRoute) {}
ngOnInit(){
this.getMatchId();
}
getMatchId() :void {
this.route.params.forEach((params: Params)=> {
let id = +params['id'];
this.matchService.getMatch(id).subscribe(
resultArray => {
this._postArrayMatch = resultArray;
const keys = Object.keys(this._postArrayMatch) // new stuff starts here
for(let i = 0; i < keys.length; i++) {
newArr.push(this._postArrayMatch[keys[i]]);
newArr[i]['team'] = keys[i];
}
},
error => console.log("Error ::" + error))
})
}
And then you can access ALL of your sub objects in your html
Component.html
<div *ngFor="let post of newArr">
<p> {{post.team}}: {{post.name}}</p>
</div>
Currently, with what you have you are hard coding for team 1, and if you want to do that then you shouldn't be using the *ngFor
Thats seems the easiest way i could find on how to get the data from objects.
<div *ngFor="let post of objectKeys(_postArrayMatch.team1)">
<div> Team1: {{ _postArrayMatch.team1[post]}}</div>
</div>
component.ts
objectKeys = Object.keys;

Angular 5 - JSON to "scope" binding to *NgFor

I want to bind the data that i get from my JSON request to the *NgFor to be able to read it out, This is how the data looks like that i get in:
{Id: null, Code: "9348892084", HasInfo: true, Info: Array(26)}
HasInfo:true
Info:
Array(26)
0:{ProductId: 32631, Name: "JNOOS", Image: "http://sd-m-mg", …}
1:{ProductId: 32969, Name: "SWIFT", Image: "http://sd-33087.jpg",…}
2:{ProductId: 32570, Name: "GABIX", Image: "http://sd-c7273.jpg", …}
3:{ProductId: 32473, Name: "MISMA", Image: "http://sd-mt8-8343e4d95.jpg", …}
I was working with AngularJS before and there i made the request as such:
$scope.getAll{
$http({
method: 'Get',
url: "http://URL/" + Code
})
.success(function (data) {
$scope.All = data.Info;
})
No i am moving to Angular5 and i would like to get the same array of information bind to the :
<div *ngFor="let x of info">
{{ x.Name }}
</div>
How would i adjust the below to get the same as above?
export class AppComponent {
readonly ROOT_URL = 'http://url/content/'
constructor(private http: HttpClient) {}
ngOnInit() {
this.getData()
}
getData() {
this.http.get(this.ROOT_URL + 'Getscope')
.subscribe(
data => {
var test = data;
// var info = data.Info = not valid!;
// var test can't be approached by *ngFor
console.log(test);
console.log(test.info);
//$scope.info = data.Info;
//this.info = data;
}, error => {
console.error("HTTP FAILURE ERROR!!!");
}
)
};
}
Also the json output has an array inside an object, do i say this correct?
From your code you are using info in html but not assigning to any class variable,
Take a public variable public info; and assign data using this.info = data.Info
Component:
export class AppComponent {
readonly ROOT_URL = 'http://url/content/'
public info;
constructor(private http: HttpClient) {}
ngOnInit() {
this.getData()
}
getData() {
this.http.get(this.ROOT_URL + 'Getscope')
.subscribe(
data => {
this.info = data['Info']; // here is the change
}, error => {
console.error("HTTP FAILURE ERROR!!!");
}
)
};
}
Your HTML can be same:
<div *ngFor="let x of info">
{{ x.Name }}
</div>
The simplest solution would be to use an async pipe. This way you do not need to subscribe to the stream returned by http.get.
app.component.ts
import { Observable } from 'rxjs/Observable';
import { map } from 'rxjs/operators';
export class AppComponent {
readonly url = 'http://url/content/'
constructor(private http: HttpClient) {}
info$: Observable<any[]>;
ngOnInit() {
this.info$ = this.http.get(this.url + 'Getscope')
.pipe(map(resp => resp.Info));
}
}
app.component.html
<div *ngFor="let x of info$ | async">
{{ x.Name }}
</div>

Angular 4 - binding not working when declaring variable as a property of an object

I have a page where I display a list with locations, and clicking on each of them I am displaying assets for that location. I have set up the template like this:
<li
*ngFor="let location of locations"
(click)="select(location)"
droppable
[dragOverClass]="'drag-target-border'"
(onDrop)="onItemDrop($event, location)"
>
{{ location.name }}
<ul *ngIf="currentLocation == location && assetsVisible">
<ng-container *ngIf="currentLocation.assets?.length > 0;else message">
<li
*ngFor="let asset of currentLocation.assets"
class="asset"
draggable
[dragData]="asset"
>
<p>{{ asset.title }}</p>
<p>{{ asset.address }}</p>
<p>{{ asset.value }}</p>
</li>
</ng-container>
<ng-template #message>No assets for {{ location.name }}</ng-template>
</ul>
</li>
And in the component, I have tried to first set the empty assets array, and an empty currentLocation object with a property assets:
locations: any[] = [];
currentLocation: any = {
assets: any[] = []
};
And then in the method select I am fetching the assets like this:
select(location){
if (this.currentLocation != location || !this.assetsVisible) {
this.assetsVisible = true;
}
else {
this.assetsVisible = false;
}
this.currentAddress = address;
this.locationService.getAssets(location.Id)
.subscribe(
assets => this.currentLocation.assets = assets
);
}
What I would like to do is to since I am using the drag and drop plugin for Angular I would like to make it possible for user to drag assets from one location to another. And then just temporarily push that asset to an array of the property currentLocation.assets where the asset was dropped to.
I have made a function for that:
onItemDrop(e: any, location) {
this.select(location);
this.currentLocation.assets.push(e.dragData);
}
That drags and drops the asset from one location to another, but I get an error:
ERROR TypeError: Cannot read property 'push' of undefined
I have done console.log(this.currentLocation.assets) in both select and onItemDrop method, and both times I get:
undefined
But, if I do console.log(this.currentLocation) in both functions, I get an object with an empty assets array as a property of it. And I can access every other property of that object, but I can't access assets array.
Also, when I do console.log in ngOnInit I get an empty array back in the console:
console.log(this.currentLocation.assets);
So, I am not sure why do I get this error, why do I get an object with an empty assets array as a property of it, but I can't access directly property of that object, when I can access all other properties of the same object?
This is the whole component:
import { Component, OnInit } from '#angular/core';
import { LocationService } from '../services/location.service';
#Component({
moduleId: module.id,
selector: 'sd-addresses',
templateUrl: 'location.component.html'
})
export class LocationsComponent implements OnInit {
errorMessage: string;
locations: any[] = [];
currentLocation: any = {
assets: any[] = []
};
assetsVisible: boolean = false;
constructor(public locationService: LocationService) {}
ngOnInit() {
this.getLocations();
}
getLocations() {
this.locationService.get()
.subscribe(
locations => this.locations = locations,
error => this.errorMessage = <any>error
);
}
select(location = {}) {
if (this.currentLocation != location || !this.assetsVisible) {
this.assetsVisible = true;
}
else {
this.assetsVisible = false;
}
this.currentLocation = location;
this.locationService.getAssets(location.Id)
.subscribe(
assets => this.currentLocation.assets = assets,
);
}
onItemDrop(e: any, location:any = {}) {
this.currentLocation.assets = [];
this.currentLocation.assets.push(e.dragData);
this.select(location);
}
}
In your declaration try this:
locations: any[] = [];
currentLocation: any = {
assets: []
};
This way you will be declaring currentLocation that is an object type {} and has a property assets that is an array type [].

Refresh sortable when a new item is added to the array

Sortable component only shows initial array elements. When a new value is pushed into array, the sortable does not display it.
Component:
import { Component } from '#angular/core';
#Component({
selector: 'custom-item-template-demo',
templateUrl: './custom-item-template.html'
})
export class CustomItemTemplateDemoComponent {
public itemStringsLeft: any[] = [
'Windstorm',
'Bombasto',
'Magneta',
'Tornado'
];
public addItem() {
this.itemStringsLeft.push("new item");
}
}
Template:
<button type="button" (click)="addItem()">Add</button>
<template #itemTemplate let-item="item" let-index="index"><span>{{index}}: {{item.value}}</span></template>
{{itemStringsLeft.length}}
<pre>{{ itemStringsLeft | json }}</pre>
<bs-sortable
[(ngModel)]="itemStringsLeft"
[itemTemplate]="itemTemplate"
itemClass="sortable-item"
itemActiveClass="sortable-item-active"
placeholderItem="Drag here"
placeholderClass="placeholderStyle"
wrapperClass="sortable-wrapper"
></bs-sortable>
Workaround: Call manually to writeValue of the SortableComponent
import { Component, ViewChild } from '#angular/core';
#Component({
selector: 'custom-item-template-demo',
templateUrl: './custom-item-template.html'
})
export class CustomItemTemplateDemoComponent {
public itemStringsLeft: any[] = [
'Windstorm',
'Bombasto',
'Magneta',
'Tornado'
];
#ViewChild(SortableComponent, {static: false}) sortableComponent: SortableComponent;
public addItem() {
this.itemStringsLeft.push("new item");
this.sortableComponent.writeValue(this.itemStringsLeft);
}
}
Another workaround with re asigning list values:
public addItem() {
this.itemStringsLeft.push("new item");
this.itemStringsLeft = this.itemStringsLeft.slice();
}
should be add import { SortableComponent } from 'ngx-bootstrap';
It will working.
The spread operator worked well for me with this issue:
addItem(){
this.itemStringsLeft = [...this.itemStringsLeft, {name: 'Windstorm', range: 5}];
}

Resources