angular: how to make a button disabled based on a condition - angularjs

Sorry for not being specific, but I'm having a blackout and am desperate.
Have been struggling for 2 days now to find the logic to do the following:
every item has a 'inCart' value. if it's 0, it's not in there, if it's below 7, it's in one of 6 'slots' depending on the value.
now i have buttons that assign each item to a value in inCart.
but if one of the items has a value in it that is 4, then I obviously don't want all the items to show a button that will assign it to 4.
<tr ng-repeat="x in myItems">
<a ng-repeat="slot in [1,2,3,4,5,6]" ng-click="addToCart(x._id, slot)"
class="btn btn-primary {{x.inCart != '0' ? 'disabled' : ''}}">
{{slot}}
</a></tr>
Thinking about it logically I know my mistake: i only look if the current item is in the cart, and if it is then it disables the button. How do I look at all the items in the cart, and if one of them is for example 4, then all the buttons that assign an a value 4 to inCart are disabled, so that I won't have multiple items with the same location inCart.
based on the approach I can fix this I am pretty sure I can make everything work better then how it is now, it's just that I can't even figure this out so let alone the more detailed issues.

You can use the ngClass directive
You can evaluate a expression with ng-class and if it's true you can apply a certain class. If not, then the class won’t be applied.
<button ng-class="{'disabled': expression}">

If you use an actual button, you can make use of ngDisabled:
<input type="button" ng-repeat="slot in [1,2,3,4,5,6]"
ng-click="addToCart(x._id, slot)"
class="btn btn-primary"
ng-disabled="x.inCart !== '0'" value="{{slot}}" />

You could call a function to check if the slot is available.
Controller
// return the slots already used
var slotsUsed = $scope.myItems.map(function (i) {return i.inCart;}).filter( function(s) { return s > 0;});
$scope.slotAvailable = function(s) {
return slotsUsed.indexOf(s) == -1;
}
View
<tr ng-repeat="x in myItems">
<td>
<a ng-repeat="slot in [1,2,3,4,5,6]"
class="btn btn-primary"
ng-click="addToCart(x._id, slot)"
ng-class="{'disabled': x.inCart != '0' || !slotAvailable(slot)}">
{{slot}}
</a>
</td>
</tr>

There's a directive designed specifically for this: ngDisabled
https://docs.angularjs.org/api/ng/directive/ngDisabled

Related

Add a <br> every 5 item with ng-repeat

I'm trying to get an html <br> tag after a determined amount of elements with ng-repeat. As an example every 5th button gets a <br> appended to it. I tried multiple approaches but can't seem to get it right. I'd appreciate if anyone can give me some pointers on how to achieve this. Thanks in advance.
<button type="button" class="btn btn-default btn-small" ng-repeat="item in items"
{{item}}
</button>
You can check for every fifth element using $index of ng-repeat, after that you can use ng-show directive to show <br /> accordingly
<br ng-show="($index + 1) % 5 == 0" />
I assume this is supposed to be a row of buttons before each <br>? Easiest way to do this is to create a button container to repeat on, and making use of the ngIf directive alongside the $index property of ngRepeat to determine if the <br> should exist:
<span ng-repeat="item in items">
<button type="button" class="btn btn-default btn-small">{{ item }}</button>
<br ng-if="$index && $index % 4 == 0">
</span>
The first $index inside the ngIf will evaluate to false on the first index (as it's the 0th index, so you don't get an early line break) and $index % 4 will correspond to the 5th button overall accounting for the 0th index.

AngularJS - How to correctly use ng-show inside a ng-repeat? My boolean isn't getting updated

