Table loses sort after function call in Angular - javascript

I have a table that uses Angular orderby. I use a function called tcoSort that makes a calculation and then sorts the table. The problem is when I call displayFullPricing() from the radio button in the rows - the table loses the sorting.
<table class="table table-striped table-hover">
<tr ng-repeat="prices in productVariant.prices | orderBy: tcoSort">
<td>£{{prices.monthly.retailPrice}}</td>
<td>£{{prices.nonRecurring.retailPrice | number:0}}</td>
<td><input type="radio" name="{{productVariant.code}}" ng-click="displayFullPricing($index)"></td>
</tr>
$scope.tcoSort = function (productVariant) {
if(!$scope.isDisplayPrice){
return productVariant.nonRecurring.retailPrice + (productVariant.monthly.retailPrice * 36);
}
};
$scope.displayFullPricing = function (index) {
$scope.isDisplayPrice = true; //This is to stop the table sort running more than once.
I put this here to act as a flag as everytime this function was called from the radio button, tcoSort would fire again and sort the table.
This meant that the radio button would only work once.
//rest of code
}
I guess the issue is that I am creating the flag to stop the sort happening every time I click a radio button in the table. But If i don't put it there, the $index is always set to the top result - so it would always be 0.

Instead of passing the $index of the relevant item to your displayFullPricing function you should just pass the object itself, e.g.
ng-click="displayFullPricing(prices)"
Presumably somewhere in // rest of code you're just selecting the relevant item based on its index anyway?

Related

How to track the progress of a large OrderBy

