Stop loop using ng-if condition in angularJS - javascript

I have this code. $scope.counter = '1';
<div ng-repeat="cat in catadata">
<div ng-if="cat.TopCategoryID = 2" >
<div ng-if="counter = 1" >
<div class="feedsDisplay" ng-init="counter = counter + 1">
Creater Img:{{cat.CreatedBy_Profile.Picture.URI}}"
</div>
</div>
</div>
</div>
I want to stop loop when counter = 2, I want to display just 1 result.
ng-init="counter = counter + 1
{{counter}}
displaying 11111 it should display 12345

you are comparing with = instead of ==
Try like this
ng-if="counter == 1"
value of counter is string . add + sign before counter
try like this
ng-init="counter = +counter + 1"

You cant stop ng-repeat conditionally.
To print only one result you can use
<div ng-repeat="cat in catadata | limitTo : 1">
OR
<div>{{catadata[0]}}</div>

Rather than misusing ng-repeat, ng-init and ng-if like this, just try to extract the first matching cat before you render it, e.g. in a controller, directive or service:
for (var i = 0, maxI = catadata.length; i < maxI; ++i) {
if (cat.TopCategoryID === 2) {
$scope.firstMatchingCat = cat;
break;
}
}
And then in the template:
<div class="feedsDisplay">
Creater Img:{{firstMatchingCat.CreatedBy_Profile.Picture.URI}}"
</div>

Related

Sum array inside array elements and bind it in html inside an ngFor loop div in angular8

*)Please check out this 2 images in this i have a stock-shift array it's length is 2 and inside every
array i have another array, in the inside array i have to sum the elements of the quantity
*)check my html code and ts file and help me with the solution
*)Check the third image Ui there I need to bind sum value before the quantities like that for every
tile (box) i need to get different value but it is updating last value in all tiles (boxes)
html
====
<div *ngFor="let data of dataSource; let i = index;">
<!-- Stockshift card -->
<mat-card *ngIf="type === 'shift'" class="mb-3 card" (click)="addParams(data,data?.invoiceNumber,5, i)">
<mat-card-content class="p-2" fxLayout="column">
<div fxLayout="row" fxLayoutAlign="space-between">
<div><span [ngClass]="{'text-success' : data?.isSyncSuccess, 'text-danger' : data?.isSyncFailed, 'text-warning': (!data?.isSyncFailed && !data.isSyncSuccess)}"><b>#{{ data?.invoiceNumber }}</b></span></div>
<div><span [ngClass]="{'text-success' : data?.isSyncSuccess, 'text-danger' : data?.isSyncFailed, 'text-warning': (!data?.isSyncFailed && !data.isSyncSuccess)}"><b>{{ data?.entryDate | date: 'dd MMM yyyy'}}</b></span></div>
</div>
<span class="text-dark pt-2 card-content">
<span>{{(data?.type === 'TO') ? 'Sent' : 'Received'}}</span>
<span *ngIf="data && data?.productList[0]?.quantity < 1"> product <span class="text-primary">({{data?.productList[0]?.quantity}} Quantity)</span> {{(data?.type === 'TO') ? 'to' : 'from'}} </span>
<span *ngIf="data && data?.productList[0]?.quantity > 1"> products <span class="text-primary">({{totalProductList}} Quantities)</span> {{(data?.type === 'TO') ? 'to' : 'from'}} </span>
<span class="text-primary"> {{ data?.shiftGodown?.outletName }} </span>
</span>
</mat-card-content>
</mat-card>
</div>
Ts file
======
get totalProductList() {
let quantity: number;
this.transactionData.stockShift.forEach(stock => {
quantity = stock.productList.reduce((stock, list) => {
return stock + list.quantity;
}, 0)
});
return quantity;
}
1 solution - if you want to use getter totalProductList you can create a component for your box and in this case getter will work as expected.
stock.component.ts for example
get totalProductList() {
return this.stock.productList.reduce((stock, list) => {
return stock + list.quantity;
}, 0)
});
}
2 solution (BAD, cause call function in template) - you need to transform you getter to function and pass one box element to work correctly
ts
getTotalProductList(stock) {
return stock.productList.reduce((stock, list) => {
return stock + list.quantity;
}, 0)
});
}
html
<span class="text-primary">({{getTotalProductList(data)}} Quantities)</span>
3 solution - also transform you getter to function and call it for each element when you receive data and save to element property (stock .quantitiesSum for example, and use in html {{stock .quantitiesSum}})
p.s.
I think you should compare productList.length instead productList[0].quantity here
<span *ngIf="data && data?.productList[0]?.quantity < 1">
to
<span *ngIf="data && data?.productList.length < 2">

