I am trying to repeat a child array of a multidimensional array with ng repeat in Angular.
My json object is this:
$scope.items = [{ "id":1,
"BasisA":"1",
"Basis":true,
"personSex":"m",
"isCollapsed":false,
"name":"Mark Polos",
"age":"1955",
"results":[{"1000":{"company_name":"***","model":"***","modelname":"***","pr":222,"rating":4.5,"priority":9,"matching":1},
"1001":{"company_name":"***","model":"***","modelname":"***","pr":228.7,"rating":5.7,"priority":7,"matching":2},
"1002":{"company_name":"***","model":"***","modelname":"***","pr":241.7,"rating":1.9,"priority":4,"matching":3}
}]
}]
Itried somthing like this:
... data-ng-repeat="item in items">
And then in the table of this child:
<tr data-ng-repeat="i in item | orderBy:'insItem.pr'">
It doesn't look like that results property is actually an "array." If that's just a typo in your example, then disregard. If not ... read on.
It looks like an array with a single item, and that Item is a set of properties which are, in turn, objects. In other words, you would reference the property "pr" for the result named "1000" by with code that looks like item.results[0]["1000"].pr NOT with code that looks the way your ng-repeat is expecting(item.results[0].pr).
Can you transform your items when you get them so that results is a true array?
OR - can you use a function inside of your controller that returns the array you are looking for?
View Code:
<... data-ng-repeat="result in resultsFromItem(item)" >
Controller Code:
$scope.resultsFromItem = function (item) {
if(item==undefined || item.results==undefined || item.results.length==0) {
return [];
}
var myResults = [];
for (var key in item.results[0]) {
if(item.results[0].hasOwnProperty(key)) {
myResults.push(item.results[0][key]);
}
}
return myResults;
}
You might even decide to hang that "transformed" results object off each item object (so you only have to go through the transform one time) if you wanted to.
You should access to the results field:
... data-ng-repeat="item in items">
<tr data-ng-repeat="i in item.results">
Since the nested array is in the results property of the main object.
I used three nested ng-repeat directives to get this rolling :-) The third ng-repeat uses ng-repeat="(key, value) in result" functionality to display all result object keys and values, which I got working with the help of this answer on how to iterate over keys and values in ng-repeat. The orderBy: part isn't yet working (if someone knows how to implement that then any help is welcomed).
<ul>
<li ng-repeat="item in items">
id: {{item.id}}, name: {{item.name}}, age: {{item.age}}, results:
<table>
<tr ng-repeat="result in item.results">
<td>
<table style="border: 1px solid black;">
<tr ng-repeat="(key, value) in result | orderBy: value.pr">
<td> {{key}} </td> <td> {{ value }} </td>
</tr>
</table>
</td>
</table>
</li>
</ul>
Plunker
Related
I want an Angular list showing all the instances of one object property from an array of objects, and nothing more – for example, only the countries.
$scope.testSites = [
{ "site": "Testsite1", "country": "Country1", "customer": "Customer1"},
{ "site": "Testsite2", "country": "Country2", "customer": "Customer2"}
];
$scope.chosenCategory = 1;
$scope.categoryNames = ["site", "country", "customer"];
$scope.aspect = $scope.categoryNames[$scope.chosenCategory];
However, I want to use the above variable 'aspect' for choosing which property to show in the list. Something like {{x.country}}, though it works, is therefore not sufficient. I've tried this, but it returns an empty list:
<table border="1" class="list-group-item list-group-item-success">
<tr>
<th>{{aspect | capitalize}}</th>
</tr>
<tr ng-repeat="x in testSites | orderBy:myOrderBy">
<td>
{{x.aspect}}
</td>
</tr>
</table>
Is there something I can do?
You can do via {{ x[aspect] }} - [] bracket notation. It evaluates the expressoin and uses the result to find the property.
You can find Demo here
I am trying to use the NG-Table library to display data to users in a web-app I'm creating, but it seems that the built in functions are not able to sort data structured like this:
var data = [
{
generalInfo:{
name: "Morty",
age: 20
}
},
{
generalInfo:{
name: "Ricky",
age: 20
}}];
The actual database I am using requires the data to be structured like this, so I can not change the structure. I tried changing my HTML to match this format, but the filter element does not allow dot notation.
My html looks like this:
<div ng-controller="appController as vm">
<table ng-table="vm.tableParams" class="table table-bordered table- striped table-condensed" show-filter="true">
<tr ng-repeat="user in $data">
<td title="'Name'" filter="{ generalFeatures.name: 'text'}" sortable="'generalFeatures.name'">{{user.generalFeatures.name}}</td>
<td title="'Age'" filter="{ generalFeatures.age: 'number'}" sortable="'generalFeatures.age'"> {{user.generalFeatures.age}}</td>
</tr>
</table>
</div>
Is there any way for me to sort data structured this way?
I found a temporary solution to this problem. I could create new properties in the parent object by looping through all of the properties in the nested object and adding them to the parent.
angular.forEach(data, function (user) {
if(user.generalInfo != null){
user.age = user.generalInfo.age;
user.name = user.generalInfo.name;
}
});
I would like to iterate over some data like this:
<table>
<tr ng-repeat="(k,val) in items">
<td>{{k}} {{val.style}}</td>
<td ng-repeat="(k2, item) in val.items">{{item.title}}</td>
<td>{{item.ingredients}}</td> <-- (a)
<td>{{item.moreInfo}}</td> <-- (b)
</tr>
</table>
(a) and (b) [and c, d, e...] would also use the object "item in val.items",
but {{item.ingredients}} is not a valid expression there, because it is out of the <td> with the object that I want to use to create more columns.
Example of what it would look like: http://jsfiddle.net/yj7xopgy/
Is there any way to do something like that?
Use ng-repeat-start and ng-repeat-end.
<td ng-repeat-start="(k2, item) in val.items">{{item.title}}</td>
<td>{{item.ingredients}}</td>
<td ng-repeat-end>{{item.moreInfo}}</td>
Updated Fiddle
I have two <tr>s and an ng-repeat on each, but both perform the same operation on their child elements, like so:
<tr ng-repeat="item in data : filterFunc" ng-if="mode === 'something'">
<td>{{item.name}}</td>
</tr>
<tr ng-repeat="item in data : filterFunc" ng-if="mode === 'somethingelse'">
<td>{{item.name}}</td>
</tr>
Imagine, there are two types of data sets and which one is to be rendered is decided by the mode property at run time. So, either the first <tr> is rendered in the DOM or the other one.
Initially the first <tr> will be rendered and the associated filterFunc function will work properly. There's a simple drop down which has two options, one each for the two modes. If you select the other mode, the second <tr> will be rendered and first one will be removed from the DOM.
The problem is, the filterFunc now is bound to both the <tr>s and operates on both of them.
How do I unbind the scope or watchers for the first one let it be bound only to the second one? or any one of them at any point of time? Here's the fiddle http://jsfiddle.net/6kx4ojL4/
Note: For the sake of simplicity, I have simply returned the passed-in data object as-is in filterFunc. Check the output in browser console. It gets called twice, i.e. for both data sets.
It is not clear what you want to do exactly from your description. My first thought is that you could separate your modes in ng-if, say, use mode1 and mode2, so you can control two <tr>s separately..
Use ng-show instead of ng-if
<tr ng-repeat="item in data : orderBy: 'name'" ng-show="mode === 'something'">
<td>{{item.name}}</td>
</tr>
<tr ng-repeat="item in data : orderBy: 'name'" ng-show="mode === 'somethingelse'">
<td>{{item.name}}</td>
</tr>
How about this. Check this JSFiddle: http://jsfiddle.net/vxcjw45d/1/
If it's not what are you looking for tell me - I will delete it :)
HTML:
<body ng-app="myApp">
<table ng-controller="myController">
<tr ng-repeat="item in data | orderBy: 'name'"
ng-if="mode === 'something'">
<td>{{ item.name }}</td>
</tr>
<tr ng-repeat="item in data | orderBy: 'name'"
ng-if="mode === 'somethingelse'">
<td>{{ item.age }}</td>
</tr>
<tr>
<td>
<button ng-click="changeMode()">
Change Mode
</button>
</td>
</tr>
</table>
</body>
JS:
var myApp = angular.module('myApp', []);
myApp.controller('myController', function($scope) {
$scope.data = [
{ name: 'John', age: 21 },
{ name: 'Doe', age: 33 }
];
$scope.mode = 'something';
$scope.changeMode = function() {
if ($scope.mode === 'something') {
$scope.mode = 'somethingelse';
} else if ($scope.mode === 'somethingelse') {
$scope.mode = 'something'
}
};
});
I have an object that contains a mixture of numbers and text for values. I'd like to apply the numbers filter to the object's value when it's a number (obviously). But when it isn't a number, I'd be okay with it just spitting out the string. As is, applying | number to the value formats the numbers, but leaves the string values empty (afterall, they aren't numbers).
I'm guessing it'll have to be a custom filter (which I have yet had a need to make). Is there a way to do it solely within the HTML when doing the ng-repeat?
<table>
<tr ng-repeat="(metric, metricData) in data">
<td>{{metric}}</td>
<td>{{metricData | number}}</td>
</tr>
</table>
$scope.data = { name:"this is the name",
score:48
outcome:"as expected",
attendance:820,
total:212.34
};
Here is the requested alternate version of the answer from #callmekatootie using ng-if (v1.1.5):
<table>
<tr ng-repeat="(metric, metricData) in data">
<td>{{metric}}</td>
<td ng-if="isNumber(metricData)">{{metricData | number}}</td>
<td ng-if="!isNumber(metricData)">{{metricData}}</td>
</tr>
</table>
This has the advantage of only running the filter on the elements which are numeric. This is probably of little benefit in this case but may be useful in other more complex filter situations. To answer your other question about the built-in angular.isNumber, #callmekatootie does use that in the scope function isNumber, which is only a wrapper for using the built-in in the view.
Here is a fiddle
You could try it this way - In your controller, you can have a function which identifies if the provided value is a string or a number:
$scope.isNumber = function (value) {
return angular.isNumber(value);
};
Next, in your view you could have the following:
<table>
<tr ng-repeat="(metric, metricData) in data">
<td>{{metric}}</td>
<td ng-show="isNumber(metricData)">{{metricData | number}}</td>
<td ng-hide="isNumber(metricData)">{{metricData}}</td>
</tr>
</table>
Thus, when the metricData is a number, it is filtered and when it is a string, it is output as it is.
I know this is old, but I think the best solution is to move the logic to a filter.
app.filter("metricDataFilter", function($filter) {
return function(value) {
if(angular.isNumber(value)) {
return $filter("number", value);
}
return value;
}
}
That way the HTML is more concise, and angular won't have to redraw dom elements
<table>
<tr ng-repeat="(metric, metricData) in data">
<td>{{metric}}</td>
<td>{{metricData | metricDataFilter}}</td>
</tr>
</table>