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

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>');

Related

How to make a table sort-able while the whole components is passed as string via ng-bind-html in AngluarJS

Hi I have a situation in AngluarJS that the HTML is generated by back-end and the only thing that front-end should do is to put the HTML which is mostly table tags into the ng-bind-html and show it to the user. But now these tables should be sort-able too. How can I do it?
The thing that I've already done is to create my own directive using this so make the static string HTML take some actions too. But having them sorted is something else. In other word I want to make my fully generated table with all <tr> and <td> to get sorted by my actions.
Here is my simplified code (compile is my directive):
JS:
// The string is fully generated by back-end
$scope.data.html =
'<table> <tr> <th ng-click="sortByHeader($event)"> Name </th>
<th ng-click="sortByHeader($event)"> Age </th> </tr>
<tr> <td> Sara </td> <td> 15 </td> </tr>
<tr> <td> David </td> <td> 20 </td> </tr>'
HTML:
<div compile="data.html"></div>
The ng-click="sortByHeader($event) is something that back-end can prepare for me so I can use it thanks to the compile I wrote that let me find out which header has been clicked. Other than that there is nothing I can do. Unless you can help me :D
Thanks in advance, I hope my question was clear.
Since you tagged your question with sorttable.js I'm going to assume that you are using that script to sort your tables.
Now, if I understand it correctly, sorttable.js parses your HTML for any tables with the class sortable. Your table is apparently loaded dynamically, therefore sorttable.js does not know about it when it parses the HTML.
But you can tell it to make a dynamically added table sortable, too.
Relevant part taken from the following page:
https://kryogenix.org/code/browser/sorttable/#ajaxtables
Sorting a table added after page load
Once you've added a new table to the page at runtime (for example, by
doing an Ajax request to get the content, or by dynamically creating
it with JavaScript), get a reference to it (possibly with var
newTableObject = document.getElementById(idOfTheTableIJustAdded) or
similar), then do this:
sorttable.makeSortable(newTableObject);
You should be able to do that with angular. If not, I can try to put something together later.
Is the answer to the question "Does the rendered table have to exactly match the HTML retrieved by the backend?" a kind of "No"?
If that's the case, then here's a hacky way of gaining control of the table contents by parsing and capturing stuff from the backend HTML string using regular expressions.
For example: grab all row data and apply sorting client side
// Variables to be set by your sortByHeader functions in order to do client-side sorting
$scope.expression = null;
$scope.direction = null;
var regexToGetTableHead = /<table>\s*(.*<\/th>\s*<\/tr>)/g;
$scope.tableHead = regexToGetTableHead.exec($scope.data.html);
$scope.tableRows = [];
var regexToGetRowContents = /<tr>\s*<td>\s*(\w*)\s*<\/td>\s*<td>\s*(\w*)\s*<\/td>\s*<\/tr>/g;
var match;
while ((match = regexToGetRowContents.exec($scope.data.html)) != null) {
$scope.tableRows.push({
"name": match[1],
"age": match[2]
});
}
And HTML
<table>
<thead compile="tableHead"></thead>
<tbody>
<tr ng-repeat="row in tableRows | orderBy: expression : direction">
<td>{{row.name}}</td>
<td>{{row.age}}</td>
</tr>
</tbody>
</table>

Check if a row already exists before pushing it to table

I have a html table
<table>
<tr ng-repeat="row in rowsproductrequests">
<td>{{row.PRODUCTID}}</td>
<td>{{row.DESCRIPTION}}</td>
</tr>
</table>
a second table for search products :
<table>
<tr ng-repeat="row in searchproductsList">
<td>{{row.PRODUCTID}}</td>
<td>{{row.DESCRIPTION}}</td>
</tr>
</table>
On click I am pushing the item from the search table (Second table) to the main table (first table) :
JS:
$scope.addItemAsIng = function(row){
$scope.rowsproductrequests.push(row);
}
The items are pushed. My problem is how can I check if an item exists in the first table so I can stop pushing the same item twice.
Use includes, for example:
$scope.addItemAsIng = function(row){
if(!$scope.rowsproductrequests.includes(row)) $scope.rowsproductrequests.push(row);
}
An alternative approach
Rather than populate the second table by pushing selected items to a second array, I'd suggest using the same array but send it through a custom filter that identifies which items are selected.
How to do it
1) Create a function in your controller that sets the selected property of a given object to true:
$scope.onAvailableRowClicked = function(row){
row.selected = true;
}
2) Then wire that up to the first table (of available objects) using the ng-click directive:
<h4>Available</h4>
<table>
<tr ng-repeat="row in rowsproductrequests"
ng-click="onAvailableRowClicked(row)">
<td>{{row.PRODUCTID}}</td>
<td>{{row.DESCRIPTION}}</td>
</tr>
</table>
3) Now create a custom filter that identifies all of the objects within a given array that have the selected property set to true:
app.filter("isSelectedFilter", function() {
return function(input){
return input.filter(function(obj){
return (obj.selected === true);
});
}
});
4) Finally, use the filter in the ng-repeat of the second table to identify selected records:
<h4>Selected</h4>
<table>
<tr ng-repeat="row in rowsproductrequests | isSelectedFilter">
<td>{{row.PRODUCTID}}</td>
<td>{{row.DESCRIPTION}}</td>
</tr>
</table>
Benefits of this approach
You only have to maintain one array. Less variables means that your code becomes easier to read, you can see more easily what is driving the view. It's also easier to debug if things go wrong.
There's only ever one instance of each object. One source of the truth means that there's no danger of objects falling out of sync ("should I use this or that!?!?"). It also means that it's not physically possible to select an object more than once.
You can easily reuse the selected property in other parts of the view. If you wanted to highlight in the available list which objects had been selected, you could do so easily using ng-class for example. (The thought of using array comparison logic here is already giving me a headache!)
Demo
CodePen: A custom filter to identify selected records
Approach One: One solution will be once you add the row from the second table, remove that item from the collection and add to the first table that,s how you will never get duplicate items.
Second Approach: you will have to maintain an identity column in both tables based on these identity columns you need to check before adding whether this item exists.
Also you can use "indexOf()", it will return array index of your current item, if not exit it will return -1
Like
if($scope.rowsproductrequests.indexOf(row) != -1)

