Trouble with updating object properties in AngularJs - angularjs

I am building my first app in AngularJs.
Here is the plunkr with what I've done so far. The user should be able to add new websites and group them in groups. Groups are also made by the user. Any time the new group is created it is available for new websites. What app should also do is to update group objects with newly assigned websites... and this is where I fail.
Here is how json should look like:
{
"sites": [
{
"url": "http://sfdg",
"id": 0,
"groups": [
{
"name": "adsf",
"id": 0
}
]
}
],
"groups": [
{
"name": "adsf",
"id": 0,
"sites": [//sites assigned
]
}
]
}
In the plunkr code I used push but that just adds new group...
Could you please direct me to the right way of achieving this.
Thanks!

To prevent circular references (a website object refers to a group object that refers to the website object, etc...), I would store id's to the relevant objects instead.
First, when creating a new group, add an empty sites array to it:
function createGroup(newGroup) {
newGroup.sites = []; // <-- add empty array of sites
$scope.groups.push(newGroup);
newGroup.id = groupIdSeq;
groupMap[newGroup.id] = newGroup;
groupIdSeq++;
return newGroup;
}
Then, when you create a new site, update each group to which the site is added:
function createSite(newSite, groups) {
$scope.sites.push(newSite);
newSite.id = siteIdSeq;
sitesMap[newSite.id] = newSite;
// instead of storing the groups array, only store their id:
newSite.groups = groups.map(function(group) { return group.id });
// and add this new sites id to the groups' sites array.
groups.forEach(function(group) {
group.sites.push(newSite.id);
});
siteIdSeq++;
return newSite;
}
(updated plunker here)

Related

Javascript looping through an object

I have an object which is given back through my REST API and I need to iterate through it for synchronizing a DB. So the object contains another object called tables. The tables object has different arrays with table names and their key value pairs.
I could not loop through the tables object about two days whatever I did and it is really annoying getting null or undefined values back.
For example I tried iterating through the table array with the JavaScript function object.forEach((article)=>console.log(article.id,article.name));
const obj = response.content.tables.article;
function findArticles(obj) {
obj.forEach((article)=>console.log(article.id,article.name));
}
I can't get any value back. When I try to console.log(response.content); it shows me everything. As soon as I try to output response.content.tables it says undefined.
This is the structure of the object response.content:
{
"status": "1",
"message": "sync out request successfull",
"tables": {
"article": [
{
"id": 1,
"name": "baseball"
},
{
"id": 2,
"name": "truck"
},
],
"food": [],
"animals: []
}
}
Try converting the response to an object using JSON.parse(xyz) before attempting to get the properties.
var xyz = '{ "status": "1", "message": "sync out request successfull", "tables": { "article": [{"id": 1,"name": "baseball"},{"id": 2,"name": "truck"}],"food": [],"animals": []}}'
var obj = JSON.parse(xyz);
$(obj.tables).each(function (ix, el) {
console.log(el)
});
I solved it like this:
var obj = response.content;
var JSON = JSON.parse(obj);
var articleTable = JSON.tables.article;
articleTable.forEach((article)=>console.log(article.id,article.name));
After I parsed the response.content object to JSON it was available to access the nested objects as 'tables' and 'article'. After passing the article object with the articleTable variable to the forEach it has been possible to access each elements. Now I get results.
I really appreciate your help
T3.0 it wasn't able to solve the problem without you.

Why *ngFor in Angular shows the new object first from the array of objects which is having .subscribe() method on in Ionic 3

