Two way data binding with dynamic table and angularjs? - angularjs

I am creating a table where I will have dynamic row creation. Everytime user clicks a button, a new row is created, as follows:
The second column will contain the amount of products. So, I'd like to sum all values from second column to display them with two-way data binding, using bg-model and ng-bind. Thus when user types in one, the valus will automatically update. I have no clue about it, in that the rows are dynamic!
For a fixed number of elements it works, but I dunno for a dynamic generation. Help?

We need to initially setup an object to use and perhaps put in some example data
$scope.ourTableArray = [];
$scope.ourTableArray.push({'textColumn': 'example text', 'valueColumn': 10});
Because of the way that Angular works with data-binding we can bind the ng-repeat of the table row to an array that will contain all of our values:
<tr ng-repeat="row in ourTableArray">
<td><input type="text" ng-model="row.textColumn"</td>
<td><input type="number" ng-model="row.valueColumn"</td>
</tr>
Next we need a way to add rows, so we can do that by creating a function to call on ng-click from some button. This will populate our $scope.ourTableArray following the format that was used in the first example value that we hardcoded in.
$scope.addRow = function(){
$scope.ourTableArray.push({'textColumn': '', 'valueColumn': 0});
}
Now that the ng-repeat is set up we just need to have a way to calculate the values. The solution to that is by creating a function to sum up our valueColumn from our object:
$scope.calculateTotal = function() {
var count = 0;
for(row in $scope.ourTableArray){
count += $scope.ourTableArray[row].valueColumn;
}
return count;
}
To output our new total all we have to do is call this function within our page.
Current Total of 2nd column: {{calculateTotal()}}
To summarize: Like I had stated above, because we have the ng-repeat binded to our array, anytime we modify the array (either by adding a row, removing a row, editing a rows values) it will be reflected immediately within the table. In addition, since we are outputting the results of our $calculateTotal() function, anytime we modify one of the values within our array it will also change.
Codepen Example

I think you can create a table of objects with ID, name and value and a function that will add new data on scope to it. Then you in your controller you can add function that will simply sum all values and one that will edit object from table by ID.
Next as ng-repeat in this table display it in view.
To all inputs in ng-repeat add function with sum and edit.
I did this in one of my projects but even if it works i don't think that's how it should be done. You can try it if you want.

Related

angularjs bind function call to span without triggering event

I use ng-repeat to populate my table. One of the columns in the table should be dynamically populated again by a different function call.
Below is my code snippet.
<tr ng-repeat="item in ctrl.items">
<td><span ng-bind="item.name"></span></td>
<td><span ng-bind="getItemDetails(item.id)"></span></td>
</tr>
I have array of items. I need to display those items in a table. Item name will be present in the item object, however, item details will be populated by another function call which needs item id.
On using ng-bind (like in the code above) I face 2 issues.
Multiple calls to function even if array has 1 items. Sometimes it goes on thereby freezing my browser and server out of memory issue
The item id doesn't get passed to function always. Sometimes it is undefined.
I am not sure if ng-bind is the right directive to be used. ng-model doesn't work though. Is there any other directive or other way to do it?
How can I achieve this?
EDIT:
Here is the jsfiddle url: https://jsfiddle.net/grubxaur/
If you check browser console, you can see the function is called twice. I guess it is called N no. of times where N is no. of columns in the table.
I have tweaked my implementation a bit to get rid of this issue. Rather than calling a function within ng-repeat, I modified the items array within the controller using angular.forEach before ng-repeat is invoked.
Something like code below.
angular.forEach(self.items, function(item){
item.details = $scope.getItemDetails(item.id);
});

Using smart-table sorting by dynamic map value

I have a table to show records. One of it's columns contains select, to choose the version of the record. The second one is the map of dates, where keys are the versions. This looks like this:
<td><select ng-model="record.currentVersion"> ...</td>
<td>{{record.dates[record.currentVersion]}}
I'd like to add sorting by the second column's value, but I'm not sure how to address object's own property in st-sort. Something like st-sort="dates[currentVersion]" doesn't work. Is this possible to do?
Define a new function:
$scope.getDate = function(record) {
return record.dates[record.currentVersion];
};
Use st-sort:
<th st-sort="getDate">Date</th>
Demo: http://plnkr.co/edit/l6poIsZ2YHUyeFf4TdET?p=preview

ng-option track by group name breaks the select

