Insert something between elements generated with ng-repeat - angularjs

What would be the best way, to append an element in between elements generated using ng-repeat?
For example:
<div class="block" ng-repeat="position in [1,2,3,4,5,6,7,8,9,10]">{{position+1}}</div>
I would like to put a <br> after every 4th element. My guess would be to create a directive with the link.post. Tell me if I'm on the good track.
Thank you
jsFiddle: http://jsfiddle.net/uPMZU/

I'm not entirely sure what you're trying to achieve with the <br> but if your are trying to stack them in groups of four then you can do something using the $index variable from ng-repeat and a class change.
JSFiddle with this: http://jsfiddle.net/y44Cb/13/
HTML
<div ng-class="{block: ($index +1) % 4}" ng-repeat="position in [1,2,3,4,5,6,7,8,9,10]">
<div class="red-block">{{position+1}}</div>
</div>
CSS
div.red-block {
display: inline-block;
border: 1px solid red;
width: 50px;
height: 50px;
}
div.block {
float: left;
}

You could use ng-repeat-start and ng-repeat-end and then check whether you want to render the break in a function.
<div class="block" ng-repeat-start="position in [1,2,3,4,5,6,7,8,9,10]">
<div class="red-block">{{position+1}}</div>
</div>
with
<br ng-if="includeBreak(position)" ng-repeat-end>
or
<p ng-if="includeBreak(position)" ng-repeat-end></p>

You can use an ng-switch to do what you need
<div class="block" ng-repeat="position in [1,2,3,4,5,6,7,8,9,10]" data-ng-switch on="($index + 1) % 4 == 0 && $index > 0">
{{position+1}}
<br data-ng-switch-when="true">
</div>
jsfiddle: http://jsfiddle.net/7qrne/ if you inspect the "5" div you'll see it has the same with the "9" div, but none of the others.

Related

'ng-repeat' re rendering creates a flicker