I am new to Ionic 3 & Angular and ran into an issue with *ngFor and .subscribe() method. Please forgive me if it is an easy question. I tried to figure out the behaviour of http.get(..).map(..).subscribe() function when used along with *ngFor to maintain an array of objects and show the records to user using *ngFor on the .html template file but unable to know *ngFor's odd behaviour.
In my Ionic 3 App, I am getting the data using an API and storing it inside a component variable called "users" which is declared as below in component .ts file -
users: Array<any>;
I have below component function which gives me data for this users array -
addUser(count: number) {
this.http.get('https://randomuser.me/api/?results=' + count)
.map(data => data.json().results)
.subscribe(result => {
for (let val of result) {
this.users.push(val);
}
})
}
Initially, I get data for 10 users from the API using above component function by simply calling it inside my ngAfterViewInit() function like -
this.addUser(10);
This gives me 10 user record objects inside my users array which I show to the user using something like below in the view .html file -
<ion-card *ngFor="let user of users">
{{user.email}}
</ion-card>
At this time *ngFor puts the last array element at first in the view and shows the records in the descending order as the elements in the array starting from index 9 to 0.(LIFO order)
Now I start popping the last element from this users array using users.pop(); and push another element at the beginning at index 0 by shifting current elements using users.unshift(..) in below function and calling it as addNewUser(1); -
addNewUser(count: number) {
this.http.get('https://randomuser.me/api/?results=' + count)
.map(data => data.json().results)
.subscribe(result => {
for (let val of result) {
this.users.unshift(val);
}
})
}
At this moment, if we consider the first array which had 10 elements, the last object at index 9 had been removed and another element at index 0 has been added making the previous elements on index 0-8 to shift to index 1-9.
On doing so, my view gets updated which has *ngFor and surprisingly this time it shows the first element at first place which is actually on index 0 which is the one I recently put. This is opposite to the order earlier followed by *ngFor to render elements on the screen.
Why *ngFor in Ionic 3 view shows the recently inserted object element first from the array of objects which is dependent on the subscribe method .subscribe() method. I am really confused about this.
I really need to clear the concept of *ngFor and subscribe(). Please help me.
Note : The API mentioned above is publicly accessible for testing and you may call it to check the response structure if required.
Pasting a sample API response below on calling https://randomuser.me/api/?results=1 -
{
"results": [
{
"gender": "male",
"name": {
"title": "mr",
"first": "daniel",
"last": "stoll"
},
"location": {
"street": "9719 tannenweg",
"city": "cottbus/chosebuz",
"state": "bremen",
"postcode": 81443
},
"email": "daniel.stoll#example.com",
"login": {
"username": "greenleopard994",
"password": "chat",
"salt": "OUjisBdQ",
"md5": "8148d51998f3fef835a5f3979218c181",
"sha1": "131ae09d045b345efe36a330bf17a450b76f7de3",
"sha256": "94c3a362b5f516d0fb1d4e9dbb632d32d57b8886d5cc7bf0d5cedc99e7d55219"
},
"dob": "1957-04-26 22:07:14",
"registered": "2002-04-29 10:57:34",
"phone": "0556-4348870",
"cell": "0172-5116200",
"id": {
"name": "",
"value": null
},
"picture": {
"large": "https://randomuser.me/api/portraits/men/14.jpg",
"medium": "https://randomuser.me/api/portraits/med/men/14.jpg",
"thumbnail": "https://randomuser.me/api/portraits/thumb/men/14.jpg"
},
"nat": "DE"
}
],
"info": {
"seed": "8fd4afe85884c767",
"results": 1,
"page": 1,
"version": "1.1"
}
}
Refer this example showing my issue.
If you have a sorting issue with indexing and you think it's related.. you could work around the issue by assigning an index:
*ngFor="let user of users; let i = index"
and then reference the direct index value
users[i]
You should make a copy of that array. Editing array elements while looping them can lead to unexpected behaviour.

Query data from firebase Array

