ng-if not working as expected - javascript

This is how I populate the Table and attach checkbox to controller
<tr ng-repeat="key in queryResults.userPropNames">
<td><input type="checkbox"
data-ng-checked="selectedKeys.indexOf(key) != -1"
data-ng-click="toggleSelect(key)">
</td>
<td>{{key}}</td>
<td ng-repeat="user in queryResults.users">
{{user.properties[key]}}
</td>
</tr>
This is how my HTML for button looks
<div>
<span ng-if="!validKeys" class="button-terminal primary save-user-keys"
data-disabled="false">Save Keys</span>
<span ng-if="validKeys" class="button-terminal primary save-user-keys"
data-ng-click="saveUserKeys()">Save Keys</span>
</div>
and my Controller looks like
$scope.toggleSelect = function (attribute) {
if ($scope.selectedKeys.indexOf(attribute) === -1) {
$scope.selectedKeys.push(attribute);
} else {
$scope.selectedKeys.splice($scope.selectedKeys.indexOf(attribute), 1);
}
};
$scope.saveUserKeys = function() {
$scope.customAttributes.mappingForUser = $scope.selectedKeys;
$scope.saveMappings();
};
$scope.validKeys = !!$scope.selectedKeys;
But my button is always enabled, even if I de-select all the checkboxes
What is wrong with this code?
Thank you

