Why is this Angular class property undefined? - arrays

I am currently working on a Angular project that makes it possible to create lobbies for different web games. The idea is that the application already gathers players so a web game can be started immediately.
But right now I am running into a problem that is caused by getting data from my Springboot Java API.
The Angular application gets the data correctly but when i try to convert the observable into a normal Game[] after subscribing ofcourse. It makes all the properties of the elements in the Game[] undefined. What is causing this to happen?
The Game Service:
//Returns a list of all games known to the API
getAllGames() :Observable<Game[]>
{
return this.httpClient.get<Game[]>(this.connectionService.getConnectionString()+"/games",this.httpOptions)
}
The Game class:
export class Game {
public Id : String;
public Name: String;
public RedirectURI : String;
public MinUserCount : Number;
public MaxUserCount : Number;
constructor(Id : String,Name : String,RedirectURI : String,MinUserCount : Number,MaxUserCount : Number)
{
this.Id = Id;
this.Name = Name;
this.RedirectURI = RedirectURI;
this.MinUserCount = MinUserCount;
this.MaxUserCount = MaxUserCount;
}
}
The Component:
games: Game[];
//Get all games known to the API
this.gameService.getAllGames().subscribe( elements => this.games = elements )
//This doesn't work all the elements of this.games are undefined.
I have also tried to work with a foreach of the array that gets returned.
With no effect either
games: Game[];
//Get all games known to the API
this.gameService.getAllGames().subscribe(elements => {
elements.forEach(item => {
var game = new Game(item.Id, item.Name, item.RedirectURI, item.MinUserCount, item.MaxUserCount)
this.games.push(game)
})
})
The JSON Result for the GetRequest
[
{
"name": "QuizGame",
"id": "dg217d65126b5e31v6d5v5d15v564d",
"maxUserCount": 4,
"redirectURI": "http://localhost:8082",
"minUserCount": 1
},
{
"name": "RPG",
"id": "dt76TQWR127367125SDYATFHa32615",
"maxUserCount": 10,
"redirectURI": "http://localhost:8083",
"minUserCount": 0
},
{
"name": "Scribble Clone",
"id": "378167e86dsahdgahkj312DJKHDg2d",
"maxUserCount": 9,
"redirectURI": "http://localhost:8084",
"minUserCount": 1
},
{
"name": "WebPonker",
"id": "0o00dhsnvkjzbcxhgjyg23v2gh3dvg",
"maxUserCount": 4,
"redirectURI": "http://localhost:8085",
"minUserCount": 4
}
]

The properties in your JSON response start with a lowercase.
In your Game class, you use properties that start with an uppercase.
I believe the parsing from JSON to a typescript object is case sensitive. Could you try to change the first letter of your properties to lowercase?

Related

How to generate an array of objects in Alpine.data with Livewire Eloquent collection and Adding properties in those js objects

and thanks for your attention and help,
I have a Collection in my livewire controller. This collection contains a list of players, with some properties : here we will just focus on id and name.
So we can imagine that we have 3 players in the collection :
Players[0] : 'id'=>1, 'name'=>'Yann';
Players[1] : 'id'=>2, 'name'=>'Robert';
Players[2] : 'id'=>3, 'name'=>'Jessica';
I need to get these players in my alpine data.
I can easily get these players in Alpine with the #js method :
window.addEventListener('alpine:init', () => {
Alpine.data('data', () => ({
players: #js($players),
}))
})
So, now I have in my alpine.data :
players: [
{ id: 1, name: 'Yann' },
{ id: 2, name: 'Robert' },
{ id: 3, name: 'Jessica' },
]
Now I can easily display the players in my html with a template x-for :
<template x-for="player in players">
<p x-text="player.name"></p>
</template>
But I want to add some additionnal properties in each player object. Those properties will be updated in front depending user's actions.
I would like to get something like this :
players: [
{ id: 1, name: 'Yann', touched20: 0, touched15: 0 },
{ id: 2, name: 'Robert', touched20: 0, touched15: 0 },
{ id: 3, name: 'Jessica', touched20: 0, touched15: 0 },
]
All additionnal properties are the same for each object in the array, so I imagine i could use a foreach loop to put them in the objects.
But I can't see and don't understand how i can include a loop in my alpine.data script to do this.
Anyone could help me ?
I edit my question because I found a solution :
I just make a loopPlayers function outside of my alpine data and call this function in the alpine data :
function loopPlayers() {
let players = [];
const livewirePlayers = #js($players);
livewirePlayers.forEach(element => {
players.push(element);
});
players.forEach(function(element) {
element.touched15 = 0;
})
return players;
}
And in alpine.data :
players: loopPlayers(),
Now I have my collection of players from my livewire controller & I have new properties for each element of the collection in the js data
That was easy, as usual I guess :)

Push into Array from Json Angular 5