ng-repeat and ng-if syntax for arrays within arrays

I have an array within an array and I'm having some issues with ng-repeats and ng-ifs. My array looks something like this:
data.stage = [];
task_obj = {};
data.stage.push({
workflow_stage: wf.getDisplayValue('name'),
task: []
});
task_obj.number = userTasks.getValue('number');
task_obj.parent = userTasks.getValue('parent');
task_obj.state = userTasks.getValue('state');
task_obj.url = userTasks.getDisplayValue('url');
task_obj.incompleteCounter = incompleteCounter;
task_obj.icon = icon;
task_obj.status = status;
task_obj.style = style;
task_obj.short_description = userTasks.getValue('short_description');
task_obj.bundle_name = bundle.getValue('bundle_name');
task_obj.workflow = bundle.getDisplayValue('workflow');
task_obj.workflow_stage = bundle.getDisplayValue('workflow_stage');
for(var a=0; a < data.stage.length; a++){
if(bundle.getDisplayValue('workflow_stage') == data.stage[a].workflow_stage) {
data.stage[a].task.push(task_obj);
}
}
}
If I have an ng-repeat that looks like ng-repeat="item in data.stage track by $index", how would I access the short_description for example? Would it be like {{item.task.short_description}}?
Similarly, if I wanted to write an ng-if where bundle_name is "MyBundle", how would I write it? I've tried ng-if="item.task.bundle_name=='MyBundle'", but it obviously doesn't work.
Can anyone guide me on the correct syntax?
You need to use ng-repeat for task also
<div ng-repeat=ng-repeat="item in data.stage track by $index">
<div ng-repeat=ng-repeat="task in item.task track by $index">
<div ng-if="task.bundle_name == 'MyBundle' ">
My bundle condition
</div>
<span>{{task.short_description}}</span>
</div>
</div>

AngularJS get first item in a repeat that has a certain value