bind different value to ng-model on some condition

I have started using Angular and don't have much experience in it.
I am stuck with an issue. I am using ng-show and ng-model.
its like
<tr ng-show="responseValid">
<td> <input id="nameId" ng-model="model.name"/> </td>
</tr>
I am pre-populating the value in model.name.
Now if response is valid the input tag is shown otherwise not.
but when I submit the form, nameId value is bind by ng-model="model.name"
Issue here is I want model.name should not contain any value if response is not Valid i.e when input tag is hidden. but its not happening.
How can I nullify/empty the value in model.name? Is there anything available that I can use in the tag itself?
You should use ng-if instead of ng-show I believe.
ng-if removes elements from the DOM while ng-show only sets display:none.
When your send your model, check the state of the validResponse and set your model data based in this
example:
$scope.sendModel = function(){
$scope.model.name = $scope.validResponse? $scope.model.name : '';
}
A more advanced example http://codepen.io/gpincheiraa/pen/mPzmxO
You can use ngSwitch or ngIf.
<tr ng-if="responseValid">
<td> <input id="nameId" ng-model="model.name"/> </td>
</tr>
If the condition is not met, angular will completely remove the DOM element, till the condition will meet.

How to find out if input is valid without form in AngularJS

I've faced following issue:
I have a <table>, where <tr>'s a generated via ng-repeat, and each <tr> contains several <input> elements. Smth like this:
<table>
<tr ng-repeat="plan in plans">
<td>
<input ng-pattern="/^\d+((\.|\,)\d+)?$/" ng-model="plan.field1" ng-blur="updateRow(plan)">
</td>
<td>
<input ng-pattern="/^\d+((\.|\,)\d+)?$/" ng-model="plan.field2" ng-blur="updateRow(plan)">
</td>
</tr>
</table>
When user finishes editing input I want to update full row. But I want to do it only if this input is valid. I mean I want to execute updateRow(plan) only if this condition ng-pattern="/^\d+((\.|\,)\d+)?$/" is satisfied. Or maybe somehow check it within updateRow(). But I can't find a way to do it without forms.
1)Is there a way to do it? Or may be there is better way to implement my idea?
2)And also is there way to bind ng-blur to each input in a row? Because I have about 20 inputs in a row and it looks bad when there is such amount of repeating.
Thanks to everybody in advance!
So I solved the first question by using forms and ng-form. I put every tr element in separate tbody and applied ng-form to each tbody element.
So i believe that I have to use forms if I need validation.

Show &nbsp if value empty

I need to show a non-breaking space in a table cell if a value is empty. This is my template:
<td class="licnum">{{participant.LicenseNumber}}</td>
I've tried this, but it doesn't work:
<td class="licnum">{{participant.LicenseNumber} || "$nbsp;"}</td>
Here's the problem with it returning null values:
If License Number comes over with null value, the cell is empty and the row coloring looks like this.
Using lucuma's suggestion, it shows this:
After changing the if statement in the filter, still doesn't show non-null values:
What you have should work. You are missing a closing } and have one in the middle of the expression that needs to be removed.
Here is a demo showing your solution working and an ng-if. http://plnkr.co/edit/UR7cLbi6GeqVYZOauAHZ?p=info
A filter is probably the route to go, but you could do it with a simple ng-if or ng-show (either on the td or even on a span):
<td class="licnum" ng-if="participant.LicenseNumber">{{participant.LicenseNumber}}</td>
<td class="licnum" ng-if="!participant.LicenseNumber"> </td>
or
<td class="licnum"><span ng-if="participant.LicenseNumber">{{participant.LicenseNumber}}</span><span ng-if="!participant.LicenseNumber"> </span></td>
I'm offering this up as an alternate solution that doesn't require adding code to a controller/filter.
You can read up a little about this method: if else statement in AngularJS templates
angular.module('myApp',[])
.filter('chkEmpty',function(){
return function(input){
if(angular.isString(input) && !(angular.equals(input,null) || angular.equals(input,'')))
return input;
else
return ' ';
};
});
Just as #Donal said in order for this to work you'll need to use ngSanitize's directive ng-bind-html like this:
<td class="licnum" ng-bind-html="participant.LicenseNumber | chkEmpty"></td>
EDIT:
Here's a simple example: http://jsfiddle.net/mikeeconroy/TpPpB/

Resources