AngularJS - Trouble with ng-repeat - javascript

Here's the data I'm working with:
$scope.products = [
{
'id': 643,
'name': 'Product Name',
'applications': [
{
'id': 100,
'name': 'Adobe After Effects CC (2014)',
'gfx': [
{
'id': 514,
'name': 'Graphics AE Test 1'
}
]
},
{
'id': 101,
'name': 'Adobe Premiere Pro CC (2014)',
'gfx': [
{
'id': 514,
'name': 'Graphics AP Test 1'
},
{
'id': 512,
'name': 'Graphics AP Test 2'
}
]
}
]
}
];
What i'm trying to do is loop through all of the graphics card for a specific application. You choose the application via a dropdown, and I have the selected application in a variable {{ scope.selectedApplication }}. So with that, here's what I have tried:
<tr data-ng-repeat="driver in result.applications | filter: { name: selectedApplication } track by $index">
<td>{{ driver.gfx[$index].name }}</td>
</tr>
So this is somewhat working, just not exactly how I want. The filter is filtering it down to the correct application, which works fine. The problem I am having is that driver.gfx[$index].name is only showing the first result. Since I am looping through applications instead of gfx, the $index isn't going to work for me.
How can I loop through the graphics cards after my initial ng-repeat? It seems like I need two statements, but how would that work?
Am I going about this the wrong way?

you can have nested hg-repeat if you like to have a nested table.
<tr data-ng-repeat="driver in result.applications | filter: { name: selectedApplication } track by $index">
<td>
<table>
<tr ng-repeat="item in driver.gfx">
<td >
{{item.name}}
<td/>
</tr>
</table>
</td>
</tr>
if you want to have single denormalized table, one option would be to create a function that does the denormalization and then use result of that function in a normal hg-repeat.
The other option would be having multiple tbody. so your outer loop occurs on tbody and inner loop on row
<tbody data-ng-repeat="driver in result.applications | filter: { name: selectedApplication } track by $index">
<tr ng-repeat="item in driver.gfx">
<td >
{{item.name}}
<td/>
</tr>
</tbody>
and finally you can have some rows that either style them as separators or just hide them by CSS and use ng-repeat-start and hg-repeat-end like this
<table>
<tr class="separator" data-ng-repeat-start="driver in result.applications | filter: { name: selectedApplication } track by $index"><td></td></tr>
<tr ng-repeat="item in driver.gfx">
<td >
{{item.name}}
<td/>
</tr>
<tr class="seperator" ng-repeat-end><td></td></tr>
</table>

You need another ng-repeat that loops through each gfx.
<td>
<ul>
<li ng-repeat="gfx in driver.gfx">{{ gfx.name }}</li>
</ul>
</td>

Assuming the selected application is set via a binding of {{ selectedApplication }} ($scope is implied), then your ng-repeat should look like:
<tr data-ng-repeat="driver in selectedApplication | filter: { name: selectedApplication } track by $index">
<td>{{ driver.gfx[$index].name }}</td>
</tr>
This means you'll be talking about the driver inside the selected application object.

Related

Pass through bi-dimensional array with ng-repeat and $index