<div ng-repeat="widget in widgets"
ng-class="">
<div>{{widget.row}}</div>
</div>
I'm trying to apply a class inside the repeat based on a particular value in the repeat, for example if widget.row = 0 and it is the first widget with that value displayed then give it a class and all the other widgets that have row as 0 do not get the class. This will need to be the case if it equals 1 or 2 and so on so I can't just use $first as there will be multiple row values and multiple widgets for example it may output something like:
0 0 0 0 1 1 2 2 2 2
So the easiest way for me to achieve this was using the Adjacent sibling selector rather than do it with angular as each item is not really aware of the others:
<div ng-repeat="widget in widgets"
class="widget-row-{{widget.row}}">
<div>{{widget}}</div>
</div>
and then use CSS for:
.widget-row-0:first-child {}
.widget-row-0 + .widget-row-1 {}
.widget-row-1 + .widget-row-2 {}
.widget-row-2 + .widget-row-3 {}
Best practise is to prepare your data in a init function in your controller. It's nice and KISS! It's the best way to prepare your data in control function instead of misapply the E2E binding of AngularJS. It solve your problem so no class is written when there is no need for (as you asked for). Its proceeded once instead of calling a function again, again and again by E2E binding like ng-class="shouldIAddAClass()".
View
<div ng-repeat="widget in widgets"
ng-class="{ 'first' : widget.first }">
<div>{{widget.row}}</div>
</div>
Controller
$scope.widgets = [{
row: 0
}, {
row: 2
},{
row: 0
},{
row: 1
},{
row: 1
},{
row: 2
},{
row: 0
}];
//self calling init function
(function init () {
var widgetRowFound = {};
angular.forEach($scope.widgets, function (widget, key) {
if (angular.isDefined(widgetRowFound[widget.row])) {
$scope.widgets[key].first = false;
} else {
$scope.widgets[key].first = true;
widgetRowFound[widget.row] = true;
}
});
})();
Not the cleanest one but will work
<div ng-repeat="widget in widgets">
<div ng-class="{'myClass': applyClass(0, widget.row)}"></div>
</div>
----------
$scope.widgetsRows = {};
function applyClass(number, row){
if(!$scope.widgetsRows[row]){
$scope.widgetsRows[row] = true
}
return row == number && $scope.widgetsRows[row];
}
You can add the class you want to use to the widget objects in the controller first:
var tempRow = "";
for(var i = 0;i < $scope.widgets.length;i++) {
if($scope.widgets[i].row != tempRow) {
$scope.widgets[i].class = "myClass";
tempRow = $scope.widgets[i].row;
}
}
Then you can use that class:
<div id="widgets" ng-repeat="widget in widgets"
class="{{widget.class}}">
<div>{{widget.row}}</div>
</div>
Hope this helps
You can create a method that will be called from ng-class to achieve your goal. The method should return the class to be used.
$scope.firstHitFound = false;
$scope.isFirstZeroValue = function(value){
if($scope.firstHitFound == false && value == 0){
$scope.firstHitFound = true;
return class1;
}else{
return class2;
}
}
The HTML / Angular shoudl look as:
<div ng-class="isFirstZeroValue(widget.row)">
If you want to style it, add the class to all the widget that match your criteria, and use css to perform it only on the first of them.
Html:
<div id="widgets" ng-repeat="widget in widgets"
ng-class="{'widget-first': widget.row == 0}">
<div>{{widget.row}}</div>
</div>
Css:
#widgets.widget-first:first-of-type {
background: #ff0000;
}
You can use ng-class in addition of your ng-repeat:
Example
<div ng-repeat="widget in widgets" ng-class="{'test': widget.value === 0}">
<div>{{widget.row}}</div>
</div>
You need to call a method that will check if the row result is not same with previous value. If it not same , it will return true value and will be assigned ng-class, and if not return false. Filter this out using ng-if.
Html
<div ng-repeat="widget in widgets"
ng-class="">
<div ng-if="calculate(widget.row)">
<div ng-class="test">{{widget.row}}</div>
</div>
<div ng-if="!calculate(widget.row)">
<div>{{widget.row}}</div>
</div>
</div>
Controller
var arr = [];
$scope.calculate = function (row) {
arr.push(row);
var breakLoop = false;
angular.forEach(arr, function (oldVal, newVal) {
breakLoop = false;
if (oldVal != newVal) {
breakLoop = true;
}
)};
return breakLoop;
}

Dynamic ng-switch inside of ng-repeat