I have a ng-repeat that loops over 9 divs, each one has a different color.
When the user clicks on one div, its color it's gonna be the background color of a section.
I'm trying to do this:
The array that gets repeated is structured like this:
interface colorBoxes {
color: string;
isSelected: boolean;
}
in the view:
<div ng-repeat="s in vm.colorBoxes track by $index">
<div class="pointer" ng-click="w.backgroundColor = s.color; vm.pickColor(s, $index)" ng-style='{"background-color": s.color}'>
<i ng-show="vm.isColorSelected($index) === true" class="fa fa-check fa-1x checkOnSelectedLegend"></i>
</div>
</div>
in the controller:
pickColor(array: any, index: number) {
for(var i = 0; i<=this.colorBoxes.length; i++) {
this.colorBoxes[i].isSelected = false;
}
array[index].isSelected = true;
}
I use this function so when you click on a DIV, its variable: isSelected gets true, and all the other DIV's have theirs set to false.
I use this variable in the view with a ng-show, to show a check mark on the DIV that is currently selected, but this isn't working, below the function I put in that ng-show
isColorSelected(index:number):boolean {
return this.colorBoxes[index].isSelected
}
What am I doing wrong?
To summarize, I want that when you click on a box, its color string gets applied to another element (that is working correctly with my code), then, that box need to have a check mark appear on top of it, I tried with the above functions, by setting the isSelected var to true when clicked, but it doesnt work.
I'm pretty sure the problem is that angular isn't checking for changes in that ng-show, I just don't know exactly how to make it check for changes, and maybe there is a cleaner way to obtain what I'm trying to do!
Thank you
addded fiddle: http://jsfiddle.net/7zymp2gq/1/
Ok, here you have your code fixed and working:
http://jsfiddle.net/7zymp2gq/4/
Basically there were 2 things wrong with the function $scope.pickColor:
The loop was entering into not existing fields, I have changed the <= with a <
It was updating array[index], and it should be updating $scope.colorBoxes[index]
Instead of using function at ng-show you can use the isSelected property:
<div ng-app="app" ng-controller="Ctrl">
<div class="class1" ng-repeat="s in colorBoxes track by $index">
<div class="pointer class2" ng-click="pickColor(colorBoxes,$index);" ng-init="lastselected=s.isSelected?$index:null" ng-style='{"background-color": s.color}'>
<i ng-if="s.isSelected" class="fa fa-check fa-1x checkOnSelectedLegend"></i>
</div>
</div>
<div class="changeColor" ng-style='{"background-color": chosenColor}'></div>
</div>
Check this demo.

Upgrade a simple select/unselect into a proper array I can call on

I have an ng-repeat that shows a list of items a user can 'select' and I am using the following code (stripped down example) for the selection process
<div class="panel" ng-class="{'titleSelected': titleSelected[$index]}" ng-repeat="item in listofitems">
<h1>{{item.name}}</h1>
<button class="btn btn-success" ng-click="titleSelected[$index] = !titleSelected[$index]">
<span ng-hide="titleSelected[$index]">Add to Buy List</span>
<span ng-show="titleSelected[$index]">Remove from Buy List</span>
</button>
</div>
Now this is working fine (I have also had this working using item.id to track rather than $index. i click an item, background changes colour thanks to ng-class and the button dynamically becomes a remove button
However I was expecting titleSelected to be an array of stored info, but I was clearly wrong. What I actually need is a live array that stores / removes item.id on click and I have approached this all wrong.
Also when I use filters (not shown in the code), if any item is selected and then hidden by the filters.. and brought back it loses its 'selected' status.
This is the case with both $index and item.id
I suspect little functions with push and splice are required but I also still need the simple functionality of the add remove in place as well. and in perfect sync with what has actually been selected
Any pointers appreciated
EDIT 1 : Ok by simply adding
$scope.titleSelected = [];
into my controller, and using item.id instead of $index my app is now 1) remembering the selected items between filter changes AND createing an array I can call on. However the array contains (on the first selection) an entry for every single item.
So I have 503 items, and on first click the array length jumps to 912 (no idea where this number comes from).. most of which are null, but if I click on the item with id=4 then the fourth entry becomes true i.e
null,null,null,true,null...
Done with a couple of functions bound to ng-click
$scope.basket=[];
$scope.addToBasket = function(item) {
$scope.basket.push(item);
};
$scope.removeFromBasket = function(item) {
var index = $scope.basket.indexOf(item);
if (index > -1) {
$scope.basket.splice(index, 1);
}
};
and in the HTML
<button ng-hide="titleSelected[item.id]" class="btn btn-success"
ng-click="addToBasket(item.id);titleSelected[item.id] = !titleSelected[item.id]">
Add to Buy List
</button>
<button ng-show="titleSelected[item.id]" class="btn btn-success"
ng-click="removeFromBasket(item.id);titleSelected[item.id] = !titleSelected[item.id]">
Remove from Buy List
</button>