My firebase users tree has this structure:
users:
{
{
'userName': 'abc',
'userEmail' : 'abc#abc.com',
'userPreferences':
[
0:'Cinema',
1:'It'
]
},
{
'userName': 'abc',
'userEmail' : 'abc#abc.com',
'userPreferences':
[
0:'Cinema',
1:'Music'
]
}
}
Then, I try to find all users that their preference list contain 'Cinema'.
I try this code:
var ref1 = new Firebase("https://event-application.firebaseio.com/users");
$scope.user = $firebaseArray(ref1.orderByChild("userpreferences").equalTo('Cinema'));
console.log($scope.user);
But I don't get the best result. I get this record:
Your JSON structure shows preferences as userPreferences, so wouldn't the following work?
var ref1 = new Firebase("https://event-application.firebaseio.com/users");
$scope.user = $firebaseArray(ref1.orderByChild("userPreferences").equalTo('Cinema'));
console.log($scope.user);
However I think there is also another problem with your code, you're called an .equalTo('Cinema') however you're comparing it to an array, correct me if i'm wrong but I don't think the behaviour of .equalTo('Cinema') is to loop through each of the values and compare them, I think it's just a straight up comparison
If this is the case, you may need to build a custom query by reading the data from firebase and manipulating it via function available to a snapshot
In NoSQL you'll often end up with a data model that reflects the way your application uses the data. If you want to read all the users that have a preference for Cinema, you should model that in your tree:
users: {
'uid-of-abc': {
'userName': 'abc',
'userEmail' : 'abc#abc.com',
'userPreferences': [
0:'Cinema',
1:'It'
]
},
'uid-of-def': {
'userName': 'def',
'userEmail' : 'abc#abc.com',
'userPreferences': [
0:'Cinema',
1:'Music'
]
}
},
"preferences-lookup": {
"Cinema": {
"uid-of-abc": true,
"uid-of-def": true
},
"It": {
"uid-of-abc": true
},
"Music": {
"uid-of-def": true
}
}
Now you can find out what users prefer cinema with:
ref.child('preferences-lookup/Cinema').on('value', function(snapshot) {
snapshot.forEach(function(userKey) {
console.log(userKey.key()+' prefers Cinema');
});
});
This is covered in this blog post on denormalizing data with Firebase, in the Firebase documentation on structuring data and in dozens of answers here on Stack Overflow. A few:
Storing Relational "Type" or "Category" Data in Firebase Without the Need to Update Multiple Locations
Get Firebase items belonging to category
Retrieve data based on categories in Firebase
How to query firebase for property with specific value inside all children

Creating an array from GeoJSON file in OpenLayers 3

I am using OpenLayers 3 to animate the paths of migrating animals tagged by scientists. I load the geoJSON file like so
var whaleSource = new ol.source.Vector({
url: 'data/BW2205005.json',
format: new ol.format.GeoJSON()
});
Instead of loading this directly into a layer, I would like to use and reuse the data in the geoJSON file for different purposes throughout my program. For example, I want to pull the lat & lon coordinates into an array to manipulate them to create interpolated animated tracks. Later I will want to query the geoJSON properties to restyle the tracks of males and females.
How might I load the geoJSON data into various arrays at different stages of my program instead of directly into a layer?
Thanks much
When using the url property of ol.source.Vector the class loads the given url via XHR/AJAX for you:
Setting this option instructs the source to use an XHR loader (see ol.featureloader.xhr) and an ol.loadingstrategy.all for a one-off download of all features from that URL.
You could load the file yourself using XHR/AJAX using XMLHttpRequest or a library like jquery which has XHR/AJAX functionality. When you've retreived the GeoJSON collection you can loop over the features array it holds and split it up into what every you need and put those features into new separate GeoJSON collections. Here's a very crude example to give you and idea of the concept:
Assume the following GeoJSON collection:
{
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [0, 0]
},
"properties": {
"name": "Free Willy"
}
}, {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [1, 1]
},
"properties": {
"name": "Moby Dick"
}
}, {
// Etc.
}]
}
Here's how to load it (using jQuery's $.getJSON XHR function) and to split it up in to separate collections:
// Object to store separated collections
var whales = {};
// Load url and execute handler function
$.getJSON('collection.json', function (data) {
// Iterate the features of the collection
data.features.forEach(function (feature) {
// Check there is a whale already with that name
if (!whales.hasOwnProperty(feature.properties.name)) {
// No there isn't create empty collection
whales[feature.properties.name] = {
"type": "FeatureCollection",
"features": []
};
}
// Add the feature to the collection
whales[feature.properties.name].features.push(feature);
});
});
Now you can use the separate collections stored in the whale object to create layers. Note this differs some from using the url property:
new ol.layer.Vector({
source: new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(whales['Free Willy'], {
featureProjection: 'EPSG:3857'
})
})
});
Here's a working example of the concept: http://plnkr.co/edit/rGwhI9vpu8ZYfAWvBZZr?p=preview
Edit after comment:
If you want all the coordinates for Willy:
// Empty array to store coordinates
var willysCoordinates = [];
// Iterate over Willy's features
whales['Free Willy'].features.forEach(function (feature) {
willysCoordinates.push(feature.geometry.coordinates);
});
Now willysCoordinates holds a nested array of coordinates: [[0, 0],[2, 2]]