Having this input:
myArray = [
{name: "name1", id: 1, parameters: ["first", "second"]},
{name: "name2", id: 2, parameters: ["first"]},
{name: "name3", id: 3, parameters: ["first", "second"]},
];
I want to put it on a table to look similar to this:
I tried to do it, the first two columns are correct but the last one doesn't appear:
<tr ng-repeat="rows in $ctrl.name track by $index">
<td>{{$ctrl.name[$index]}}</td>
<td>{{$ctrl.id[$index]}}</td>
<td><span ng-repeat="rows in $ctrl.parameters[$index] track by $secondIndex">{{$ctrl.parameters[$index][$secondIndex]}}</span></td>
</tr>
The error message is
Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use
'track by' expression to specify unique keys.
So probably two ng-repeats aren't the solution but it don't know how to do this. Any ideas?
This should also work:
<tr ng-repeat="item in myArray">
<td>{{item.name}}</td>
<td>{{item.id}}</td>
<td>
<span ng-repeat="subItem in item.parameters">{{subItem}}</span>
</td>
</tr>
Well, you should use ng-repeat on myArray and then show whatever you want.
And to show the parameters in the desired format you can use row.parameters.toString().replace(',', ' ')
Please check the working snipppet
Thanks
var app = angular.module('myApp', []);
app.controller('ctrl', function($scope){
$scope.myArray = [
{name: "name1", id: 1, parameters: ["first", "second"]},
{name: "name2", id: 2, parameters: ["first"]},
{name: "name3", id: 3, parameters: ["first", "second"]},
];
})
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
}
td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #dddddd;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="ctrl">
<table >
<tr>
<th>Name</th>
<th>Id</th>
<th>Parameters</th>
</tr>
<tr ng-repeat="row in myArray">
<td>{{row.name}}</td>
<td>{{row.id}}</td>
<td>{{ row.parameters.join(' ') }}</td>
</tr>
</table>
</div>
The solution suggested by Aleksey Solovey solved the problem:
<tr ng-repeat="rows in $ctrl.name track by $index">
<td>{{$ctrl.name[$index]}}</td>
<td>{{$ctrl.id[$index]}}</td>
<td><span ng-repeat="rows in $ctrl.parameters[$index] track by $index">{{$ctrl.parameters[$parent.$index][$index]}}</span></td>
</tr>
Use this
<tr ng-repeat="rows in myArray track by $index">
<td>{{ rows.name }}</td>
<td>{{ rows.id }}</td>
<td><span ng-repeat="data in rows.parameters track by $index">{{ data+ " " }}</span></td>
</tr>

Remove a row on ng-click using angularJs

I have a table with the default empty row(Row further includes a columns with different functionalities).Dynamically a multiple rows can be added by click of a button as per the requirement.i want to remove a row when i click a remove-icon from the same-row(remove-icon included in one of the column).
<td class="text-right"> <a href="" class="remove-service" ng-click="vm.removeService(service,true)">
<img src="images/delete.png"></a>  </td>
i have a above column to be included in all rows.whenever i click on remove-icon from any of the rows.(i want to remove a particular row from which icon is clicked)
vm.removeService = function (service, removeService) {
}
Here,is the ng-repeat code for the row.
<tr class="bx--table-row" ng-repeat="service in vm.servicesWithCountries track by $index">
vm.servicesWithCountries = [{ serviceName:"", countries: []}];
Hi you can do like below : Assuming your ng-repeat object will be like somewhat mention below.
<table>
<tr ng-repeat="item in items | orderBy: 'id'">
<td>
<span>
Hello, {{item.name}}!
</span>
</td>
<td>
Delete
</td>
</tr>
</table>
and in Controller :
$scope.items = [{
name: 'item 1',
id: '4'
}, {
name: 'item 2',
id: '3'
}, {
name: 'item 3',
id: '2'
}, {
name: 'item 4',
id: '1'
}];
$scope.delete = function(item) {
$scope.items.splice($scope.items.indexOf(item), 1);
}
refer this fiddle for complete code
You can use splice and indexOf on your service
<tr class="bx--table-row" ng-repeat="service in vm.servicesWithCountries track by $index">
<td class="text-right"> <a href="" class="remove-service"
ng-click="vm.removeService(service, true)">
<img src="images/delete.png"></a>  </td>
</tr>
Like:
ng-click="vm.removeService(service,true)"
JS
vm.removeService = function(service, removeService) {
if(removeService === true){
var index = vm.servicesWithCountries.indexOf(service);
vm.servicesWithCountries.splice(index, 1);
}
}

How to make ng-repeat statement in angular JS run for a limited number of times

