Loop variables in Vue.js similar to AngularJS - javascript

I my vue.js application I want to know the last repeated element in a v-for directive. I know there is something similar in angularjs with the $last loop variable in its ngRepeat directive:
<div ng-repeat="(key, value) in myObj">
<span ng-if="$last">Shows if it is the last loop element</span>
</div>
Is there any equivalent in vue.js which I am not aware of or do I have to implement some own logic?

It works for me :)
<div v-for="(item, index) in items">
<span v-if="index === (items.length-1)">This is the last loop element</span>
</div>

you can also put the logic in a computed property :
<div v-for="(value, key) in myObj">
<span v-if="key === last">This is the last loop element : {{ value }}</span>
</div>
//...
computed: {
last() {
let keys = Object.keys(this.myObj)
return keys.slice(-1)[0]
}
}
fiddle here

There's no equivalent for this in Vue, no.

You can do this instead, check for index if it is last
<div v-for="(value, key, index) in myObj">
<div v-if="index === Object.keys(myObj).length-1"> my content</div>
</div>

You can try this
<div v-repeat="myObj">
<span v-if="$index === (myObj.length-1)">Shows if it is the last loop element</span>
</div>

Based on #Nisha answer I have been doing this:
<template v-for="(obj, index) in originalData">
<template v-if="index == 0 || (index >= 1 && obj.valueA !== originalData[index - 1].valueA)">
{{ obj.valueA }}
</template>
</template>
I want the loop to always happen the first time, but to conditionally check a value for every loop afterwards. I am using <template> tags to avoid outputting unnecessary markup. In my example originalData is an array of objects.

Related

VueJS Loop remaining

I have a little problem. I would like to create a loop to display my articles the problem is that. I want it to never be empty basically if I have 0 articles but. I want 10 this one will display 10 skeleton if I have 1 item this will remove 1 skeleton to replace it with my item.
I don't know if I was clear enough so here is a little picture.
Basically I have 6 dark gray rectangles but if I have 1 item I would only have 5 if I have 6 I would have no dark gray rectangle at all.
I use
<transition-group name="fade" tag="div" class="badge__grid">
<template v-for="(n, i) in badges.data">
<div :key="n + i" class="badge__item">
<img
class="badge__item--picture"
:src="
'https://images.habbo.com/c_images/album1584/' +
n.badge_code +
'.gif'
"
:alt="n.badge_code"
/>
<span class="badge__item--code">
{{ n.badge_code }}
</span>
</div>
</template>
<div
v-for="i in badges.remaining"
:key="i"
class="badge__item badge__item--empty"
></div>
</transition-group>
It works but I find it too many and I can't simplify my code
You have to use two loops. Using a computed property you can simplify it a bit further:
<div v-for="a in articles" >
<h4>{{a.title}}</h4>
</div>
<div v-for="r in remaining" >
</div>
and the computed property:
remaining(){
let rem = 10 - this.articles.length;
return(rem > 0?rem:0)
}
Code Sandbox: https://codesandbox.io/s/empty-cherry-vy9fw?file=/src/components/Looper.vue:516-599

ngIf check if object is empty [done]

i have a list name "day".
export class Day{
id:number;
name: string;
items: Object;
}
i want to display "Hello" when items is empty. I try to used *ngIf but it doesn't work.
This is my code.
<div class=" list" *ngFor="let day of days">
<div class="list-name">
<strong>
{{day.name}}
</strong>
<div *ngIf= "day.items === 0">
Hello
</div>
</div>
<div *ngFor="let item of day.items">
<div class="column" [style.background-image]="'url(' + item.photoPath + ')'">
<h4 class="item-name">{{item.name}}</h4>
</div>
</div>
</div>
You have to use length to check the array size
<div *ngIf= "day.items?.length === 0">
Try this
<div *ngIf= "day.items.length === 0">
You can use <div *ngIf= "day.items == null || day.items.length === 0"> for check items is null or empty.
Demo https://stackblitz.com/edit/angular-scssrw
As I seem items is not an array. It is an object. Using ngFor over an Object would resuslt in an error.
Secondly, you should use .length over an array to get the count of Objects. so it should be as,
<div *ngIf= "day?.items?.length === 0">
You can simply make use of conditional statement expression inside your interpolation without using 'ngIf'.
...
<strong>
{{ (day.item && day.item?.length) ? day.name : day.name + 'Hello' }}
</strong>
...
Actually the question is about finding the length of object that is only possible using Object.keys(myObj).length. In Angular you can use it as:
in .ts file:
objectKeys = Object.keys;
in html file:
<div *ngIf="objectKeys(myObj).length > 0"></div>
Here is the reference:
https://stackblitz.com/edit/angular-object-length-xzxajl?file=app%2Fapp.component.html

Trigger ng-click inside ng-repeat when ng-repeat item.length=1

