Angular loop from the received data from JSON - angularjs

I pass array ImageList to view
res.render('imagelist', { title: 'Imagelist',listImages:imageList });
In view there are this skope (for example):
$scope.planets = [
{
name : 'Mercury',
distance : 0.4,
mass : 0.055
},
{
name : 'Venus',
distance : 0.7,
mass : 0.815
}
]
How I can add data from listImages to the scope in the loop?
Past one varible to scope:
$scope.planets = [
{
name: <%- JSON.stringify(title); %>,
distance: 0.4,
mass: 0.055,
test: 'test'
},
];
But I need to send the array (first code part).
Question: I need to pass array (List) to Angular frontEnd. There I use a table with filtering.
There I use a table with filtering. This table will get data from the scope (See the example above).
But I want to transfer an arbitrary number of rows in a list and display them in a table. How can I do that? I need a cycle in scope, but I do not understand how to write it.
Table code:
<table>
<tr><th>Name</th><th>Average Distance</th><th>Mass</th></tr>
<tr><td><input ng-model="f.name"></td><td><input ng-model="f.distance"></td></td><td><input ng-model="f.mass"></td></tr>
<tr ng-repeat="p in planets | filter:f"><td>{{p.name}}</td><td>{{p.distance}}</td><td>{{p.mass}}</td><td>{{p.test}}</td></tr>
</table>

Related

Rendering information of each child in a list of a denormalized data structure