I'm receiving from an endpoint the following response
{
"data": [{
"volume": 4.889999866485596,
"name": "Carton03",
"weight": 5.75,
"storage": 3
}, {
"volume": 2.6500000953674316,
"name": "Carton02",
"weight": 4.5,
"storage": 2
}, {
"volume": 1.4500000476837158,
"name": "Carton01",
"weight": 5,
"storage": 1
}],
"response": "true",
"type": "Storages"
}
Below I'm trying to create an array at my component:
export class StorageComponent {
private data: any;
private stors: Storage [];
constructor (private storageService: StorageService ) {}
storage () {
this.data = this.storageService.Storage();
//storageService.Storage is an assistant service that parse the response
// and brings me back only the data-array of the response
for (let i = 0; i < this.data.data.length; i++) {
const storages = this.data.data[i];
console.log(storages.storage);
console.log(storages.name);
console.log(storages.weight);
console.log(storages.volume);
this.stors[i] = storages;
}
}
}
I have a const storages to examine that i can evaluate data
and everything is ok.
The problem is that I want to fill up my variable stors that is an array of Storage model which has these attributes , storage,name etc.
I'm trying to do that through the last line but something is wrong ,
getting :
Cannot set property '0' of undefined
Any ideas?
The error is saying that during the first iteration of the loop, the assignment to this.stors[0] is not possible because it is undefined. The stors property has not been initialized. Consider:
constructor (private storageService: StorageService) {
this.stors = [];
}
Just do an initialization when defining the stors property like this:
private stors: Storage[] = [];
you can initialize other properties as well:
private data: any = null;
you may initialize this in the constructor or ngOnInit, but I think it's a better way like this to initialize arrays directly in the properties.
Initializing your class properties will avoid this type of behavior.

Angular2 Mapping nested json array to model

I am not able to map the nested json array which is response from Web to my model array in Angular2. Suppose I have json array response as below:
[{
"base_url": "http://mysearch.net:8080/",
"date": "2016-11-09",
"lname": "MY PROJ",
"name": "HELLO",
"description": "The Test Project",
"id": 10886789,
"creationDate": null,
"version": "2.9",
"metrics": [{
"val": 11926.0,
"frmt_val": "11,926",
"key": "lines"
},
{
"val": 7893.0,
"frmt_val": "7,893",
"key": "ncloc"
}],
"key": "FFDFGDGDG"
}]
I was trying to manually map the fields referring the link Angular 2 observable doesn't 'map' to model to my model and was able to display those in my HTML by iterating through ngFor.....but I want to also display ncloc and lines value also in the HTML but I am not sure how to map those values to my Model array like mentioned in the above link.
Can you please help me with this?
Thanks.
EDIT
Mode class
export class DeiInstance {
base_url: string;
date: string;
lname : string;
name : string;
id : number;
key:string;
constructor(obj: DeiInstance) {
this.sonar_url = obj['base_url'];
this.lname = obj['lname'];
this.name = obj['name'];
this.id = obj['id'];
this.key = obj['key'];
this.date = obj['date'];
}
// New static method.
static fromJSONArray(array: Array<DeiInstance>): DeiInstance[] {
return array.map(obj => new DeiInstance(obj));
}
}
You can simplify your model and your mapping a lot.
You don't need to map your API response manually. JavaScript/TypeScript can do this for you.
First you need multiple interfaces.
export interface DeiInstance {
base_url: string;
date: string;
lname: string;
name: string;
description: string;
id: number;
creationDate: string; //probably date
version: string
metrics: Metric[];
key: string;
}
export interface Metric {
val: decimal;
frmt_val: decimal;
key: string;
}
You can then use the as-"operator" of TypeScript to cast your API response to the DeiInstance Type.
sealSearch(term: string): Observable<DeiInstance[]> {
return this.http.get(this.sealUrl + term)
.map(response => response.json() as DeiInstance[])
.catch(this.handleError);
}
If you use interfaces instead of classes you have also the advantage that you have less production code which will be sended to the client browser.
The interface is only there for pre-compile-time or however you want to call it.
Hope my code works and it solves your problem.

Angularjs: Many-to-Many with Through Relationship