I have a standard enough dynamically filled table in bootstrap that is instantiated with the following definition
<table>
<tr ng-repeat="c in controller.items :
orderBy:controller.predicate:controller.reverse">...</tr>
</table>
And a pretty standard function that, when called with a string argument, will sort the table, using the string argument as the predicate.
controller.orderCasesBy = function (predicate) {
controller.reverse = (controller.predicate === predicate) ?
!controller.reverse : false;
controller.predicate = predicate;
}
The page I'm working on potentially has upwards to a thousand rows, so I was trying to figure out a way to track the progress of the ordering function and update a progress bar accordingly.
If this were a case where I was trying to track the progress of the entire table rendering for the first time, I could attach an ng-init function to each repeated row that could update the progress bar whenever it's called. ng-init functions don't seem to be called when a table is reordered, however, so I was wondering if there was anything I could do to achieve a similar effect.
If I can't, I'd love to hear suggestions on alternative ways to track this.
You can add an object parameter to the ordering function, which updates its value property (or call it whatever you want), then use the object.value to render your status indicator as you see fit.
Since you said you're open to alternatives, you don't need to actively reverse it at all (and therefore don't need to indicate progress for it). ngRepeat allows you to track by index, which means you can simply display them in reverse index order.
Example:
<table>
<tr ng-repeat="c in controller.items track by $index">
<td ng-model="controller.items[controller.items.length-($index+1)]"></td>
</tr>
</table>

Multiple event maps and helpers for multiple text inputs

I've got this helper and event map with a Session. The event is listening for a button click then grabs the input text from the input field. Then it sets the Session. The helper gets the Session and does a find() on the collection for CampYear (one of the collection fields).
Session.setDefault('keyCampYear', '');
Template.camp.helpers({
'Query': function () {
var cy = Session.get('keyCampYear');
return Programs.find({CampYear: cy}).fetch();
}
});
Template.query.events({
'click #camp-year-button': function(event) {
var campYearTemp = document.getElementById('camp-year').value;
Session.set('keyCampYear', campYearTemp);
}
});
The output is set up with a spacebars {{#each}} to present a table:
<tbody>
{{#each Query}}
<tr>
<td>{{Fname}}</td>
<td>{{Lname}}</td>
<td>{{CampYear}}</td>
</tr>
{{/each}}
</tbody>
This works as expected. I now need to add another text field and button to query the database for a different field, and output to the same spacebars {{#each}}. But I'm not able to get it working. Adding another event map, Session and helper doesn't seem to work. Is there a way I can get another input field with its button to search for a different field in the collection and output on the same spacebars {{#each}}?
NON-WORKING CODE BELOW:
Add text box and button:
<input type="text" id="donate">
<button id="donate-button">GO</button>
Add new event map, helper and Session:
Session.setDefault('keyDonate', '');
Template.camp.helpers({
'Query': function () {
var don = Session.get('keyDonate');
return Programs.find({DONATE: don}).fetch();
}
});
Template.query.events({
'click #donate-button': function() {
var donateTemp = parseInt(document.getElementById('donate').value, 10);
Session.set('keyDonate', donateTemp);
}
});
What ends up happening is that the entire Mongo collection loads in the browser by default, without the user clicking either button. The first button and text box are inoperable, the newly created button and text box work, but very sluggishly.
Looks like I found a solution. I'm not familiar with Meteor enough to know that this is THE solution, but here's what I did. First, to prevent the entire collection from loading on the page, both Sessions had to be set to a default of undefined:
Session.setDefault('key', undefined);
Rather than:
Session.setDefault('key', '');
I could default one Session like this with a '' value and it didn't seem to mind, with two set like this, things went haywire. The next change was to add another Spacebars {{#each}} inside the table just below the first one.
{{#each Query}}
<tr>
<td>{{Fname}}</td>
<td>{{Lname}}</td>
<td>{{CampYear}}</td>
</tr>
{{/each}}
{{#each Query2}}
<tr>
<td>{{Fname}}</td>
<td>{{Lname}}</td>
<td>{{CampYear}}</td>
</tr>
{{/each}}
Each helper now needs to link to the appropriate {{#each}}. The third part was that I was forced to set one Session back to undefined while setting the other to prevent stacking the tables on top of each other.
Template.query.events({
'click #donate-button': function() {
var donateTemp = parseInt(document.getElementById('donate').value, 10);
Session.set('keyCampYear', undefined);
Session.set('keyDonate', donateTemp);
}
});
Template.query.events({
'click #camp-year-button': function() {
var campYearTemp = document.getElementById('camp-year').value;
Session.set('keyDonate', undefined);
Session.set('keyCampYear', campYearTemp);
}
});

AngularJS toggle getting blocked by Math function

I have rows where cells are populated with words from an API. I've added a toggle feature on the cells to show if a user selected it. I'm using ng-class={selected:toggle} ng-click="toggle = !toggle" to toggle a CSS class to show the cell is selected or not.
HTML
<tr ng-repeat="row in game.rows">
<td id="row-{{$parent.$index+1}}-col-{{$index+1}}" ng-repeat="word in row.words" ng-class={selected:toggle} ng-click="toggle = !toggle"><div class="points">{{generateRandomPoints()}}</div>{{word}}</td>
</tr>
I added a Math function to the controller to randomly assign points to each cell (<div class="points">{{generateRandomPoints()}}</div>):
JavaScript
$scope.generateRandomPoints = function(){
return Math.floor((Math.random()*5)+2);
};
This function appears to be blocking my ability to use the toggle feature. Another oddity is that each click of a cell re-generates random numbers from the Math function.
Any thoughts why the toggling would be blocked?
It appears your generateRandomPoints call is punching through the local scope of your repeated element. Try creating a function like this:
$scope.toggle(index) {
game.rows[index].toggle = !game.rows[index].toggle;
}
And call it like ng-click="toggle($index)".
Full documentation for ngRepeat can be found here.

Angular js binding list of data dynamically based on checkboxes

I have an angular app in this plunkr
Here the button opens a modal which has a table. When an item in the table is checked or unchecked, it changes the values in the progress bar accordingly.
Below the table, it has a list of all the checked courses shown. How do i bind this data accordingly such that when a checkbox is checked, it also shows the item in this list dynamically? Here is the code i used for displaying this list of items:
<div ng-repeat="child in selectedCourses">
{{child.course.subject}}-{{child.course.course_no}}
</div>
The problem is that you push the same items many times into $scope.selectedCourses, which results into an error (because of duplicate values in ngRepeated items) which in turn causes the view to not be updated.
To prevent adding the same item many time, you should check if it is already present in the array:
$scop.checkplanneddetails = function (course) {
...
for (var k = 0; k < $scope.planneddetails.length; k++) {
if ($scope.requirementcoursename==$scope.planneddetails[k].course_name) {
if (!$scope.checkcoursefunction($scope.selectedCourses, course)) {
// ONLY add the course if it is NOT already present
$scope.selectedCourses.push(course);
}
course.checked = true;
return true;
}
}
return false;
};
There is also a problem with using ng-checked="checkplanneddetails(child), because checkplanneddetails is not idempotent. You should not use it with ngChecked, because it is run every time a $digest cycle takes place.
You should ngInit instead and also make sure you add course.checked = true at the proper place inside checkplanneddetails (see above):
<!-- Change that: -->
<input type="checkbox" ng-checked="checkplanneddetails(child)" ... />
<!-- To this: -->
<input type="checkbox" ng-init="checkplanneddetails(child)" ... />
See, also, this short demo.

Delete multiple ng-repeat elements on click of a single button

I have a table showing project names as shown in below image ,
On delete button click, I am passing the selected check-boxes data as array of objects to my controller
[{id:1,name:'Name 8'},{id:2,name:'Name 7'}]
Then deleting the names from the table at the server side. This all works fine but how do i remove the rows from DOM after deleting them?? I went through this post which says how to remove ng-repeat elemets from DOM but in that case the elements are removed one at a time, by passing the $index to the splice() function
In my case i need to remove multiple rows. If ill have to use the splice function in my controller how do i get the index from the selected rows object? Or is there any better way of doing it.
Hope my question is clear!
Update: jsFiddle
Solution: Well i had to modify #wickY26 answer a bit to suite my scenario. Here is my update jsFiddle
What i did was , in the delete() change code to
angular.forEach($scope.projects, function (row, index) {
if($scope.projects[index].checked) {
$scope.projects.splice(index,1);
}
});
You can keep selected rows on an object via binding checkbox with ng-model,
so example table html should be like this
HTML
<table class="table table-bordered">
<tbody>
<tr ng-repeat="row in data" ng-class="{'success' : tableSelection[$index]}">
<td>
<input type="checkbox" ng-model="tableSelection[$index]" />
</td>
<td ng-repeat="cell in row">
{{cell}}
</td>
</tr>
</tbody>
</table>
and if you define a function in your controller which travel through your data and splice array depends on tableSelection object boolean values...
UPDATE
after your comment I debug my code and see I cannot remove multiple rows same time, so I look at my code and change some part of it...
at my example you cannot delete multiple rows same time because everytime you splice an element from data array you shift indexes of array for rest, so right way to do it starting from last index,
Here new CONTROLLER
$scope.removeSelectedRows = function() {
//start from last index because starting from first index cause shifting
//in the array because of array.splice()
for (var i = $scope.data.length - 1; i >= 0; i--) {
if ($scope.tableSelection[i]) {
//delete row from data
$scope.data.splice(i, 1);
//delete rowSelection property
delete $scope.tableSelection[i];
}
}
};
I update my PLUNKER added comments and some other functionallty as well...

Categories