Let say there is some data of students that are in classes (groups) of different schools:
{
"students": {
"alexa" : {
"firstName" : "ReJina",
"grade" : 2,
"lastName" : "idk",
...
},
"bobby" : { ... },
"chris" : { ... },
...
},
"schools": {
"springfield" : {
"name" : "Springfield Elementary",
"location" : "123 Main St",
...
},
"quahog" : { ... },
"zion" : { ... },
...
},
"group": {
"one" : {
"members" : {
"alexa" : true,
"bobby" : true,
"chris" : true
},
"school" : {
"springfield" : true
},
"name" : "Math Class"
},
"two" : {
"members" : {
"bart" : true,
"lisa" : true,
"meg" : true,
...
},
"school" : {
"quahog" : true
},
"name" : "Art Class"
},
"three" : { ... },
"four" : { ... },
...
}
}
To my knowledge this is the proper way of structuring data in a flat way. Please let me know if this is incorrect. I keep the associated lists on each item, so each class needs a roster (members) and if I also had the situation where a student was in more than one class (group) or school, those would need lists of classes and schools they attend.
Now in the HTML we need to have a view that shows all of the schools classes (groups) and the students in each class and the students in the same view with a nested ng-repeat:
<div id="data-table-div" class="col-md-6">
// Get all the groups, and show them one by one
<div ng-repeat="group in groups">
// This works - How do I get the school it is associated to?
// ??
{{group.name}} - {{group's school's name}}
//Start a table
<table>
<thead> <tr> <th>Student Name</th> <th>Grade</th> ... </thead>
<tbody>
// Get the students in each group, and show them one by one
// ??
<tr ng-repeat="student in groups.members">
<td>{{student.name}}</td> <td>{{student.grade}}</td> ...
</tr>
</tbody>
</table>
</div>
</div>
And in the controller :
angular.module('sofDataViewerApp')
.controller('dataTableController', function ($scope, $firebaseObject, $firebaseArray) {
// This gets all of our groups. It also allows `group.memebers` to render the appropriate number of `<tr>`s as it knows it has N students.
var groupsRef = new Firebase("https://sof-data.firebaseio.com/studyGroups");
$scope.groups = $firebaseArray(groupsRef);
// This will give all the students. How do I get just the template's `group.members` to be each appropriate student?
var studentsRef = new Firebase("https://sof-data.firebaseio.com/students");
$scope.allStudents = $firebaseArray(studentsRef);
});
Question:
How do I get the HTML template to properly iterate over the groups' members and I have access to all of the data of each student (such as their firstName and grade)? (I am assuming it may involve better code in the controller and possibly in the HTML)
PLNKR: http://plnkr.co/edit/uAn1Ky9v5NHHDFPs2c6S?p=preview
(If you know how to inject Firebase and angularFire in the plnkr, then it should work, but I still can't figure that out despite including the CDN of them...)
The closest explanation I can understand is on this page by Firebase, they suggest using a LINK_ID, but it is not described as what they mean, esp since they talk about not searching by id. In my case, a link would be synonymous with group and a comment would be a student.
var commentsRef = new Firebase("https://awesome.firebaseio-demo.com/comments");
var linkRef = new Firebase("https://awesome.firebaseio-demo.com/links");
var linkCommentsRef = linkRef.child(LINK_ID).child("comments");
linkCommentsRef.on("child_added", function(snap) {
commentsRef.child(snap.key()).once("value", function() {
// Render the comment on the link page.
});
});
It looks like you're trying to join multiple "tables". The best way to do that with AngularFire is by extending $firebaseArray (link to docs). With this you can load the additional data from the other locations as each child get added (or is modified).
Also see this answer from Kato that show extending an array (although for a different use-case).
Finally you can consider duplicating the data that is necessary for each screen into the list for that screen. While Firebase is pretty fast to retrieve joined data (it keeps an open connection and can pipeline multiple requests), it will always be faster if you have to do fewer requests to get the data.
An example of this would be if you want to display a list of the members in a group:
"group": {
"one" : {
"members" : {
"alexa" : "ReJina",
"bobby" : "Robert",
"chris" : "Christian"
...
So instead of just storing true, we actually store the first name of each member here. This allows us to get all the data to show the group members with a single read operation:
ref.child('group/one/members').on('value'...
Then when the user clicks on one of the group members, we'd go to that group member's page and load the data from /students/$studentid and possible have the groups that they're part of (and the names of those groups) under there too..

How do perform an "except" filter in Angular?

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>

Angular merge nested arrays

I have a dataset that looks like this:
[
{
'title' : 'cats',
'names' : [
'felix',
'tom',
... more names
]
},
{
'title' : 'dogs',
'names' : [
'fido',
'rover',
... more names
]
},
{
... More animal types
]
And I would like to have the following:
<p ng-repeat='name in names'>{{ name }}</p>
But, to do that I really need to at some stage set
$scope.names = ['felix', 'tom', 'fido', rover'];
My question is: is there an 'Angular' way to merge arrays or take content from multiple places from one object? Or do I need to use a for loop with a concat function to create the array I use?
Sure, just defined names based on your data, demo.
$scope.names = function() {
return Array.prototype.concat.apply([], animals.map(function(animal) {
return animal.names;
}));
};
Then use that method in your view
<p ng-repeat='name in names()'>{{ name }}</p>
Or we could assume the list of animals won't change, and use a library like lodash for readability, demo.
$scope.names = _.chain(animals)
.pluck('names')
.flatten()
.value()
It is not likely to be a core functions for such feature in javascript nor in angular as angular is just mvc framework on javascript. function you need is not common enough for javascript, or specific enough for mvc

Angular ng-repeat with constant & Restangular

I'm working with a weird data model (no way around it at this point). I'm using restangular to make a rest call to get back a single resource object
Normally, the resource object returned by restangular is just whatever I set my
$scope.resource = response to and I can do resource.name , resource.id in the view/template, etc..
Except this group of resources instead of returning the key, value pairs in the response object, it returns an object within an object like so
resource1: {name: 'value', stuff: 'value', etc}
which is fine because then I would just set my $scope.resource = response.resource1 in my controller
except the problem is, is that there's 5 different kind of resource object names so if I make a resource by id call I might get back resource2, resource4, resource1, etc. so setting my $scope.resource = response.resource1 would only work when I get resource1.
My first attempt to solve this was to just use ng-repeat in which I set
<ul ng-repeat="(resource, value) in resource">
<li class="list-group-item">
Name:
<span class="pull-right"> {{ resource.name }} </span>
</li>
</ul>
which works great except because restangular returns all this extra stuff it's looping through each object it's repeating a bunch of blank html stuff if that makes sense.
My other thought was to try making a constant and make an object that has all 5 resources there and my ng-repeat would only populate based off that constant object (ie: it would check for those strings "resource1, resource2, etc" and if it's there then it will populate the template. But I'm not exactly sure how to do this.
Any other options or are there ng-repeat features i'm just not utilizing? any Help thanks
Here's the example I will be working from. Initially your incoming data looks something like this I believe...
$scope.data = [
{
resource1 : { name: 'r1' }
},
{
resource2 : { name: 'r2' }
},
{
resource2 : { name: 'r2' }
}];
When you receive the data you can normalize it by flattening it out into the following structure...
$scope.normalized = [
{ name : 'r1' },
{ name : 'r2' },
{ name : 'r2' }
];
Or you can add a common field for the object "type"
$scope.expanded = [
{
type : 'resource1',
resource1 : { name: 'r1' }
},
{
type : 'resource2',
resource2 : { name: 'r2' }
},
{
type : 'resource2',
resource2 : { name: 'r2' }
}];
Or you can normalize but retain type data...
$scope.normalizedType = [
{ type : 'resource1', name : 'r1' },
{ type : 'resource2', name : 'r2' },
{ type : 'resource2', name : 'r2' }
];
Normalizing upon retrieval of the data is probably your best bet. The question then becomes do you need to retain the objects type information.
So another solution I came up with was put all the resource key names into a list
resources = ['resource1', 'resource2', 'resource3', 'etc..']
and in my restangular service promise I just checked for which resource number it would be with a for loop like this
return ResourceRestangular.all('resource').get(resourceId).then(function(response){
for (i = 0; i < resources.length ; i++){
if (resources[i] in response){
self.resource = response[resources[i]];
}
}
return self.resource;
No need for ng-repeat anymore!

Object double data binding

I'm trying to bind a specific value of an array of objects on an ng-repeat in a table with in-line editing.
The problem that I'm encountering is that my object doesn't have the same value on each ng-repeat.
Example object:
var products = [{
id: 1,
value: 20
},
{
id: 2,
value: {
finalValue: 30,
customValue: 10
}
}];
To my knowledge if I return just the primitive value it won't be binded. I have the property names to get to the specific value on each ng-repeat. For example I have on my first iteration the way to get to the value is just value and the second one has to be value.finalValue.
To my knowledge... to bind the value I can not return just the primitive value 20 or 30, I have to return an object. So my first try was to create a function in my controller that does a property.split('.') and return an array with the object that contains the value and the property to get to it.
Eg) on my first iteration it would return:
[{ id: 1, value: 20 }, 'value']
On the second iteration it would return:
[{ finalValue: 30, customValue: 10 }, 'finalValue']
And now this will be binded to the actual model, but how can I display this on my template?
I can do something like:
<div ng-repeat="product in products>
{{ getValueObj(product) }}
</div>
But how can I display the value itself? Something like results[0][results[1]] ?
Why not on your ngRepeat do something like:
ng-repeat="product in products"
and in your binding do something like:
{{product.value.finalValue || product.value}}
The full example will be something like:
<div ng-repeat="product in products>
{{product.value.finalValue || product.value}}
</div>

Resources