Angular.js : Dynamic length of columns in table - angularjs

I am new to Angular and have been trying to make a dynamic table.
The user enters the no of columns he wants and then he can add as many rows to each column as he wants.
I have provided an "add" button at the end of each column to add new row to that particular column.
My code looks somewhat like this:
<table>
<tr>
<th ng-repeat="x in setno"> SET {{x.number}}</th>
</tr>
<tr ng-repeat="z in selected">
<td> </td>
</tr>
<tr>
<td ng-repeat="x in setno">
<button ng-click="selected.push({})"> add </button>
</td>
</tr>
</table>
where, setno is list containing numbers:
$scope.setno[{id:"a", number:"1"},{id:"b", number:"2"},...];
and selected has a similar structure.
The problem is that when I click "add" button of any column, a new row gets add to all the columns.
Whereas I want, a new row to be added to only the one whose "add" button has been clicked.
How can I know whose "add" button has been clicked?

How can i know whose "add" button has been clicked?
You need access to the current $index of the target repeater. Now I'm not sure which it is you're trying to target but you can store an ng-repeater's $index as another variable for access from inner repeaters using the following syntax:
<!-- outer loop -->
<div ng-repeat="(idxA, item) in items)">
<!-- inner loop -->
<div ng-repeat"(idxB, obj) in item.objs">
<ul>
<!-- another inner loop -->
<li ng-repeat="li in obj.pts">
In the above code idxA will be the same as the outer loop's $index, idxB is the inner loop's $index and so on. Then you just pass the index (e.g. idxA or idxB) to your method.
resources:
https://stackoverflow.com/a/18914958/1121919
https://docs.angularjs.org/api/ng/directive/ngRepeat

Related

Accessing the first column of first row of a table in Protractor

<tr ng-repeat="a in people">
<td>
{{a.firstname}}
</td>
<td>
{{a.lastname}}
</td>
<td>
{{a.email}}
</td>
</tr>
protractor code
var firstname = element.all(by.repeater('a in people'));
expect(firstname.get(0).getText()).toEqual('george');
This returns the entire first row. How do I get the first column? Thanks
You could get it like this:
var all = element.all(by.repeater('a in people')).all(by.css('td'));
expect(all.get(0).getText()).toEqual('george');
element.all(by.repeater('a in people')); it will find just one element. If on the page will be two elements with this repeater text it will find two. But you need make search by td also.
Technically when ng-repeat executes, it will repeat the element into the dom, In your case < tr ng-repeat='a in people'> tag will be repeated. So element.all(by.repeater('a in people')) results in multiple rows, hence get(0) method will return first row.
We can use following code to get first column of the first row.
var firstname = element(by.repeater('a in people').row(0).column('a.firstname'));
expect(firstname.getText()).toEqual('xyz');

How to fill table with many editable rows in AngularJs

In my AngularJS website, I would like to show a table containing adresses that should be editable in the cells. This is how the columns look like:
Id, Label, Address, Geolocation, Additional Information
The Address column contains an input element, so that I can search for another address to change the data.
The table has sometimes more than 200 rows. I am experiencing massive performance problems when loading the table the first time and also when resizing the div in which the table is.
My approach so far:
Table HTML:
<div>
<table>
<tbody>
<tr table-row
address="address"
ng-repeat="address in data">
</tr>
</tbody>
</table>
</div>
I came up with a custom directive "table-row" which contains the td elements for a single row. It also has its own scope under which the address can be changed.
Table-row HTML:
<td>
{{ address.id }}
</td>
<td>
{{ address.label }}
</td>
<td>
<input type="text"
ng-model="addresstext"/>
</td>
...
Some thoughts on how this could be done better:
The main problem with my code is that the table is getting pretty big and I keep creating a scope for every single row. I suppose that the performance is going to increase if there would just be a single scope. However, I do not see how to do this without an ng-repeat to create the rows.
Isn't there a way to have one scope for the table and still a dynamic number of rows in the table, each being editable with the input textboxes in the cells?

Angularjs: How to sort array of arrays by nth value?

I have two groups of arrays.
First contains header fields =>
$scope.fields=[field0, field1, field2,...,field20];
Second has multiple arrays of data with respect to above field =>
$scope.data=[
[A,B,C,...,someVal20],
[Q,W,E,...,someVal20],
[R,T,Y,...,someVal20],
...
[B,N,M,...,someVal20]
];
Values of fields are shown in a drop down
<select ng-model="filteredOption" ng-options="option.label for option in fields"></select>
The same field values are used as table header.
<tr>
<th ng-repeat="f in fields">{{f.label}}</th>
</tr>
And the data as
<tr ng-repeat="d in data">
<td ng-repeat="i in d track by $index">{{i}}</td>
</tr>
Question is
How do I sort (ascending) the data based on the 'nth' field (which user selects from drop down)?
Its confusing because header and data are two distinct arrays, and data is not in key,value format.
Plunkr here
Column Header - ColumnName in you case it is field0
<th>
<a href="#" ng-click="sortType = 'field0'; sortReverse = !sortReverse">
field0
</a>
</th>
On Column header click it sets the properties of sortType and sortReverse
<tr ng-repeat="d in data | orderBy:sortType:sortReverse"">
<td ng-repeat="i in d track by $index">{{i}}</td>
</tr>
useful link for sorting https://scotch.io/tutorials/sort-and-filter-a-table-using-angular
Thanks to this answer, I've found working solution.
I tracked the selected field's position ($scope.sorter set to that $index). And used it as orderBy:sorter
Still, keeping this question open for others to suggest more acceptable solution.

