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
Related
I have a database of arrays that I want to unpack and insert into a table.
For example I'll have an array called a=[1,2,3,4] and b=['a','b','c','d'] each with and equal length.
And I will have a table with just the headers
a
b
My generated array would be
[[1,'a'],[2,'b'],[3,'c'],[4,'d']] created with the zip function from the underscore package.
My goal is to iterate over this array and generate the following
a
b
1
'a'
2
'b'
3
'c'
4
'd'
At the moment, I have a
function returnIt(){
let _ = require('underscore')
//returns [[1,'a'],[2,'b'],[3,'c'],[4,'d']] so x[0] = 1 and x[1] = 'a'
for (var x of _.zip([1,2,3,4],['a','b','c','d'])){
return (
<>
<td>
{x[0]}
</td>
<td>
{x[1]}
</td>
</>
)
}
return(
<table>
<thead>
<tr>
<th>a</th>
<th>b</th>
</tr>
</thead>
<tbody>
<tr>
{returnIt()}
</tr>
</tbody>
</table>
)
But this doesn't work. I get
As you can see I only get one row, the code does not produce more than one row! Sorry about the headers, I tried changing my program as much as I could to suit your eyes.
Anyways, how come this is my result and what can I change?
In returnIt, your for loop returns in the code block so it will only run once and return the first pair of array elements you've transformed to html elements. Try returning a mapping of the zipped elements to markup fragments. Then you’ll see them all.
Here's a complete example:
import _ from "underscore";
export default function App() {
return(
<table>
<thead>
<tr>
<th>a</th>
<th>b</th>
</tr>
</thead>
<tbody>
{returnIt()}
</tbody>
</table>)
}
function returnIt(){
//returns [[1,'a'],[2,'b'],[3,'c'],[4,'d']] so x[0] = 1 and x[1] = 'a'
let zipped = _.zip([1,2,3,4],['a','b','c','d'])
return zipped.map(pair => {
return (
<tr>
<td>
{pair[0]}
</td>
<td>
{pair[1]}
</td>
</tr>
)
})
}
Assuming this is using React
return (
<>
<td>
{x[0]}
</td>
<td>
{x[1]}
</td>
</>
)
<td> should be direct descendant of <tr>
The problem is you're returning from the loop body. So it'll only ever get to the first element.
for (anything) {
return ...
}
will always return immediately without continuing the loop, no matter what is between the parentheses.
You need to have the loop build up the HTML and return all the rows at once, or else maybe use something like a generator function and add a loop to the place that calls the function.
Assuming you're using some framework that lets you just return unquoted HTML like that, the problem with the table part of your return value is probably that you can't have a <div> between a <tr> and one of its enclosed <td>s.
I have a dynamically created table containing some redundant place holders that I would like to remove, they look like this:
{firm[i][j]} //i,j are numbers
I am trying regular expression in JavaScript but it doesn't work, here is my regular expression, table below is a string which will be inserted into DOM.
var table = "
<table class='table table-sm' style='margin:auto;'>
<thead>
<tr>
<th colspan='5'>QARELEASE</th>
</tr>
</thead>
<tbody>
<tr style='text-align:left;'>
<td width='25%;'>{firm[i][j]}</td>
<td width='25%;'>{firm[i][j]}</td>
<td width='25%;'>{firm[i][j]}</td>
<td width='25%;'>{firm[i][j]}</td>
</tr>
</tbody>
</table>"
regular expression:
table = table.replace(/{firm[\d{1}][\d{1}]}/g, "");
Not quite sure why it couldn't work
If you are trying to match [] too, you will need to escape them:
table = table.replace(/{firm\[\d{1}\]\[\d{1}\]}/g, "");
another direct approach is to return from capturing groups like below:
var str = '{firm[123][12]}';
str.replace(/\{firm\[(\d+)\]\[(\d+)\]\}/g,"$1,$2") //will return 123,12
str.replace(/\{firm\[(\d+)\]\[(\d+)\]\}/g,"{foo $1-$2}") // will return {foo 123,12}
//and so on ....
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
The documentation is not quite clear on this, or I may not be understanding how to implement this in the HTML, but how do you handle a template in tempojs that has an empty list/array of items coming from JSON output? Is there a template directive that can be used to display something when the data list is empty (i.e. like the else empty in normal conditional code)?
Here's an example:
Javascript:
$(function() {
/*var data = [
{id:'1',name:'Test One',coordinates:'12.0012,-122.92'}
];*/
var data = [];
Tempo.prepare('userLocs').render(data);
});
HTML:
...
<tbody id="userLocs">
<tr data-template>
<td>{{name}}</td>
<td>{{coordinates}}</td>
<td>Delete</td>
</tr>
<tr data-template-fallback>
<td colspan="3">Javascript is not available.</td>
</tr>
</tbody>
...
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>