$scope.selectedKeys is an Array, even when no keys are selected. However empty Arrays are truthy (!![] // => true).
One fix would be to check the length of selectedKeys instead:
$scope.validKeys = $scope.selectedKeys && $scope.selectedKeys.length;
Alternatively, if assigning validKeys was just an attempt to get the view to render correctly, on the view you could just update the ngIf to ng-if="selectedKeys.lengh"

If you print validKeys (i.e. {{validKeys}}, do you see it changing between true/false? Also, if I understand this correctly, you should be testing for the length of validKeys - if higher than 0, enable the button, otherwise disable it.

Related

ng-checked in a radio button in AngularJS

In my angular application, I am looping through a collection and displaying the records with input type="radio".
<tr ng-repeat="account in vm.pagedAccounts.items"
ng-class="{ 'highlight': (account.rowIsSelected) }"
<td>
<input
ng-model="account.rowIsSelected"
value="{{account}}"
name="selectedAccount"
ng-checked="account.rowIsSelected"
ng-change="vm.selectAccount(account)"
type="radio">
</td>
In my controller, I first set rowIsSelected property to false for all the accounts.
response.data.results.forEach(function(account) {
account.rowIsSelected = false;
});
So, I just make sure whenever account.rowIsSelected is set to something, make that checked.
This is working fine.
But, in the selectAccount function, if a different account is clicked, I want to remove the previous all highlights and highlight the current one.
vm.selectAccount = function (account) {
if (account.rowIsSelected) {
//First set all false
vm.pagedAccounts.items.forEach(function(account) {
account.rowIsSelected = false;
});
var selectedAccount = vm.pagedAccounts.items
.filter(function(x){
return x.id=== account.id;
});
//Then set only that accounts property to true
selectedAccount[0].rowIsSelected = true;
}
But if I click the same row twice, it is no longer checked. I want to keep it checked and highlighted.
How to do it?
Does whatever I am doing seem right?
Please help.
Try this.
vm.selectAccount = function (checkedItem) {
var selectedAccount;
vm.pagedAccounts.items.forEach(function(account) {
if(checkedItem.id == account.id){
selectedAccount = account;
account.rowIsSelected = true;
}else{
account.rowIsSelected = false;
}
});
//Then set only that accounts property to true
selectedAccount[0].rowIsSelected = true;
}
I think you should organize your radio buttons in a bit different way, like it recommends angularjs
Something like:
<tr ng-repeat="account in vm.pagedAccounts.items"
ng-class="{ 'highlight': vm.pagedAccounts.selected === account }">
<td>
<input
ng-model="vm.pagedAccounts.selected"
ng-value="account"
type="radio">
</td>
...
And radio buttons should be automatically selected, when ng-model values is equal to ng-value, so you don't need any specific logic or ng-checked, etc.

Angular 1: check all check boxes in a large ng-repeat

I have an table created using ng-repeat and there hundreds of rows, up to 600 or 700. Each row includes a checkbox and I have a "Check All" box at the top to check all the boxes in one go. However I'm running into browser performance issues, IE11 (the clients preferred choice) in particular becomes unresponsive. After several minutes all the checkboxes appear checked but you still can't scroll or do anything so it is effectively useless.
I have created a controller array and when the checkAll box is clicked it loops through the model (the one used in ng-repeat) and adds a value to the array. I presume it's this looping through the array that is causing the slow-down but I'm not sure. Pagination has been ruled out, they want all the rows on one page.
<table>
<tbody>
<tr>
<th>Table Header</th>
<th><input type="checkbox" id="checkAllCheckBox" ng-model="vm.allChecked" ng-change="vm.tickOrUntickAllCheckBoxes()" />
</tr>
<tr ng-repeat="payment in vm.payments>
<td>{{ payment.somePaymentValue }}</td>
<td>
<input type="checkbox" class="paymentsApprovalCheckbox"
ng-checked="vm.approvedPayments.indexOf(payment.payId) > - 1"
ng-value="payment.payId" ng-model="payment.approved"
ng-click="vm.handleCheckBoxClick(payment.payId)" />
</td>
</tr>
</tbody>
</table>
Here is the angular function that checks/unchecks all
vm.tickOrUntickAllCheckBoxes = function(){
if (vm.allChecked == false) {
vm.approvedPayments = [];
} else {
vm.payments.forEach(function(payment){
vm.approvedPayments.push(payment.payId);
});
}
};
Swapping out the angular vm.tickOrUntickAllCheckBoxes() function for a plain old javascript option makes the checkAll box work almost instantaneously in IE11 however I lose access to the checked payment.payId values. I wonder is there away for angular to get them? Here is the plain javascript checkAll() function:
<script>
function checkAll(x) {
var checkBoxes = document.getElementsByClassName('paymentsApprovalCheckbox');
for (var i = 0; i < checkBoxes.length ; i++) {
checkBoxes[i].checked = (x.checked == true);
}
}
</script>
Then I update the checkAll checkbox like this:
<input type="checkbox" id="checkAllCheckBox" ng-model="vm.allChecked" onclick="checkAll(this)" />
If you check one checkbox individually then the ng-model="payment.approved" in the repeating checkboxes is updated but this does not happen if they are checked with the checkAll function. Is it possible for angular to detect the boxes checked with checkAll()? I guess this is just putting off the same old inevitable slow-down to a slightly later point in the process.
Anyone have any ideas or work-arounds? Thanks!
I would use the ng-model to the best of its abilities. In your controller:
$onInit() {
// If you need this from a REST call to populate, you'll have to
// remember to do that here;
this.model = {
all: true,
items: {}
};
}
In your loop:
<tr>
<th>Table Header</th>
<th>
<input type="checkbox"
id="checkAllCheckBox"
ng-model="vm.model.all"
ng-change="vm.tickOrUntickAllCheckBoxes()" />
</tr>
<tr ng-repeat="payment in vm.payments track by $index">
<td ng-bind="payment.somePaymentValue"></td>
<td>
<input type="checkbox"
class="paymentsApprovalCheckbox"
ng-change="vm.approvedPayments($index)"
ng-model="vm.model.items[$index]" />
</td>
</tr>
Then in your controller:
tickOrUntickAllCheckBoxes() {
const boxes = this.model.items.length;
this.model.all = !this.model.all;
// Several ways to do this, forEach, map, etc.,
this.model.items.forEach((item) => { item.checked = !this.model.all });
}
And for setting it individually:
approvedPayments(idx) {
// Sets all the item's boxes checked, or unchecked;
this.model.items[idx].checked = !this.model.items[idx].checked;
// Possible call to extended model to get payment info;
handleCheckBoxClick(idx);
}
You should be able to put all the payment information into the one approvedPayments() method rather than have two separate methods (move logic out of template and into the controller or a service). I.e., your model could look like:
this.model.items = [
// One 'option' with id, payment etc;
{
id: 73,
paymentId: 73,
somePaymentValue: 210.73,
currencyType: 'GBP',
checked: false
},
{
// Another 'option' etc...
}
]
One issue to note is the incompatibility of ngChecked with ngModel, had to look it up (which is why I haven't used ng-checked in the above).
Thank to everyone for the suggestions. The solution I came up with was to push some of the work back to the server side. Instead of just loading the payments model (in which each payment record contains a lot of info) i am now loading two additional models when the page loads, one of which is a set of key/value pairs where the keys are payId and the values are all false and another one with the same keys and all values are true. Example:
{
"1": false,
"2": false
}
These are used for the checkAll/Uncheck all - just set the vm.approvedIDs variable to the true or false one. Then, the vm.approvedIDs variable is used as the model in the ng-repeat checkbox.
I have to do a bit of extra work on the server side when the user sends the approvedIDs back to the server to get only the key/id of the 'true' entries. Here are the relevant angular controller functions:
$onInit() {
// call http to get 'data' from server
vm.payments = data.payments;
vm.paymentIDsFalse = vm.approvedIDs = data.paymentIDsFalse;
vm.paymentIDsTrue = data.paymentIDsTrue;
};
// tick/untick all boxes
vm.tickOrUntickAllCheckBoxes = function(){
if (vm.allChecked == false) {
vm.approvedPayments = vm.paymentIDsFalse;
} else {
vm.approvedPayments = vm.paymentIDsTrue;
}
};
// tick/untick one box
vm.handleCheckBoxClick = function(payId, currentValue){
vm.approvedPayments[payId] = currentValue;
};
vm.submitApprovedIds = function(){
// post vm.approvedPayments to server
};
HTML:
<table>
<tbody>
<tr>
<th>Table Header</th>
<th><input type="checkbox" id="checkAllCheckBox" ng-model="vm.allChecked" ng-change="vm.tickOrUntickAllCheckBoxes()" />
</tr>
<tr ng-repeat="payment in vm.payments>
<td>{{ payment.somePaymentValue }}</td>
<td>
<input type="checkbox" class="paymentsApprovalCheckbox"
ng-value="payment.payId"
ng-model="vm.approvedPayments[payment.payId]"
ng-click="vm.handleCheckBoxClick(payment.payId, vm.approvedPayments[payment.payId])" />
</td>
</tr>
</tbody>
</table>
It looks to me as if there must be a better way than creating these additional models but it is working pretty smoothly for now and I can move on to the next thing!

Making a text box that duplicates upon input in AngularJS?

I'm trying to make a series of text boxes. It starts with one text box, but when the user puts information into it, another empty text box appears beneath it. This continues indefinitely.
Each text box needs to have an ng-model value associated with it, and each needs to be generated by ng-repeat.
For example, my HTML is this:
<table ng-controller="BoxesController">
<tr ng-repeat="box in boxes">
<td><input type="text" ng-model="box.input"></td> <!--boxes[0].input-->
</tr>
</table>
I'm using box.input rather than just box because it needs to have other variables assigned to it as well.
Then my controller would be:
.controller('BoxesController',['$scope',function($scope){
$scope.boxes = [
{input: ""}
];
if($scope.boxes[$scope.boxes.length - 1] !== ""){
$scope.boxes.push({input: ""});
$scope.$apply;
}
}])
This would create an empty box in the view in which box.input === "". The if is basically "If the last value in the array is not empty, append a new empty value to the array."
This whole thing should, initially, create a single empty box then generate more boxes as the user inputs data box by box.
However, what it actually does is generate two empty boxes that do not respond to input at all.
Would anyone know what to do here, how to make this work?
Thank you!
Wrap the condition within a method:
$scope.newBox = function() {
if($scope.boxes[$scope.boxes.length - 1].input !== ""){
$scope.boxes.push({input: ""});
console.log( $scope.boxes)
$scope.$apply;
}
};
Html:
<td><input type="text" ng-model="box.input" ng-blur="newBox()"></td>
Demo
As the answer above, try to use an method. Here another example using ng-change.
angular.module('app',[])
.controller('BoxesController',['$scope',function($scope){
$scope.boxes = [
{}
];
$scope.callChange = function() {
if($scope.boxes[$scope.boxes.length - 1].val !== ""){
$scope.boxes.push({val: ""});
}
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app'>
<table ng-controller='BoxesController'>
<tr ng-repeat="box in boxes">
<td><input type="text" ng-model="box.val" ng-change="callChange()"></td>
</tr>
</table>
</div>

Get elements from Parent Row of checked checkboxes

I have the following row in a table.
<tr class="data_rows" ng-repeat='d in t2'>
<td class="tds"> <input class='checkBoxInput' type='checkbox' onchange='keepCount(this)'></td>
<td class="tds"><a href='perf?id={{d.ID}}'>{{d.ID}}</a></td>
<td class="tds">{{d.HostOS}}</td>
<td class="tds">{{d.BuildID}}</td>
<td class="tds">{{d.Description}}</td>
<td class="tds">{{d.User}}</td>
<td class="tds">{{d.StartTime}}</td>
<td class="tds">{{d.UniqueMeasure}}</td>
<td class="tds">{{d.TotalMeasure}}</td>
</tr>
Here's the HTML for button that will invoke the function to collect the ids from checked check boxes and store them.
<div id='compButtonDiv' align='center' style="display: none;">
<input id='cButton' type='button' value='compare selections' onclick='submitSelection()' style= "margin :0 auto" disabled>
</div>
The data is in t2 which consists of an array of length 15-20.
What i want to do is get the value of ID i.e, {{d.ID}} of the 2 checked check boxes so that i can store them in a variable and pass them as query parameters to URL using `location.href = url?param1&param2'
Here's the javascript:
function keepCount(obj){
debugger;
//var count=0;
if(obj.checked){
obj.classList.add("checked");
}else{
obj.classList.remove("checked");
}
var count = document.getElementsByClassName("checked").length;
var cBtn = document.getElementById('cButton');
//alert(count);
if(count == 2){
cBtn.disabled = false;
}
else if(count < 2){
cBtn.disabled= true;
}
else{
cBtn.disabled= true;
alert("Please Select two sets for comparison. You have selected: " + count);
}
}
function submitSelection(){
// what should be the code here??
location.href= "existingURL?a&b";
}
Now can someone please tell me how to get the id's?? I need to extract ID from the checkboxes that are checked(on the click of button whose code i've mentioned above'.
Thanks.
-Ely
Firstly when we use angularjs we tend to depend less and less on DOM manipulation.
For this reason, what you can do is to attach ngModel to the checkbox.
Like:
<input class='checkBoxInput' ng-model='d.isChecked' type='checkbox' onchange='keepCount(this)'>
What this does is, it attaches the variable (in your case the property of item in the list) to the check box. If it is checked it is true, if unchecked, initially it will be undefined, later on checking and then unchecking it will be false.
Now, when you submit, just loop over the original list in the function and check the values of d.isChecked (true/falsy values). Then you can add the necessary items in a separate list for submission.
The only concern is when checking the list on submission , check if(d.isChecked), so that it ignores the falsy values(false/undefined).

how to maintain old value in Angular js

i have created editable Grid in angular js.
issue that i m facing is if user click edit and change values and then cancel edit . it updates the scope that is default behavior as m using ng-model
but i wanted to preserver old state so in case if user tries to cancel i can set old values in Grid
View
<tr ng-repeat="course in courses">
<td>
<span>{{course.id}}</span>
</td>
<td>
<span ng-show="course.editMode == null || course.editMode == false">{{course.number}}</span>
<input class ="form-control" ng-model="course.number" ng-show="course.editMode == true" />
</td>
<td>
<span ng-show="course.editMode == null || course.editMode == false">{{course.name}}</span>
<input class="form-control" ng-model="course.name" ng-show="course.editMode == true" />
</td>
<div class="btn-group">
<i ng-click="updateCourse(course)"></i>
<i ng-click="cancelEditMode(course)"></i>
</div>
Controller
$scope.courses = [];
var oldvalue = '';
$scope.toggleEditMode = function (course) {
course.editMode = true;
oldvalue = course;
};
$scope.cancelEditMode = function (course) {
course.editMode = false;
var index= $scope.courses.indexOf(course);
$scope.courses[index] = oldvalue;
};
i have tried to have a variable oldvalue and when user clicks edit i save existing value in oldvalue and then on canceledit i tries to set oldvalue.
but it didnt work, i always get new value in oldvalue.
can some one help me out wat i m doing wrong ?
You should create a copy of the object as the old value. You can use angular.copy method for that. Otherwise you are operating on the same object and any updates to this object will change the old value too. Here's a working example in plunker.
You have a second error in cancelEditMode method because you are setting editMode property to false in the object you will replace. That's also fixed in the example.
That's because course is an object, and when you assign an object to some variable, that variable will hold only a reference to that object. So, when you change the original object (the course), the oldvalue reflects the change.
You need to make a copy of the original course object:
...
oldValue = angular.copy(course);
...

Categories