How to create dynamic expression in ng-class

I have a ngRepeat element that has a delete button that triggers a confirmation message. I'm trying to make the confirmation message show with a dynamic expression like so:
<div ng-repeat="item in items">
<a ng-click="delete(item_id)"></a>
<span ng-class="{'show':'delete_'+item._id}">Are you sure you want to delete your review? </span>
</div>
Unfortunately I can't get the expression 'delete_'+item._id to show up in the ngClass directive. Can someone please offer a solution?
Using ng-class="{'show': 'delete_' + item._id}" will add the class show whenever the expression 'delete_' + item._id' evaluates to true (which is always). You probably want to have an expression next to show which is true when the user has clicked the a tag, i.e. ng-class="{'show': userWantsToDelete(item)}".
Additionally, if you want to add the class 'delete_' + item._id to the element, you can use angular expressions with class attribute, i.e., class="{{'delete_' + item._id}}" on the element.
try this one.
<a ng-click="delete = !delete">delete</a>
<span ng-class="{show: delete}">Are you sure you want to delete your review? </span>
if you want to show one delete confirmation message of last clicked <a>
In view
<a ng-click="delete(item._id)">delete</a>
<span ng-class="{show: item._id==delete_id}">Are you sure you want to delete your review? </span>
In controller
$scope.delete = function(id) {
$scope.delete_id = ($scope.delete_id == id) ? !id : id;
}

ng-repeater issue Duplicates in a repeater is not allowed

I'm trying to create a form like below, this using ng-repeat directive in angular and it whenever I created a new row complains
"Duplicates in a repeater are not allowed.".
While I understand the solution for this is by putting "track by $index", however it causes another issue, which clicking delete on one row deletes the value of other field. So I suspect that track by index is OK for static text but not input form. So how to use ng-repeat correctly for my case? See my JSFiddle for demo.
My current code :
HTML
<div class="row-fluid spacer10">
<a ng-click="addAKA()" class="btn btn-primary spacer5 left30"><i class="icon-plus icon-white"></i> Add New Alias</a>
</div>
<div class="row-fluid spacer10"></div>
<div class="row-fluid spacer5" ng-repeat="item in aliasList track by $index">
<input type="text" class="span6 left30" ng-model="item">
<button class="btn btn-danger" ng-click="deleteAKA($index)">delete</button>
<BR/>
</div>
Javascript
$scope.addAKA = function ()
{
if($scope.aliasList == null)
{
$scope.aliasList = [];
}
$scope.aliasList.push("");
$scope.aliasjson = JSON.stringify($scope.aliasList);
}
$scope.deleteAKA = function (idx)
{
var aka_to_delete = $scope.aliasList[idx];
$scope.aliasList.splice(idx, 1);
$scope.aliasjson = JSON.stringify($scope.aliasList);
}
Just change the way you iterate. Instead of this:
<div ng-repeat="item in aliasList track by $index">
do this:
<div ng-repeat="item in aliasList">
$index will be still available inside the element, use like this:
<button ng-click='deleteItem($index)'>Delete</button>
See this JSFiddle for a correct solution
There are multiple problems with your approach.
Since you are directly binding a string to ng-model and ng-repeat creates a child scope, any change to the value would not reflect back. Change you scope model to something like
$scope.list = [{text:"one"},{text:"two"}];
and bind to i.text instead of binding to i as you did earlier.
The deleteItem method was called using item instead of index. See my fiddle here
http://jsfiddle.net/JS6aJ/1/

Resources