I am using ng-repeat to list all the "zones". It works just fine on the first rendering and even when additional data from the server is added to the existing data it works without any issues or flickers. But when the "$rootScope.zones" is assigned a new value (the user can change the sort order from Alphabetical to Proximity, which sets a new value for the $rootScope.zones in that sorting order) the whole ng-repeat list goes blank for a few seconds and then works fine again.
This is not a very big issue except in the zones I have some text with CSS bubbles around. The text disappears but the bubbles remain.
How can I set the $rootScope.zones such that it doesn't create that flicker? Alternatively, how can I make sure that the "bubbles" only appear when the text is there.
This is my ng-repeat:
<ion-list class="card" ng-if="$root.currentMode.zoneBroadcast == true" can-swipe="true">
<div class="button-bar no-padding">
<button class="button bold" ng-class="{'button-calm': sortMethod === 'Alphabetically'}" ng-click="changeSortMethod('Alphabetically')">Alphabetically</button>
<button class="button bold" ng-class="{'button-calm': sortMethod === 'By Proximity'}" ng-click="changeSortMethod('By Proximity')">By Proximity</button>
</div>
<ion-item class="item no-border no-padding dark-bg" ng-repeat="zone in $root.zones track by zone.zoneName"
ng-if="checkVisibility(zone.zoneName)">
<div class="item-divider text-center">
<span>
Zone ID : <span>{{zone.zoneName}}</span>
</span>
</div>
<div class="item-body" ng-class="checkIfZoneMatches(zone.zoneName) ? 'zone-broadcast-green' : parseStringToInteger(zone.Trips.length) > 0 ? 'zone-broadcast-red' : checkIfZoneCanBookIn(zone.zoneName) ? 'zone-broadcast-yellow' : zone.Lineup.FreeList.length > 0 ? 'zone-broadcast-blue' : 'zone-broadcast-normal'">
<div class="row">
<div class="col">
<span ng-class="{'lineup free' : zone.Lineup.FreeList.length != 0}">{{zone.Lineup.FreeList.toString()}}</span>
<span ng-class="{'lineup loaded' : zone.Lineup.LoadedList.length != 0}">{{zone.Lineup.LoadedList.toString()}}</span>
<span ng-class="{'lineup accepted' : zone.Lineup.AcceptedList.length != 0}">{{zone.Lineup.AcceptedList.toString()}}</span>
<span ng-class="{'lineup offered' : zone.Lineup.OfferedList.length != 0}">{{zone.Lineup.OfferedList.toString()}}</span>
<span ng-class="{'lineup break' : zone.Lineup.BreakList.length != 0}">{{zone.Lineup.BreakList.toString()}}</span>
<span ng-class="{'lineup stc' : zone.Lineup.STCList.length != 0}">{{zone.Lineup.STCList.toString()}}</span>
</div>
</div> ...
I have tried ng-if, ng-hide, track by, etc. but nothing seems to work.
Here's the CSS I am using to create the bubbles.
.lineup{
background: white;
border-radius: 2em;
font-weight: bold;
font-size: 100%;
padding: 1%;
}
.free {
color: #00b140;
}
.loaded{
color: #6b46e5;
}
.accepted{
color: #0a9dc7;
}
.offered{
color: deeppink;
}
.break{
color: orangered;
}
.STC{
color: saddlebrown;
}
I expect the bubbles to appear only when the text is there. Moreover, when the sort order is changed by the user it should not go blank for over 2 seconds then render the template.
I would really appreciate your help.
Instead of using ng-if with ng-repeat, pipe to a filter:
SLOW
<ion-item class="item no-border no-padding dark-bg"
ng-repeat="zone in $root.zones track by zone.zoneName"
ng-if="checkVisibility(zone.zoneName)">
Faster
<ion-item class="item no-border no-padding dark-bg"
ng-repeat="zone in zones | filter : zoneFilter track by zone.zoneName">
$scope.zoneFilter = function(item) {
return $scope.checkVisibility(item);
};
For more information, see
AngularJS filter API Reference
Changing
zone.Lineup.FreeList.length != 0 to zone.Lineup.FreeList.length > 0 solved the bubble problem.

embedded stylesheet with angular-defined values

I am repurposing the HTML/CSS for this survey widget.
The CSS is built for a fixed number (5) of options (radio buttons), but I can dynamicize it using ng-repeat so that it can hold anywhere from 3 to 9 options, as the data requires.
The CSS needs to be dynamic as well to account for a variable number of options.
<div class="wrap">
<div class="widget-wrapper likert-scale-widget" action="">
<ul class='likert1'>
<li ng-repeat="option in item.response.options>
<input type="radio" name="likert" ng-value="option.value">
<label>{{option.label}}</label>
</li>
</ul>
</div>
</div>
The CSS makes extensive use of pseudo-classes:
div.likert-scale-widget .likert1:before {
top: 11px;
left: 9.5%;
display: block;
width: 78%;
}
You can't put pseudo-classes directly inline, so I can't do this:
<div class="likert-scale-widget">
<div class="likert1" style="width:78%">
So what I have to do is embed my styles in a element at the top of the page. Then I can target it with Angular:
<style>
div.likert-scale-widget .likert1:before {
top: 11px;
left: {{ 12 - item.response.options.length*1.1 }}%;
display: block;
width: {{ 74 + item.response.options.length }}%;
}
</style>
Problem is, this is very non-standard markup. My GUI hates the multiple use of parentheses.
Is there a better way of doing this?
You can use ng-style. Looks really hackish, but if your styles are dynamic towards the content, then I guess its justifiable.
Declare a variable to get the entire styles in a key value format.
$scope.likerStyle = {
'top': '11px';
'left': (12 - item.response.options.length*1.1) + '%'; // '+' automatically stringify this
'display': 'block';
'width': (74 + item.response.options.length) +'%';
};
Then at your markup, use this:
<div class="wrap">
<div class="widget-wrapper likert-scale-widget" action="">
<ul class='likert1' ng-style='likerStyle'>
<li ng-repeat="option in item.response.options>
<input type="radio" name="likert" ng-value="option.value">
<label>{{option.label}}</label>
</li>
</ul>
</div>