I am trying to create a switch based on a dynamic array of objects...
For example:
<div ng-switch on="currentItem">
<div ng-repeat="item in myItems" ng-switch-when="item.name">
<p>{{item.name}}</p>
<button ng-click="nextItem(item)">Next Item</button>
</div>
</div>
And then in my controller...
$scope.myItems = [{
"name": "one"
}, {
"name": "two"
}]
// Default first item
$scope.currentItem = $scope.myItems[0].name;
$scope.nextItem = function(med) {
for (var i = 0; i < $scope.myItems.length; i++) {
if ($scope.currentItem === $scope.myItems[i].name) {
if ($scope.myItems[i + 1] !== undefined) {
$scope.currentItem = $scope.myItems[i + 1].name
}
}
}
}
Basically, the dom should render a div for each of the items, and when a user clicks the Next Item button, currentItem should be updated, and the switch should trigger based on that.
I am not seeing the first result as I should (nothing is being rendered). Any help would be greatly appreciated.
Plunk: http://plnkr.co/edit/PF9nncd1cJUNAjuAWK22?p=preview
I have forked your plunkr: http://plnkr.co/edit/A9BPFAVRSHuWlmbV7HtP?p=preview
Basically you where not using ngSwitch in a good way.
Just use ngIf:
<div ng-repeat="item in myItems">
<div ng-if="currentItem == item.name">
<p>{{item.name}}</p>
<button ng-click="nextItem(item)">Next Item</button>
</div>
</div>
I've forked your plunkr: http://plnkr.co/edit/2doEyvdiFrV74UXqAPZu?p=preview
Similar to Ignacio Villaverde, but I updated the way your getting the nextItem().
$scope.nextItem = function() {
var next = $scope.myItems[$scope.myItems.indexOf($scope.currentItem) + 1];
if(next) {
$scope.currentItem = next;
}
}
And you should probably keep a reference in currentItem to the entire object, not just the name:
<div ng-repeat="item in myItems">
<div ng-if="item == currentItem">
<p>{{item.name}}</p>
<button ng-click="nextItem(item)">Next Item</button>
</div>
Much simpler!

Using ng-repeat to fill remaining elements for "x - available elements"

I am trying to have 4 boxes always show up. The boxes are filled by an image,but if there is no image available I want the boxes to be gray. The images variable holds an unknown amount of images, it could be 2 it could be 30.
<div class="container">
<div class="picture" ng-repeat="image in images | limitTo: 4"></div>
// PSEUDO CODE BELOW
<div class="empty" ng-repeat="i in 4 - images.length"></div>
// PSEUDO CODE ABOVE
</div>
"4 - images.length" is pseudo code, This is what I want to achieve, thinking if I only have 3 images available the result will be 1 gray box. But this syntax obviously does not work since ng-repeat require a collection.
Which made me try to provide it said collection through a function:
$scope.getNumber = function(num) {
return new Array(num);
}
and use in the following way:
<div class="empty" ng-repeat="n in getNumber(4 - cookbook.images.length)"></div>
But with no success.
<div class="picture" ng-repeat="image in images | limitTo: 4">hi</div>
<div class="empty" ng-repeat="n in [] | range:images.length:4">hey</div>
I've created a custom filter for it:
app.filter('range', function() {
return function(input, min, max) {
min = parseInt(min);
max = parseInt(max);
for (var i=min; i<max; i++) {
input.push(i);
}
return input;
};
});
DEMO
Possible repeat question
By using this updated syntax you can iterate with a specific index defined
HTML
<div ng-app="myapp">
<div ng-controller="ctrlParent">
<ul>
<li ng-repeat="i in getNumber(4) track by $index"><span>{{$index+1}}</span></li>
</ul>
</div>
Controllers
var app = angular.module('myapp',[]);
app.controller('ctrlParent',function($scope){
$scope.getNumber = function(num) {
return new Array(num);
}
});
Way to ng-repeat defined number of times instead of repeating over array?
I would probably handle this in whatever populates $scope.images. For example, something like:
$http.get('api/some/images?skip=10&take=4')
.success(function(response){
$scope.images = response;
for(var i = $scope.images.length; i<4; i++){
$scope.images.push('empty');
}
});
Then my template would just handle that:
<div class="container>
<div ng-repeat="image in images" ng-class="{empty:image=='empty',picture:image!='empty'}"></div>
</div>
wouldnt be easier to make operation modulo in controller?
function equal_number_of_images(){
for(var i = 0, add = 4 - $scope.some_files%4; i < add; i++ ){
$scope.some_files.push('');
}}

Categories