Based on the data below, and assuming there are more cars and more firebase user id's associated with specific cars. How would I ng-repeat only the car data that matches the current user. Example: if I was signed in and my Firebase.uid was simplelogin:26, how would I ng-repeat the car data that had my Firebase.uid within it? So in this case it would only display the Honda.
I have read that the way you organize Firebase data is really important so I'm not sure if this is formatted ideally, either way I would like to know if this is possible the way it's currently formatted. I still learning all this and can't seem to figure this out. Any insight would be great, thank you!
html
<h1>MyCars</h1>
<ul>
<li ng-repeat="car in cars">
<p>Make: {{ car.type }}</p>
<p>Year: {{ car.year }}</p>
<p>color: {{ car.color }}</p>
</li>
</ul>
JSON
"cars": {
"-JRHTHaIs-jNPLXOQivY": {
"type": "Honda",
"year": "2008",
"color":"red",
"simplelogin:26": {
"name":"ted"
},
"simplelogin:32": {
"name":"ted"
}
},
"-JRHTHaKuITFIhnj02kE": {
"type": "Chevy",
"year": "2006",
"color":"white",
"simplelogin:16": {
"name":"ted"
}
}
}
This is not an ideal data structure. If cars indeed have a 1:1 relationship with users, as the data suggests, then one should simply store them by user and then query that specific user id:
{
"cars": {
"ted": {
...
}
}
}
Now querying cars by user is extremely simple:
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
$scope.cars = $firebaseArray(ref.child('cars/<USER ID>'));
If cars cannot be split by user, because they have an n:1 relationship, then a query can provide the same functionality (make sure you index them on the server):
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
var query = ref.child('cars').orderByChild('name').equalTo('ted');
$scope.cars = $firebaseArray(query);
If you want to have an n:n relationship, then indexing users to cars is more appropriate:
"cars": {
"-JRHTHaIs-jNPLXOQivY": {
"type": "Honda",
"year": "2008",
"color":"red"
},
...
},
"owners": {
"ted": {
"-JRHTHaIs-jNPLXOQivY": true,
...
}
}
Fetching cars for a given user is now a bit more difficult, but still not unreasonable:
angular.factory('CachedCarList', function() {
// a simple cache of Firebase objects looked up by key
// in this case, a list of cars that have an n:n relationship to users
var carsRef = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/cars");
var carsLoaded = {};
return {
get: function(carId) {
if( !carsLoaded.hasOwnProperty(carId) ) {
carsLoaded[cardId] = $firebaseObject(carsRef.child(carId));
}
return carsLoaded[carId];
},
destroy: function(carId) {
angular.forEach(carsLoaded, function(car) {
car.$destroy();
});
carsLoaded = {};
}
}
});
angular.factory('CarList', function($firebaseArray, CachedCarList) {
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
var CarsIndexed = $firebaseArray.$extend({
'$$added': function(snapshot) {
// when something is added to the index, synchronize the actual car data
// we use the $loaded promise because returning it here will make AngularFire
// wait for that data to load before triggering added events and Angular's compiler
return CachedCarList.get(snapshot.key()).$loaded();
},
'$$updated': function(snapshot) {
return false; // our cars update themselves, nothing to do here
}
});
return function(userId) {
// when a list of cars is requested for a specific user, we return an CarsIndexed
// than synchronizes on the index, and then loads specific cars by referencing their
// data individually
return new CarsIndexed(ref.child('owners/'+userId));
}
});
And firebase-util's NormalizedCollection can help make this process much simpler:
angular.factory('CarList', function($firebaseArray) {
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
return function(userId) {
var nc new Firebase.util.NormalizedCollection(
ref.child('owners/' + userId),
ref.child('cars')
).select('cars.type', 'cars.year', 'cars.color')
return $firebaseArray(nc.ref());
}
});
The Firebase Angular guide covers many topics like this one, and also introduces a bindings library to handle syncing remote/local data on your behalf.
Also, many topics such as data structures, indexing many-to-one or many-to-many relationships, et al are covered in the Firebase docs. I'd highly recommend reading the guide front to back before going any further.
You can iterate over the properties on an object in ng-repeat using the syntax ng-repeat="(key, value) in data"
<ul>
<li ng-repeat="(key, value) in cars">
<p>Make: {{ value.type }}</p>
<p>Year: {{ value.year }}</p>
<p>color: {{ value.color }}</p>
</li>
</ul>
Related
I am new to angular and need help for the below-
I have a json data as follows in my json file:
{
"records":
[
{
"date":1619038000,
"name":"Susan",
"status":"available"
},
{
"date":1419038000,
"name":"Vinay",
"status":"not available"
},
{
"date":1419038000,
"name":"Ajay",
"status":"available"
}
],
"record2":[
{
"date":1419037000,
"name":"Soumya",
"status":"not available"
},
{
"date":1326439320,
"name":"Harsh",
"status":"available"
},
{
"date":1419031000,
"name":"Gopi",
"status":"available"
}
]
}
this is my js file:
angular.module("myApp",[]).controller("Control",function($scope,$http){
$http.get('myData.json').
success(function(data){
$scope.tableData = data.records;
});
});
I want to display data in the table from the json in such a way that all data having the same "date" are grouped together and shown jst one date in the table.Could you pls help me with this.I want it as follows:
date name status
16th april susan available
17th april vinay not available
ajay available
To show the result: GroupBy date, follow the below steps.
STEP 1: Add Moment JS library to your HTML file.
http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js
STEP 2: Update your controller file as below:
var app = angular.module("myApp",[]);
app.controller("Control",function($scope,$http){
$http.get('myData.json').
success(function(data){
$scope.tableData = data.records;
});
});
app.filter('groupBy', function() {
return _.memoize(function(items, field) {
return _.groupBy(items, field);
}
);
});
STEP 3: To render the data add the following code to your HTML file.
<ul>
<li ng-repeat="(date, persons) in tableData | groupBy:'date'">
{{date * 1000 | date:"dd.MM.y"}}
<ul>
<li ng-repeat="person in persons">
{{person.name}}
</li>
</ul>
</li>
</ul>
Codepen Link
You need to use 'ng-repeat'
Here is a tutorial and examples for your guide
https://www.w3schools.com/angular/ng_ng-repeat.asp
For date, here is some guidance
https://www.w3schools.com/angular/ng_filter_date.asp
Suppose that I have an Angular view that allows a user to check books out of a library. My data model consists of two arrays of Book entities which each have a unique ID field plus a title field. The first array contains an entity for every book in the library and the second array contains an entity for every book that the user has checked out.
libraryBooks = [{
id: 0,
title: "The Adventure of Tom Sawyer"}, {
id: 1,
title: "Moby Dick" }, {
id: 2,
title: "To Kill a Mockingbird" }, {
id: 3,
title: "The Three Little Pigs" }];
checkedOutBooks = [{
id: 0,
title: "The Adventure of Tom Sawyer"}, {
id: 3,
title: "The Three Little Pigs" }];
In short, the library has four books and the user has checked out two. If I want to list the books from both arrays, I can write this:
<h1>Library Books</h1>
<div ng-repeat="book in libraryBooks">
{{ book.title }}
</div>
<h1>Checked out Books</h1>
<div ng-repeat="book in checkedOutBooks">
{{ book.title }}
</div>
Suppose I want to display a third list: the subset of library books that the user has not checked out.
I have seen examples where the Angular "filter" is used to specify one particular value that should not be matched in order to narrow down a list, but in this case, I want to exclude multiple values, so how do I go about doing this?
I have seen examples where a custom filter is added to an Angular module, but I think that in this case, any custom filter should be scoped to this controller.
I've got this figured out. The solution is to write a filter function and attach it to $scope like so:
function filter_notCheckedOut(book) {
var i;
for (i = 0; i < that.libraryBooks.length; i += 1) {
if (that.libraryBooks[i].id === page.id) {
return false;
}
}
return true;
}
In the view, it can then be referenced like this:
<h1>Books not checked out</h1>
<div ng-repeat="book in libraryBooks | filter:filter_notCheckedOut">
{{ book.title }}
</div>
Let's say I have the following routes:
{
route: "usersById['length']",
get: function(pathSet) {}
},
{
route: "usersById[{integers:ids}]['firstName', 'lastName']",
get: function(pathSet) {}
}
With the following in my angular1 controller:
Model.get(
'usersById.length',
'usersById[0..2]['firstName', 'lastName']'
).then(function(response) {
$scope.$apply(function() {
vm.entities = response.json.usersById;
});
});
The response from the server is going to look something like:
{
jsonGraph: {
usersById: {
"0": {
firstName: 'Jiminy',
lastName: 'Cricket'
},
"1": {
firstName: 'Jafar',
lastName: 'Husain'
},
"length": 123123
}
}
}
In my angular 1 template, I want to loop through the list of users:
<tr ng-repeat="entity in users.entities">
<td>{{entity.firstName}} {{entity.lastName}}</td>
</tr>
The problem is that there aren't just users in the response, firstly it contains length and secondly it seems other meta data is returned by Model's promise, of which looks to be part of the path data: usersById
What is the preferred way of looping through the list of users? Should I doing something like this in my promise?
vm.entities = response.json.usersById.filter(function(value) {
return typeof value === 'object';
});
I'm not seeing any API call for fetching raw values anywhere.
Ok so it seems the correct way to handle this is to create another route: users which just returns usersById references, that way you have an array of just entities, not containing length etc.
I'm guessing having the path data in the array of data was just a bug.
{
users: {...},
usersById: {...}
}
<li ng-repeat="user in users">{{ user.firstName }}</li>
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.
I have a service that manages a set of data. The service is polite enough to provide options to return a subset of said data based on (whatever logic, in this example it's simply going to look for a specific data attribute value). The service will return an array of matches. In my view, I want to bind to this set of matches. However, because the service returns a new array object each time the filter function is called, that doesn't work. My view is bound to the previously returned array object.
Try this fiddle:
http://jsfiddle.net/FdWeK/1/
var app = angular.module('app', []);
app.factory('MrData', function() {
var allData = [
{name: 'Adam', type: 'boy'},
{name: 'Kassidy', type: 'girl'},
{name: 'Justin', type: 'boy'},
{name: 'Chloe', type: 'cat'},
{name: 'D The P', type: 'dog'},
];
return {
add: function(thing) {
allData.push(thing);
},
fetchAll: function() {
return allData;
},
fetchForType: function(type) {
var some = [];
for (var i = 0; i < allData.length; i++) {
if (allData[i].type == type)
some.push(allData[i]);
}
return some;
}
}
});
app.controller('SomeCtrl', function($scope, MrData) {
$scope.showSome = MrData.fetchForType('boy');
$scope.showAll = MrData.fetchAll();
$scope.addBoy = function() {
MrData.add({name: 'TED!', type: 'boy'});
}
$scope.addOther = function() {
MrData.add({name: 'Other', type: 'Other'});
}
});
and the view:
<div ng-app="app">
<div ng-controller="SomeCtrl">
<button ng-click="addBoy()">Add Boy</button>
<button ng-click="addOther()">Add Other</button>
<h2>Boys</h2>
<ol>
<li ng-repeat="thing in showSome">
{{ thing.type }}
{{ thing.name }}
</li>
</ol>
<h2>All</h2>
<ol>
<li ng-repeat="thing in showAll">
{{ thing.type }}
{{ thing.name }}
</li>
</ol>
</div>
</div>
You can see that the list of boys is not updated when you click Add Boy. And I understand why- but I don't understand how to fix this! Must be a simple design pattern or feature that I just don't know about, or can't figure out on my own.
Thanks you in advance,
Adam
You obviously understand what is broken, that your view is bound to two different lists. However, the problem is that you are filtering the model, when you really should be filtering the view. This way you always stay bound to a single list, and the view manages how that list is presented to the user.
What you should be using is a filter https://docs.angularjs.org/api/ng/filter/filter
For example, this simple ng-repeat should work:
<li ng-repeat="thing in showAll | filter:{type: 'boy'}">
Also updated fiddle
You need to reapply the fiters because the bound arrays have not changed when you call addBoy() or addOther().
One way around this is to call the filter after each call:
function applyFilter(){
$scope.showSome = MrData.fetchForType('boy');
$scope.showAll = MrData.fetchAll();
}
$scope.addBoy = function() {
MrData.add({name: 'TED!', type: 'boy'});
applyFilter();
}
$scope.addOther = function() {
MrData.add({name: 'Other', type: 'Other'});
applyFilter();
}
I would show how it could be done with angular filters but it seems someone already has.