I have a dropdown select with grouped ng-options. The user should be able to dynamically change the naming of the group name in an input field. On change of the group name value the select directive is not being updated unless you make a new selection.
I fixed this with using track by group name, but now the select dropdown is broken. Note that the option you select is not the one that is actually selected.
Is there a way to have both dynamically updated group name and a working select?
<select ng-options="player.name group by player.team for player in players track by player.team" ng-model="systemType.tertiaryEquipment"></select>
Here's the jsfiddle : http://jsfiddle.net/07woeam8/2/
You have several options here, but there are trade-offs that you need to consider.
First, the thing that is messing up your selections is the track by expression, which shouldn't be player.team because you are not selecting teams. It should be either player.name or nothing at all. Keep in mind that tracking by player.name will make the groups in the select object to not be updated in real-time.
Second, if you want the groups in the option menu to change dynamically, you need to change the entire $scope.players object, and not just change a string value inside of it. For example, you can use angular.copy:
$scope.change = function() {
var copy = angular.copy($scope.players);
copy[0].team = 'betsdfhsk';
$scope.players = copy;
};
This presents the other trade-off you need to consider. Changing the object reference completely will also void your selected object if you didn't use track by player.name in the first option.
But here's a working fiddle.

AngularJS - original index of an object within filtered ng-repeat

I am using a nested ng-repeat and a filter on a object. The first ng-repeat is filters to the headerId in a gapHeader object. The second ng-repeat filters gapSection, sectionId to the corresponding headerID.
I have an edit page which is within a separate modal window. The purpose is to edit content corresponding to the headerID & sectionID of the sub-object) This also has a separate control. Data is shared through a service.
My problem I have a button for each gapSection sub-object, which opens the edit page modal, when I pass the $index value for the current section within each section to the service, I get the $index only corresponding to the second ng-repeat? For example, if I click the button within the 2 ng-repeat on gapSection (headerId:2, sectionId:2), I get an $index of 1. I require an $index of 2 which corresponds the sub-object position within gapSection.
Is it possible to pass the true $index which corresponds to the $index defined in the original un-filtered object of gapSection? Appreciate any comments on this and thank you!
Object:
var data ={
gapHeader:[{headerId:1,name:"General Requiremets",isApplicable:true},
{headerId:2,name:"Insurance",isApplicable:false}],
gapSection:[{headerId:1,sectionId:1,requirement:"A facility is required to have company structure",finding:null,cmeasures:null,cprocedures:null,personResp:null,isAction:null},
{headerId:2,sectionId:1,requirement:"Organisation must have public liablity",finding:null,cmeasures:null,cprocedures:null,personResp:null,isAction:null},
{headerId:2,sectionId:2,requirement:"Facility must hold workers compensation insurance",finding:null,cmeasures:null,cprocedures:null,personResp:null,isAction:null}]
};
If you need the true index you do not even need to pass the $index property, just pass the object and get the index from the original list.
i.e
$scope.gapSectionFn = function(obj){
var idx = data.gapSection.indexOf(obj);
}
Also it is not clear your issue could really be a nested ng-repeat issue, because according to you gapSection is the inner ng-repeat and you are invoking the call from inner ng-repeat and in need of gapSection's index. It should just be available, but the presence of a DOM filter will just reorg the items and its index which you can also get by doing an ng-init, i.e on the view ng-init="actIndex=$index" and use actIndex.
If you are trying to access parent ng-repeat's index then, ng-init is more appropriate than $parent.$index. Since ng-init is specially designed for that., on the parent ng-repeat you would write ng-init=""parentIndex=$index" and use parentIndex.

Dynamic form controls with ng-repeat Angular

In my controller:
$scope.mytest = [1,2,3];
My view:
<div ng-repeat="foo in mytest">
{{foo}}
<input type="text" ng-model="mytest[$index]" name="$index" />
</div>
{{ mytest | json }}
When I load the page it renders this:
I can change the value, and the binding updates all the values:
The only problem, is the form field loses focus. When ng-model updates the scope's values, it causes ng-repeat to manipulate the dom, and the fields lose focus after each key press. I have to click back inside the field between each keypress to enter a value with length more than 1 character.
What I'm trying to accomplish here is to render a text field for each value of an array. Updating the text field should update the scope values, and also update all places I'm displaying those values within the view, both inside & outside of the ng-repeat
Figured it out. I changed my array to be an array of objects with IDs:
$scope.mytest = [{id:1, title:'one'}, {id:2, title:'two'}];
Then I prevented ng-repeat from removing & re-inserting a dom node when it changes:
<div ng-repeat="foo in mytest track by foo.id">
My previous attempt from the original post fails. In this example illustrating why, you can see that Angular can't discern what changes were made:
[1,2,3] // point A
[2,2,3] // point B
Angular has no way of knowing what took place to get from point A to B. Did I edit the first value? Remove it & and insert a new value? Make multiple edits that cancel each other out?
In this example, Angular is able to discern what happened to get from Point A to B:
$scope.mytest = [{id:1, title:'one'}, {id:2, title:'two'}]; // point A
$scope.mytest = [{id:1, title:'two'}, {id:2, title:'two'}]; // point B
In this example I use the "value object" design pattern. I wrap my scalar values in an object which has an identifier that is immutable, while the wrapped value remains mutable.
Combined with the "track by" syntax of ng-repeat, it allows Angular to discern between an add & subsequent removal versus an edit. ng-repeat can then directly update the bindings in the isolate scope, as opposed to redrawing the whole darn page.

Resources