I have a table which is being populated like this :
<table>
<tr>
<th>Id</th>
<th>Details</th>
<th>Score</th>
</tr>
<tr ng-repeat="x in data.items">
<td><a href="{{ x.get }}">{{ x.id }}</td>
<td>
<p><b>{{ x.name[0] }}</b></p>
<p><u>Brand</u>: {{ x.Brand }}; <u>Category URL</u>: {{ x.mlnURL }};<u>Price</u>: Rs {{x.Price}} </p>
</td>
<td>
<p><b>{{ x.score }}</b></p>
Classifier Score: {{ x.cscore }} <br>
Document Score: {{ x.sscore }} </p>
</td>
</tr>
</table>
Now in this line
<tr ng-repeat="x in data.items">
instead of running foe all the x in data.items , i want to run it a limited number of times according to how the user wants . I have this number input from the user and stored in a variable . How can i achieve this .
Here you go:
<tr ng-repeat="x in data.items | limitTo: 2">
It will repeat 2 times with this code. You can change the 2 to a var on your scope.
You can utilize the filter limitTo, like the example below:
angular.module('myApp', [])
.controller('myCont', function($scope) {
var theItems = [{
name: 'walkman',
Brand: 'sony',
Price: 20,
explain: 'an AM/FM cassette player',
score: 89,
mlnURL: 'http://sony.com'
}, {
name: 'discman',
Brand: 'sony',
Price: 30,
explain: 'a CD player',
score: 83,
mlnURL: 'http://sony.com'
}, {
name: ['ipod'],
Brand: 'Apple',
Price: 200,
explain: 'an MP3 player',
score: 89,
mlnURL: 'http://apple.com'
},
{
name: ['Flash Player'],
Brand: 'SanDisk',
Price: 40,
explain: 'an MP3 player',
score: 79,
mlnURL: 'http://sandisk.com'
}];
$scope.data = {items: theItems};
$scope.limit = 2;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCont">
# of items to show <input type="number" ng-model="limit"/>
<table>
<tr>
<th>Id</th>
<th>Details</th>
<th>Score</th>
</tr>
<tr ng-repeat="x in data.items | limitTo: limit">
<td><a href="{{ x.get }}">{{ x.id }}</td>
<td>
<p><b>{{ x.name[0] }}</b></p>
<p><u>Brand</u>: {{ x.Brand }}; <u>Category URL</u>: {{ x.mlnURL }};<u>Price</u>: Rs {{x.Price}} </p>
</td>
<td>
<p><b>{{ x.score }}</b>
</p>
Classifier Score: {{ x.cscore }}
<br>Document Score: {{ x.sscore }}
</td>
</tr>
</table>
</div>
You can do it easily by using existing filter.
<input type="text" ng-model="numberOfRecord" />
Then you can use it in ng-repeat.
<tr ng-repeat="x in data.items | limitTo: numberOfRecord">
People usually use combo box to get the number of records per page and further you can use pagination if you want.
Hope your query resolved :)
What you could do, if you have the user input, is create a new array containing all the number of values you want.
In your controller you could do:
var toShow = data.items.slice(0, size);
And just use the toShow array in your template. Every time the size gets updated, you recalculate the toShow array.
You can use limitTo filter in angularJs
Syntax is{{item | limitTo : limit : begin}}
<td ng-repeat="x in data.items | limitTo : userinput">
you can also specify where the list has to get started by using 'begin' as mentioned above
Try this:
<tr ng-repeat="x in data.items | limitTo: 2">

AngularJS: how to build an HTML table with data multiple-levels deep?

I need to create a table from some data with multiple levels of arrays and sub-arrays. I have found some solutions to do this as long as I only have two levels of arrays, but none that would work with anything more than that.
For example, take the sample data below --
$scope.professors = [{
'name': 'Albert Einstein',
'classes': [{
'name': 'Physics 101',
'students': [{
'name': 'Joe',
'grade': 'B'
}, {
'name': 'Mary',
'grade': 'A'
}]
}, {
'name': 'Physics 201',
'students': [{
'name': 'Gunther',
'grade': 'C'
}, {
'name': 'Hans',
'grade': 'C'
}]
}]
}, {
'name': 'Charles Darwin',
'classes': [{
'name': 'Biololgy 101',
'students': [{
'name': 'Danielle',
'grade': 'A'
}, {
'name': 'Anne',
'grade': 'A'
}]
}, {
'name': 'Biology 201',
'students': [{
'name': 'Frida',
'grade': 'A'
}, {
'name': 'Fritz',
'grade': 'F'
}]
}]
}];
You have some professors each with some disciplines each in turn with some students enrolled. I want to create a table-report that show all professors along with all their disciplines and students and grades. In order to do this, I would need a table such as the one below --
<table>
<tbody>
<tr>
<th rowspan="4">Albert Einstein</th>
<th rowspan="2">Physics 101</th>
<td>Joe</td>
<td>B</td>
</tr>
<tr>
<td>Mary</td>
<td>A</td>
</tr>
<tr>
<th rowspan="2">Physics 201</th>
<td>Gunther</td>
<td>C</td>
</tr>
<tr>
<td>Hans</td>
<td>C</td>
</tr>
<tr>
<th rowspan="4">Charles Darwin</th>
<th rowspan="2">Biology 101</th>
<td>Danielle</td>
<td>A</td>
</tr>
<tr>
<td>Anne</td>
<td>A</td>
</tr>
<tr>
<th rowspan="2">Biology 201</th>
<td>Frida</td>
<td>A</td>
</tr>
<tr>
<td>Fritz</td>
<td>F</td>
</tr>
</tbody>
</table>
i.e.
|------------------|-------------|----------|---|
| Albert Einstein | Physics 101 | Joe | B |
| | | Mary | A |
| | ------------|----------|---|
| | Physics 201 | Gunther | C |
| | | Hans | C |
|------------------|-------------|----------|---|
| Charles Darwin | Biology 101 | Danielle | A |
| | | Anne | A |
| |-------------|----------|---|
| | Biology 201 | Frida | A |
| | | Fritz | F |
|------------------|-------------|----------|---|
The solutions I've found generate multiple tbody elements with ng-repeat for each professor (in this case) and then another ng-repeat on a tr for each "class" for that professor. Like --
<table>
<tbody ng-repeat="prof in professors">
<tr ng-repeat="c in prof.classes">
<th ng-if="$first" rowspan="{{prof.classes.length}}">{{prof.name}}</th>
<td>{{c.name}}</td>
</tr>
</tbody>
</table>
Others use ng-repeat-start and ng-repeat-end but none more than two levels deep. Is there a way to add one more level—in this case, the students—to this?
After much frustration and head scratching, I found a way to do it using ng-repeat-start
<table>
<tbody>
<tr ng-repeat-start="p in professors" ng-if="false"></tr>
<tr ng-repeat-start="c in p.classes" ng-if="false"></tr>
<tr ng-repeat="s in c.students">
<th ng-if="$parent.$first && $first" rowspan="{{p.count}}">
{{p.name}}
</th>
<th ng-if="$first" rowspan="{{c.students.length}}">{{c.name}}</th>
<td>{{s.name}}</td>
<td>{{s.grade}}</td>
</tr>
<tr ng-repeat-end ng-if="false"></tr> <!-- classes -->
<tr ng-repeat-end ng-if="false"></tr> <!-- professors -->
</tbody>
</table>
This solution generates valid HTML table markup. By using ng-if="false" on the ng-repeat-* elements, they iterate through the arrays but generate no actual element on the page. The only elements generated are the tr rows in s in c.students.
The other thing I needed to do is create a new property inside the professor objects named count that holds the total number of students enrolled in all the classes. I needed that for the rowspan and it's of course trivial to generate on the fly when I get data from the server.

Nested ng-repeat-start / ng-repeat-end

ng-repeat-start / ng-repeat-end are used for looping through data without rendering parent element [Portal to Docs]. In my case, I want to display data in nested ng-repeat-start / ng-repeat-end blocks without extra elements.
I've tried to put two ng-repeat-start directives into the same element but it fails to display correctly. I somehow figured out a workaround as displayed below [Portal to Demo]. Any better solution for case like this?
data
[{
name: 'Chapter 1',
sections: [{
name: '1-1',
words: 1024
}, {
name: '1-2',
words: 512
}]
}, {
name: 'Chapter 2',
sections: [{
name: '2-1',
words: 2048
}, {
name: '2-2',
words: 256
}]
}]
html
<tr class="tr-for" ng-repeat-start="chapter in main.book"></tr>
<tr ng-repeat-start="section in chapter.sections" ng-repeat-end>
<td>{{chapter.name}}</td>
<td>{{section.name}}</td>
<td>{{section.words}}</td>
</tr>
<tr class="tr-end" ng-repeat-end></tr>
css
tr.tr-for,
tr.tr-end {
display: none;
}
Your internal ng-repeat should be the regular ngRepeat:
<tr class="tr-for" ng-repeat-start="chapter in main.book"></tr>
<tr ng-repeat="section in chapter.sections">
<td>{{chapter.name}}</td>
<td>{{section.name}}</td>
<td>{{section.words}}</td>
</tr>
<tr class="tr-end" ng-repeat-end></tr>
Update
Try use the first ngRepeat inside of tbody tag:
<tbody ng-repeat="chapter in main.book">
<tr ng-repeat-start="section in chapter.sections" ng-repeat-end>
<td>{{chapter.name}}</td>
<td>{{section.name}}</td>
<td>{{section.words}}</td>
</tr>
</tbody>

Categories