ng-click first apply class, then execute function

I have a shopping cart (a rootScope array) which gets turned into a list of items in it, including a button to delete that item from the cart array (the red X).
I don't have enough reputation to add an image, so here's a link to what it looks like
What I want to have happen is when I click one of the red X buttons, the item first does an animation(some sort of fade out), and then the actual cart has the item spliced from it. Using ng-click I am able to either do one or the other, but not both. When both is applied the animation doesn't trigger because it doesn't have time to. Is there a way to wait for the animation to finish, then perform the function?
(the animation executed by applying a class to the div on ng-click, so possibly a watch for class change?)
Here's my code. The code won't work in the snippet but you can see my functions and html.
$scope.removeFromCart = function(removedGame) {
index = $rootScope.cartArray.indexOf(removedGame);
$rootScope.cartArray.splice(index, 1);
};
$(".disappear").hasClass('fadeOutRight')(function(){
$scope.removeFromCart(cartArray[0]) ;
});
.cartGameDiv {
height: 140px;
width: auto;
}
<div ng-repeat = "newGame in cartArray" ng-class="disappear">
<div>
<div class="col-sm-11 col-lg-11 col-md-11 cartGameDiv">
<div class="thumbnail">
<div class="pull-left">
<img style="height: 100px; width: 213px; padding: 5px; margin-top: 5px" src="{{newGame.thumbnail}}" alt="">
<div id="ratingDiv" style="margin-left: 8px; margin-right: 8px; margin-bottom: 5px;">
<div style="display: inline-block" ng-bind-html="getTrustedHtml(newGame)"></div>
<p class="pull-right" style="color: #d17581">{{newGame.numberReviews}} reviews</p>
</div>
</div>
<div class="caption">
<h4 style="margin-top: 0" class="pull-right">{{newGame.price}}</h4>
<h4 style="margin-top: 0"><a class="categoryGameName" href="#details/{{myGamesList.indexOf(newGame)}}">{{newGame.name | removeSubName}}</a>
</h4>
<p>{{newGame.description.substring(0,290) + '...'}}</p>
</div>
</div>
</div>
</div>
<div class="col-sm-1 col-lg-1 col-md-1 cartGameDiv">
<img style="margin-bottom: 10px; margin-top: 10px;" src="images/glyphIconCheckmark.png" alt=""/>
<img ng-click="disappear='animated fadeOutRight'; removeFromCart(newGame)" style="margin-bottom: 10px" src="images/glyphIconRemoveGame.png" alt=""/>
<img src="images/glyphIconLike.png" alt=""/>
</div>
</div>
If you have any idea how to delay the function call until after the animation I'd really appreciate it! Thanks!
This is very simple to do using ngAnimate. Add the ngAnimate script to your page (you can get this from numerous CDNs), include ngAnimate as a dependency to your module and then just add some simple CSS.
.ng-leave{
-webkit-animation: fadeOutRight 1s;
-moz-animation: fadeOutRight 1s;
-o-animation: fadeOutRight 1s;
animation: fadeOutRight 1s;
}
In your example, you need not do any work applying the class yourself, ngAnimate will do it for you.
Here is a Plunker demonstrating how you would do it.

AngularJS - ng-repeat horizontally x number of times, then new line