I want to trigger automatic loadProduct function when products.length==1
Bellow my angular code.
<div class="prodItem prodItem-nos-{{products.length}} prodItem-item-number-{{$index}}"
ng-repeat="product in products track by $index"
ng-click="loadProduct(product.id)"
uib-tooltip="{{ product.name }}">
<div class="prodMeta">
<div class="prodName" ng-bind="product.name"></div>
<div class="prodDescr" ng-bind="product.description"></div>
</div>
<div class="prodBuyNow">
<button ng-click="loadProduct(product.id)">Choose</button>
</div>
</div>
If you want to trigger function when item in scope changes you can use scope.$watch()
for example :
scope.$watch('products',function(oldValue,newValue){
if(newValue.length === 1){
executeFunction();
}
});
See :
https://docs.angularjs.org/api/ng/type/$rootScope.Scope
ng-click="if(products.length === 1){ loadProduct(product.id); }"
I don't like too much conditions inside ng-click. Being products a scope variable, you can just add this logic inside your loadProduct function, adding a condition at the beginning like
if($scope.products.length === 1)
and then execute your code.
If, you want to call loadProduct() is only one product is there call it in api which fetched the products
Eg:
$http.get("/api/products")
.then(function(response) {
$scope.products = response.data;
if($scope.products.length == 1)
{
$scope.loadProduct($scope.products[0].id)
}
});
you can use ng-init function
and check your prodt list in the function
<div ng-repeat="product in products track by $index" ng-init="yourFunctionName()">
<div class="prodMeta">
<div class="prodName" ng-bind="product.name"></div>
<div class="prodDescr" ng-bind="product.description"></div>
</div>
<div class="prodBuyNow"><button ng-click="loadProduct(product.id)">Choose</button></div>
</div>
</div>

Hide elements in nested ng-repeat

I'm trying to make a nested list of items where user can hide either a certain item or a group of nested items. So far, I'm making use of $indexand I've got this:
<div ng-controller="ItemCtrl">
<div ng-repeat="x in xs" ng-hide="hidden == $index">
<span>{{ x.name }}</span>
<button ng-click="hide($index)">Collapse</button>
<div ng-repeat="y in x.ys" ng-hide="hidden == [x.$index, $index]">
<span>{{ y.name }}</span>
<button ng-click="hide([x.$index, $index])">Collapse</button>
<div ng-repeat="z in y.zs" ng-hide="hidden == [x.$index, y.$index, $index]">
<span>{{ z.name }}</span>
<button ng-click="hide([x.$index, y.$index, $index])">Collapse</button>
</div>
</div>
</div>
</div>
With this controller:
angular.module("app", [])
.controller("ItemCtrl", function($scope) {
$scope.xs = [...]; // My data here
$scope.hidden = -1; // Nothing hidden yet
$scope.hide = function(item) {
$scope.hidden = item;
};
});
It does work. The downside is, there will be too many $index to mantain while the nested list is going deeper. Plus, I have to write all the conditional on every nest level.
My question is, is there any alternative which is simpler, more reliable, and if possible, will generate automatically no matter how many nested item that I have?
please check ui-tree, I think it's what you looking for.
github ui-tree
and the demo
demo

how to use if statement in ng repeat using angular 1.0.8

My angular is 1.0.8-stable
My objective is to display data in rows of 3.
My html should look like
<div class="table-row">
<div class="item">item1</div>
<div class="item">item2</div>
<div class="item">item3</div>
</div>
My pseudo code is if the $index mod 3 == 0 then I will display <div class="table-row">.
if the $index mod 3 == 2, then I will display </div>.
So far, I have this.
<div ng-repeat='item in my_works.items'>
<!-- if index mod 3 == 0 show <div>-->
<!-- if index mod 3 == 2 show </div>-->
</div>
Perhaps I was thinking it in the wrong way as there may be a more efficient way to do this in angularjs?
You can use (key, value) in expression – where key and value can be any user defined identifiers, and expression is the scope expression giving the collection to enumerate.
You can try something like this
<div ng-repeat='(index, item) in my_works.items'>
<div class="table-row" ng-show="index % 3 == 0">
</div>
</div>
Reference
Since you can't use ng-if in 1.0.8 here are two variations that solve the problem. They both wrap 3 items into a table-row div
The outer loop counts off in groups of three, so it fires once per outer table-row div. The second approach uses a filter to control the loop range but it should have better performance. The first approach doesn't require a new filter.
Then the inner ng-repeat loops through the 3 items within a row. It uses slice to get just the 3 array items needed for that particular row.
Here's a fiddle with both of the variations working: http://jsfiddle.net/f8D8q/4/
Option 1: Solution without a new filter:
<div ng-repeat='itemtmp in items|limitTo:(items.length/3)+1'>
<div class="table-row">
<span ng-repeat='item in items.slice($index*3,($index*3+3))'>
{{item.name}} - {{$index}}
</span>
</div>
</div>
Option 2: More performant solution using range filter from http://www.yearofmoo.com/2012/10/more-angularjs-magic-to-supercharge-your-webapp.html#more-about-loops
<div ng-repeat="n in [] | range:(items.length/3)+1">
<div class="table-row">
<span ng-repeat='item in items.slice($index*3,($index*3+3))'>
{{item.name}} - {{$index}}
</span>
</div>
</div>
and the associated range filter:
app.filter('range', function () {
return function (input, total) {
total = parseInt(total);
for (var i = 0; i < total; i++) {
input.push(i);
}
return input;
};
});
Both are tested (down to 1.0.2) and work.
Convert your flat list of items to a list of lists of 3 items.
Iterate over the list of lists.
<div class="table-row" ng-repeat="list in listOfLists">
<div class="item" ng-repeat="item in list">
{{ item }}
</div>
</div>
If you already have a list, items, you could add a filter chunked and replace listOfLists with items|chunked:3, assuming you implement chunked something like this:
app.filter('chunked', function(){
return function(list, chunkSize){
var chunks = [];
angular.forEach(list, function(element, i){
if(i % chunkSize === 0){
currentChunk = [];
chunks.push(currentChunk);
}
currentChunk.push(element);
});
return chunks;
};
});
I think that matches what you are trying to do; here's a plunker: http://plnkr.co/edit/3h7JprbXFVwnNZErj7hl
I didn't get a chance to test with old Angular though.

Categories