I forked a normalization example from Kato's fiddle and updated it to the current version of AngularFire and Firebase, you can see it here. I tested around a bit to get a better understanding of how $firebaseObject and $firebaseArray work and I really get a hang of it.
Except the toJSON from $firebaseObject.
So I have this little code in the controller:
$scope.singlePost = singleMergedPost('post2');
console.log('singlePost', $scope.singlePost);
The output normally is:
{
"postData": {
"bla": "blubb",
"dateCreated": 1397584465,
"title": "Another cool website",
"upvotes": 1,
"url": "http://www.google.com",
"user": "simplelogin:2"
},
"userData": {
"email": "kato#firebase.com",
"jobTitle": "Awesome Dude",
"name": "Kato Richardson"
}
}
But when I add toJSON to $extend the output is:
{
"bla": "blubb",
"dateCreated": 1397584465,
"title": "Another cool website",
"upvotes": 1,
"url": "http://www.google.com",
"user": "simplelogin:2"
}
$scope.singlePost actually contains the same data, but I am wondering about:
Why is toJSON even called here, although I haven't sent back any data to the server (at least it looks like from my point of view) yet.
Why does <pre ng-bind="singlePost | json"></pre> only show the postData data?
1. toJSON
.toJSON() is called to strip the properties and methods of a $firebaseObject or $firebaseArray that start with $ or $$. It serializes the object to valid JSON data.
This is done to display the data in the <pre> div for debugging, without the $ methods.
See this answer from Kato.
2. Difference in data
<pre ng-bind="singlePost | json"></pre> only shows the postData because only the postData property of this (the $firebaseObject) is passed to the .toJSON() method in the line $firebaseUtils.toJSON(this.postData);
Compared to passing the whole object (including postData and userData properties) in console.log('singlePost', $scope.singlePost);
If you log $scope.singlePost.postData, you should see the same result.
Related
I'm building a ReactJS web application, I have a JSON array:
[{
"2149166938": {
"name": "Classical-42",
"icon": "/common/destiny2_content/icons/e6885dab15cafeaf0dbf80f1ff9b5eb8.jpg"
},
"2149166939": {
"name": "Countess SA/2",
"icon": "/common/destiny2_content/icons/de8906743db9fce201b33802c8498943.jpg"
},
"2154059444": {
"name": "The Long Goodbye",
"icon": "/common/destiny2_content/icons/fe07633a2ee87f0c00d5b0c0f3838a7d.jpg"
}
}]
When I make a call to an API I am returned a value of lets say 2154059444 which is the same value as the last object in my JSON file.
How would I iterate through the JSON file to find the object which has a value of 2154059444 and then access and render it's child values like name or icon?
you can do something like this. Your array is not proper please edit.
Create filtered data :
//here i am addding single dummy point you can make your function
l
et filteredData = [].concat(data.map(test => {
if(Object.keys(test)[0]==="2154059444"){
return test["2154059444"]
}
})).filter(Boolean)
and simply render it app like this .
{ filteredData.map(test => <div>{test.name}</div>)}
Here is live link
This is simple. JSON is equivalent to Javascript object. So you can do something like result[0]["2154059444"]. You might want to use JSON.parse for converting the JSON string to Javascript object.
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.
I keep running into some confusing solutions and unclear ways to wrap items that match into a div using backbone.
I am just building a simple example for myself, and would like to nest all models in a collection that have the same attribute team, using a comparator works well in organizing the list, but for the life of me I can't find a clear solution to wrapping each so that I have more control over the list of players inside the team.
There has to be a clear easy solution for a beginner like me. I really just want to keep things as clean and simple as possible. My desired html result looks like below.
<div class="pacers">
<li>Paul</li>
<li>Roy</li>
</div>
<div class="bulls">
<li>Kirk</li>
<li>Taj</li>
</div>
Based on a backbone friendly json array like below.
[
{
"name": "Paul",
"team": "pacers"
},
{
"name": "Kirk",
"team": "bulls"
},
{
"firstname": "George",
"team": "pacers"
},
{
"name": "Taj",
"team": "bulls"
}
]
So using a comparator is awesome I just write this comparator : 'team' and it handles the list order for me, cool, but I dont have much control I would like to wrap the list in a more hierarchical system.
Another approach:
If you are using underscore's templates this could be one way of doing it. You can use underscore's groupBy function to group the list based on teams.
var teams = [
{
"name": "Paul",
"team": "pacers"
},
{
"name": "Kirk",
"team": "bulls"
},
{
"firstname": "George",
"team": "pacers"
},
{
"name": "Taj",
"team": "bulls"
}
];
var groupedList = _.groupBy(list, function(l){
return l.team;
});
console.log(JSON.stringify(groupedList));
This is how it would be grouped.
{
"pacers": [
{
"name": "Paul",
"team": "pacers"
},
{
"firstname": "George",
"team": "pacers"
}
],
"bulls": [
{
"name": "Kirk",
"team": "bulls"
},
{
"name": "Taj",
"team": "bulls"
}
]
}
You can then use for each loop and in template and generate HTML in following way. The groupedList is passed as teams to below template.
<%
_.each(teams, function(team, teamName){
%>
<div class="<%=teamName%>">
<%
_.each(team, function(player){
%>
<li><%=player.name%></li>
<%
});
%>
</div>
<%
});
%>
This would generate the HTML the way you expected.
NOTE:
The code snippets are given considering underscore templating, you might have to make changes based on what you use. Hope it helps.
Correct me if I am wrong the problem being described relates more to controlling the contents of each item in relation to it's model as well as how to simply render them in groups.
1) Niranjan has covered grouping out the data into separate lists but remember that this list returned is not a Backbone construct.
2) As per the manual the '_.groupBy' method should be available to you via the collection i.e.:
myCollection.groupBy(etc);
3) I would personally consider mapping the results of the groupBy back into models and pass each and every model into a separate view and render them from within the main list view.
var CollectionView = Backbone.View.extend({
initialize : function () {
// Note: I am pretending that you have a real collection.
this.collection.fetch().then(
this.addAll(true);
);
}
addOne : function (model) {
// call .render individual template items here for each model.
var view = new ItemView(model);
this.$el.append(view.render();
},
addAll : function (groupOpts) {
var col = this.collection;
if(groupOpts === true) {
// Do grouping (or do it in the model). Maybe put back into new collection?
}
_.each(col, function(model) {
this.addOne(model);
}, this);
},
render : function () {
// Render your template here.
}
});
var ItemView = Backbone.View.extend({
render : function () {
}
});
Not a complete example but that's the general pattern I would follow when attempting the same thing. Having an individual view/model for each item, in my opinion, gives you more control.
This could be handled in a pretty crazy view template (depends on your template language)... or you could use a simpler template/view and just make some more crazy collection queries (first using a pluck to get the team, de-dupping that array, then running some where's for each of the teams... but you can see how this gets crazy)
I'd vote for the view and view template should handle this... what are you using? Jade? Mustache?
Something like this - logical psuedo code here since I don't know your template language:
var team;
forEach player in players
if(!team) {
set team = player.team
print open holder and then the first row
} (team !== player.team {
set team = player.team
print close of previous holder, then open holder and then the first row of new team
} else {
print just the player row
}
Even so, you can see how this is a bit dirty in and of itself... but what you are describing is a view/presentation concern, and you can do it here with no new additional loops and maps and wheres (like you'd have to do if you did it in the data layer before calling the views)
I'm very new to AngularJS. This is my first foray into any sort of JS, so I apologize if I've butchered anything.
Here's a brief overview of what I'm trying to do.
The user inputs a term to search, and I get the information from Twitter. The tweets are displayed with an ng-repeat (making my issue MORE frustrating). The user checks which tweets he/she would like to add to a "cart". Ipush tweets which are selected onto my service's array ("shoppingCart" inside the cart service).
I then click "Add to cart" to transfer to a new page (results.html) where I would like to display what the user selected.
Note: For our purposes here, I've skipped this and hardcoded the results.
This is where it all breaks down. I can console print the array to make sure the service is received by the next controller, but I can't get ng-repeat to work.
I have absolutely no clue what I've messed up here. I'm still very new to Angular so it's likely I've made a dumb mistake. I've tried to read through a docs and have checked out egghead.io videos. Unfortunately I just can't put two and two together.
Here is the service with the array hardcoded:
myApp.service('cart', function Cart($rootScope) {
var self = this;
var shoppingCart = [{
"created_at": "Wed, 17 Apr 2013 17:52:42 +0000",
"from_user": "LopezCarlosB",
"from_user_id": 244705286,
"from_user_id_str": "244705286",
"from_user_name": "carlos lopez",
"geo": null,
"id": 324581196224938000,
"id_str": "324581196224937984",
"iso_language_code": "en",
"metadata": {
"result_type": "recent"
},
"source": "",
"text": "#Medicencaballo #ponnyloca AHHH AHH LESLEEEEEY ASD SIGYGIS",
"to_user": "Medicencaballo",
"to_user_id": 202598647,
"to_user_id_str": "202598647",
"to_user_name": "Axel Manrique",
"in_reply_to_status_id": 324580938027765760,
"in_reply_to_status_id_str": "324580938027765760"
}, {
"created_at": "Wed, 17 Apr 2013 17:52:35 +0000",
"from_user": "WhatNowTweeting",
"from_user_id": 383266399,
"from_user_id_str": "383266399",
"from_user_name": "Herman",
"geo": null,
"id": 324581166764130300,
"id_str": "324581166764130305",
"iso_language_code": "en",
"metadata": {
"result_type": "recent"
},
"source": "",
"text": "Life on the spectrum: 5 books about autism and Asperger's - Mother Nature Network #autism #asperger #pddnos #pdd #asd"
}, {
"created_at": "Wed, 17 Apr 2013 17:52:32 +0000",
"from_user": "Elchinitooooo",
"from_user_id": 903231720,
"from_user_id_str": "903231720",
"from_user_name": "ChinoCochino..^^",
"geo": null,
"id": 324581154667782140,
"id_str": "324581154667782147",
"iso_language_code": "es",
"metadata": {
"result_type": "recent"
},
"source": "",
"text": "MaƱana viene mi amor :3 asd asd"
}];
self.add = function (item) {
shoppingCart.push(item);
}
self.count = function () {
console.log(shoppingCart);
return shoppingCart.length;
}
self.log = function () {
return shoppingCart;
}
});
Here is the controller for the view below:
myApp.controller('ResultsCtrl',
function ResultsCtrl($scope, $resource, cart) {
$scope.cart = cart;
$scope.shoppingCart = cart.shoppingCart;
});
Here is the html for that view:
<div>
<table>{{cart.log()}}
<br>
<ul>
<li ng-repeat="item in shoppingCart">{{item.text}}</li>
</ul>
</table>
</div>
I'm not getting any errors in Firebug, so I'm imagining I'm not referencing the array correctly, but have been unable to figure out how.
Thank you very much for any assistance you might be able to offer.
It looks like you're trying to accessing the shoppingCart var directly from your controller with the line
$scope.shoppingCart = cart.shoppingCart;
However, shoppingCart isn't attached to self/this, effectively making it private, so I don't think that type of access is possible. The log method in your service returns the shoppingCart var, so maybe it'll help if you change that above line to:
$scope.shoppingCart = cart.log();
I'm not exactly sure if that will work using the .service method, so if it fails you may want to look into using .factory instead, which does basically the same as .service but allows you to return an object that will be the service rather than this.
I am trying to pull in a collection from the url attribute and am having some problems. It seems fetch() returns successfully, but then I cannot access the models in my collection with get(). I am using bbb and requireJS to develop my modules
var rooms = new Rooms.Collection(); // calls the rooms module
rooms.fetch({success: function(){
console.log(rooms.get(1)); // should output the first model
});
Here is my collection code in the rooms module:
Rooms.Collection = Backbone.Collection.extend({
model: Rooms.Model,
url: 'http://localhost:8888/projects/meeting-room/app/data/rooms.json'
});
If I output rooms, everything turns out fine. But when I try for a specific model, that is when I get an error.
[{
"id": 12345,
"name": "Ford",
"occupied": false
},
{
"id": 23458,
"name": "Chevy",
"occupied": false
},
{
"id": 83565,
"name": "Honda",
"occupied": false
}]
The collection.get method looks up a model by id. If you want to find a model by position, use collection.at instead.
Also notice that array indices in javascript are 0-based, so the first model can be found with:
var model = collection.at(0);
For convenience, Backbone collections also implement some of underscore's array and collection methods, including first. That means you can also find the first model with:
var model = collection.first();