I am trying to perform a simple ng-repeat on an <li>. In the past, i have created vertical ng-repeat. I am now trying to create a horizontal one, however, one that displays 4 items, then starts the ng-repeat again on a new line.
The way i have gone about this is using the grid-wrap technique (CSS) found here: http://builtbyboon.com/blog/proportional-grids
So each <li>, has a CSS class/width of one-quarter (25%).
Is this the correct/Angular way of going about it? Or should i be using some kind of $index on the <li> and triggering a <br> when $index == 3 ?
HTML:
<div ng-controller="MainCtrl">
<ul class="grid-wrap one-whole">
<li ng-repeat="product in Products" class="grid-col one-quarter">
<div class="product-container">
<div>{{ product.ModelNo }}</div>
<img ng-src="{{product.ImgURL}}" width="80%"/>
<div>${{ product.Price }}</div>
</div>
</li>
</ul>
</div>
CSS:
.grid-wrap {
margin-left: -3em;
/* the same as your gutter */
overflow: hidden;
clear: both;
}
.grid-col {
float: left;
padding-left: 3em;
/* this is your gutter between columns */
width: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.one-quarter {
width: 25%;
}
Here's my plunkr: http://plnkr.co/edit/REixcir0gL0HGCTvclYN?p=preview
Any other improvements you see feel free to add.
Thanks
I did some research and found this answer:
Customize ng-repeat in AngularJS for every nth element
<div class="section">
<div ng-repeat="items in MyList">
<img ng-click="AddPoint($index)" src={{items.image}} />
<span>{{items.currentPoint}}/{{items.endPoint}}</span>
<br ng-if="!(($index + 1) % 4)" />
</div>
So you could use this:
<br ng-if="!(($index + 1) % 4)" />
There doesn't seem to be a better way. You probably can't get around using index.

Ng-Repeat - Angular JS

Here is my ng-repeat
<div ng-if="feed.link" articlesource source='{{article.link}}' class="post" ng- repeat="article in data.value.items | limitTo:5">
<img ng-if="articlesource != 'FACEBOOK'" title="{{article.title}}" alt="{{article.title}}" class="blog-image-ind" src="{{article.image}}" height="100" width="240" style="float:left;">
<img ng-if="articlesource == 'FACEBOOK'" title="{{article.title}}" alt="{{article.title}}" class="blog-image-ind" src="/shared/images/facebook_long.png" height="100" width="240" style="float:left;">
<div class="postMid">
<H3><a style="color: #A4A4A4;" href="{{article.link}}">{{article.title | htmlToPlaintext | ellipsis:40}}</a></H3>
<div> {{article.description | htmlToPlaintext | blogellipsis:160}}</div>
</div>
<div class="postRight">
<p> {{article.pubDate.substr(0, article.pubDate.indexOf(':')-2).trim()}}
<br>
<div ng-if="feed.name != 'THE FEED'"><p ng-if="articlesource != 'FACEBOOK' || feed.name != 'THE FEED'">BY {{article['dc:creator'] | uppercase}}</p></div>
<p ng-if="feed.name == 'THE FEED'">{{articlesource}}</p>
</p>
<a ng-if="articlesource != 'FACEBOOK'" class="postRight" href="https://sam.containerandpackaging.com">{{article['slash:comments']}} Comments </a>
</div>
<br clear="all">
</div>
I would like to add something into my ng-repeat that would allow a border on all of the bottom of the divs except for the last one.
Use Css
div.post {
border-bottom: #ff0000;
}
div.post:last-child {
border-bottom: none;
}
The :last-child selector matches every element that is the last child of its parent.
Put a class on the repeater with the condition that $last isnt true:
ng-class="{'myBorderClass : !$last }"
$last is true on the last iteration of your repeater.
Ypu can use ng-repeat's $last value to set up ng-class. Something like pseudo code below.
<div ng-class="{'noborderclass':$last}"></div>
You have two options: Javascript (Angular), or CSS. With angular, you can use $last like so:
<div ng-class="{'with-border': !$last}">
With CSS, you can use the selector :last-of-type:
div {
border-bottom: 1px solid black;
}
div:last-of-type {
border-bottom: 0;
}
Personally, I go with CSS, cause I trust CSS more than JS.

Resources