In my app I have Students who can Enroll in Classes.
An Enrollment records the start and end dates along with a reference to which Class.
The Class has details like a class name and description.
Student (1) - (N) Enrollment (1) - (1) Class
Therefore
Student (1) - (N) Class
This is what I would like the object to look like.
{
"studentId": 1,
"name": "Henrietta",
"enrolledClasses": [
{
"enrollmentId": 12,
"startDate": "2015-08-06T17:43:14.000Z",
"endDate": null,
"class":
{
"classId": 15,
"name": "Algebra",
"description": "..."
}
},
"enrollmentId": 13,
"startDate": "2015-08-06T17:44:14.000Z",
"endDate": null,
"class":
{
"classId": 29,
"name": "Physics",
"description": "..."
}
}
}
But my RESTful API cannot (easily) output the full nested structure of the relationship because it's ORM can't do Many to Many with Through relationships. So I get the student and their multiple enrollments, but only a reference to the class for each, not the details. And I need to show the details, not just an Id.
I could wait for the student object to be available and then use the references to make additional calls to the API for each classId to get details, but am not sure how, or even if, to then integrate it with the student object.
This is what I have so far.
function findOne() {
vm.student = StudentsResource.get({
studentId: $stateParams.studentId
});
};
This is what I get back from my API
{
"studentId": 1,
"name": "Henrietta",
"enrolledClasses": [
{
"enrollmentId": 12,
"startDate": "2015-08-06T17:43:14.000Z",
"endDate": null,
"class": 15
},
"enrollmentId": 13,
"startDate": "2015-08-06T17:44:14.000Z",
"endDate": null,
"class": 29
}
}
And for the class details I can them one at a time but haven't figured out how to wait until the student object is available to push them into it. Nor how to properly push them...
{
"classId": 15,
"name": "Algebra",
"description": "..."
}
{
"classId": 29,
"name": "Physics",
"description": "..."
}
Question
Is it good practice to combine the student and class(es) details through the enrollments, as one object?
If so, whats the most clear way to go about it?
If not, what is another effective approach?
Keep in mind that the app will allow changes to the student details and enrollments, but should not allow changes to the details of the class name or description. So if Angular were to send a PUT for the object, it should not try to send the details of any class, only the reference. This would be enforced server side to protect the data, but to avoid errors, the client shouldn't try.
For background, I'm using SailsJS and PostgreSQL for the backend API.
So basically on the Sailsjs side you should override your findOne function in your StudentController.js. This is assuming you are using blueprints.
// StudentController.js
module.exports = {
findOne: function(req, res) {
Student.findOne(req.param('id')).populate('enrollments').then(function(student){
var studentClasses = Class.find({
id: _.pluck(student.enrollments, 'class')
}).then(function (classes) {
return classes
})
return [student, classes]
}).spread(function(student, classes){
var classes = _.indexBy(classes, 'id')
student.enrollments = _.map(student.enrollments, function(enrollment){
enrollment.class = classes[enrollment.class]
return enrollment
})
res.json(200, student)
}).catch(function(err){
if (err) return res.serverError(err)
})
}
}
// Student.js
module.exports = {
attributes: {
enrollments: {
collection: 'enrollment',
via: 'student'
}
// Rest of the attributes..
}
}
// Enrollment.js
module.exports = {
attributes: {
student: {
model: 'student'
}
class: {
model: 'class'
}
// Rest of the attributes...
}
}
// Class.js
module.exports: {
attributes: {
enrollments: {
collection: 'enrollment',
via: 'class'
}
// Rest of the attrubutes
}
}
Explanation:
1. the _.pluck function using Lo-dash retuns an array of classIds and we find all of the classes that we wanted populated. Then we use the promise chain .spread(), create an array indexed by classid and map the classIds to the actual class instances we wanted populated into the enrollments on the student returned from Student.findOne().
2. Return the student with enrollments deep populated with the proper class.
source: Sails.js populate nested associations

how to add a complextype object dynamically to an array

We have created an array of complextype(Carrier field) objects. See below metadata
{ shortName : 'Person',
namespace : 'Demo',
autoGeneratedKeyType : breeze.AutoGeneratedKeyType.Identity,
"dataProperties": [
{
"name": "carriers",
"complexTypeName":"Carrier:#Test",
"isScalar":false
}]
}
The Carrier entity is defined as below:
{
"shortName": "Carrier",
"namespace": "Test",
"isComplexType": true,
"dataProperties": [
{
"name": "Testing",
"isScalar":true,
"dataType": "String"
}
]
}
We have the following matching data for the above entities:
{
carriers: [
{
Testing : 'InputBox1'
},
{
Testing : 'InputBox2'
}
]
}
We are trying to dynamically add the complextype object(Carrier) to the above carriers array by using the following approach:
var test = {
"Testing" : "Test"
};
var result = manager.createEntity('Carrier', test);
The above code throws an exception(undefined is not a function) inside breeze.debug.js at line number 12457(see below code)
entity = entityType.createEntity(initialValues);
The exception is thrown since the complextype entity does not have 'createEntity' function in it.
What are we missing here?
Excellent question - Sorry I didn't have a chance to address this earlier.
When adding a complexType object you need to use the createInstance() method instead of the createEntity.
var thisEntityType = manager.metadataStore.getEntityType('Carrier');
var thisEntity = thisEntityType.createInstance(initialValues);
Basically you get the complexType and then create an instance of it using the values you want assigned. Keep in mind the initial values should be a hash object of course. Often I will include a helper function to do this for me like this -
function createComplexType(entityType, constructorProperties) {
var thisEntityType = manager.metadataStore.getEntityType(entityType);
var thisEntity = thisEntityType.createInstance(constructorProperties);
return thisEntity;
}

Resources