Querying Firebase based on time

I am trying to query my Firebase based on time limits. I am following this blog post with this attached jsFiddle.
The issue is that I am getting a blank firebaseArray back.
var currentTime = (new Date).getTime();
var twentyFoursHoursAgo = currentTime - 86400000;
//scoresRef is defined as new Firebase(http://myfirebase.firebaseio.com/scores)
scoresRef.on('value', function (dataSnapshot) {
var summaryScores = $firebaseArray(scoresRef.orderByChild('timestamp').startAt(twentyFoursHoursAgo).endAt(currentTime));
$scope.summaryScores = summaryScores;
console.log(summaryScores);
}
The idea is that as users add more scores, the array will change. Then I can do different data manipulation on it (like average, etc). That way, there can be a running 24 hour average displayed on the app.
This is what the data looks like in Firebase:
What am I doing wrong? I know the data is in there.
Not sure if this answer your question, but it seems the best I can do is show you something that works.
Querying for a range of timestamps
I added this data structure:
{
"-Jy5pXbn5RpiK1-5z07O": {
"timestamp": 1441076226561
},
"-Jy5pZJsYvsmv_dMtCtn": {
"timestamp": 1441076173543
},
"-Jy5paWbkU6F8C6CEGpj": {
"timestamp": 1441076181550
},
"-Jy5pbc0pJ1I5azenAi5": {
"timestamp": 1441076247056
},
"-Jy5pfnMDKExW2oPf-D-": {
"timestamp": 1441076204166
},
"-Jy5pgk55ypuG9_xICq-": {
"timestamp": 1441076268053
},
"-Jy5phVgU2hDE_izcR8p": {
"timestamp": 1441076271163
},
"-Jy5pilBteGhu05eMWQI": {
"timestamp": 1441076215315
}
}
And then query with this code:
var ref = new Firebase('https://stackoverflow.firebaseio.com/32321406');
var startAt = 1441076265715;
var endAt = startAt + 15000;
var query = ref.orderByChild('timestamp')
.startAt(startAt)
.endAt(endAt);
query.on('value', function(snapshot) {
console.log(snapshot.val());
});
Which outputs:
{
-Jy5pgk55ypuG9_xICq-: {
timestamp: 1441076268053
},
-Jy5phVgU2hDE_izcR8p: {
timestamp: 1441076271163
}
}
And a warning that I should add an indexing rule for timestamp.
jsbin: http://jsbin.com/qaxosisuha/edit?js,console
Binding a collection of data from Firebase to AngularJS
If you're trying to bind the query results to an AngularJS view, you do this by:
$scope.items = $firebaseArray(query);
When using AngularFire don't try to use console.log to monitor what is going in. Instead add this to your HTML:
<pre>{{ items | json }}</pre>
This will print the items and automatically update as the data is asynchronously loaded and updated.
Note that this may be a good time to go through Firebase's AngularFire programming guide, which explains this last bit and many more topics in a pretty easy to follow manner.

Resources