add table data <td> only to one row on ng-repeat

in a table with ng-repeat is it possible to add a cell only to one row?
in my code:
<tbody>
<tr ng-repeat="user in users ng-click="selectUser(user)">
<td>{{user.username}}</td>
<td><input type="text"....></td>
<td><input type="checkbox"...></td>
<td><input type="submit" ... ng-show="user==selectedUser" /></td>
</tr>
</tbody>
in this code I want the last td appears only on the selected row and does not affect other rows, is it possible? or it is JS or CSS thing ?
First off you should be using the controller as syntax, it automatically puts everything in the controller under 1 object, which can cause issues with Angular. But I don't think that's the issue here.
The user you select could be equal to the selectedUser, but if they aren't pointing to the same reference, they won't be able. If usernames are distinct I'd change the ng-show="user.username == selectedUser.username"
and that should work fine.
It is possible, it seems like your code is mostly correct, but you're using selectedUser as a function and as an object representation of user. Maybe your function would be called selectUser which would set $scope.selectedUser. ng-show="user == selectedUser" would make since then.
I'm personally not a big fan of having conditions in the view, so I'd have a function in the controller which does the comparison and returns true or false.
function isSelectedUser(user) {
return user == $scope.selectedUser;
}
then you can just use ng-show="isSelectedUser(user)"
Use JQuery to append the <td> on the selected row <tr>. The :nth-child() is an easy way for you to select a row.
var selectedRow = 2;
$('tbody tr:nth-child('+ selectedRow +')').append('<td><input type="submit" /></td>');

Why is my ng-if in td having unexpected results?

Maybe I am completely confused how td works as I haven't used tables in my code for yeeaars (strong div believer), or maybe I am confused on how ng-if is attempting to separate my data.
Either way, I am not getting my expected results, but there is otherwise nothing wrong with the code, I think my logic is just off.
Here is a working Plunker http://plnkr.co/edit/LoMEMBYc6kUodb9tcbfM?p=preview
<table>
<tr ng-repeat="z in waiverModalLinks">
<td ng-if="z.id=='color'" class="{{z.id}}">{{z.title}}</td>
<td ng-if="z.id==''" class="{{z.id}}">{{z.title}}</td>
</tr>
</table>
The ultimate end result is to have every other td a different color, however I realize that the current logic is going to put the colored td on the left and the non-colored td on the right. But that was the object I was working with before I realized I needed two separate columns, finding the need for a table in the first place.
But anyway, what is not working then is that.. well they are still stacked. I would expect to see the colored td on the left-hand side of the table and the non-colored td on the right-hand side of the table.
Where's the err in my logic?
Your data model seems to be the source of the confusion. You have a simple array, so each td will be in the same row. If you create a 2d array, you can make a new row for each inner array:
$scope.waiverModalLinks =[
[
{title:"1", link:"", id:"color"},
{title:"2", link:"", id:""}
],
[
{title:"3", link:"", id:"color"},
{title:"4", link:"", id:""}
]
];
And a simpler repeater:
<tr ng-repeat="row in waiverModalLinks">
<td ng-repeat="z in row" class="{{z.id}}">
{{z.title}}
</td>
</tr>
Plunkr: http://plnkr.co/edit/VeXBJcf3naI5bRttQ1Pt?p=preview
z.id is either equal to 'color', or an empty string, but can't be both at the same time. So every row will have at most one cell:
if z.id is equal to color, the generated dom will be
<tr ng-repeat="z in waiverModalLinks">
<td class="{{z.id}}">{{z.title}}</td>
<!-- removed second td -->
</tr>
if z.id is equal to ''', the generated dom will be
<tr ng-repeat="z in waiverModalLinks">
<!-- removed first td -->
<td class="{{z.id}}">{{z.title}}</td>
</tr>
You need two cells in every row: one blank and one filled, or vice-versa:
<table>
<tr ng-repeat="z in waiverModalLinks">
<td ng-if="z.id==''"></td>
<td class="{{z.id}}">{{z.title}}</td>
<td ng-if="z.id=='color'"></td>
</tr>
</table>

Resources