Different class for the last element in ng-repeat - angularjs

I am creating a list using ng-repeat something like this
<div ng-repeat="file in files">
{{file.name}}
</div>
But for the last element alone I would like to have a class (<div class="last">test</div>) included to it. how can i achieve this using ng-repeat?

You can use $last variable within ng-repeat directive. Take a look at doc.
You can do it like this:
<div ng-repeat="file in files" ng-class="computeCssClass($last)">
{{file.name}}
</div>
Where computeCssClass is function of controller which takes sole argument and returns 'last' or null.
Or
<div ng-repeat="file in files" ng-class="{'last':$last}">
{{file.name}}
</div>

It's easier and cleaner to do it with CSS.
HTML:
<div ng-repeat="file in files" class="file">
{{ file.name }}
</div>
CSS:
.file:last-of-type {
color: #800;
}
The :last-of-type selector is currently supported by 98% of browsers

To elaborate on Paul's answer, this is the controller logic that coincides with the template code.
// HTML
<div class="row" ng-repeat="thing in things">
<div class="well" ng-class="isLast($last)">
<p>Data-driven {{thing.name}}</p>
</div>
</div>
// CSS
.last { /* Desired Styles */}
// Controller
$scope.isLast = function(check) {
var cssClass = check ? 'last' : null;
return cssClass;
};
Its also worth noting that you really should avoid this solution if possible. By nature CSS can handle this, making a JS-based solution is unnecessary and non-performant. Unfortunately if you need to support IE8> this solution won't work for you (see MDN support docs).
CSS-Only Solution
// Using the above example syntax
.row:last-of-type { /* Desired Style */ }

<div ng-repeat="file in files" ng-class="!$last ? 'class-for-last' : 'other'">
{{file.name}}
</div>
That works for me! Good luck!

You could use limitTo filter with -1 for find the last element
Example :
<div ng-repeat="friend in friends | limitTo: -1">
{{friend.name}}
</div>

The answer given by Fabian Perez worked for me, with a little change
Edited html is here:
<div ng-repeat="file in files" ng-class="!$last ? 'other' : 'class-for-last'">
{{file.name}}
</div>

Related

Two different HTML with AngularJS

I'm in a condition where I've got a JSON file with all my data.
Those data are generating an HTML component of my code.
The issue is that, occasionally, the component code needs to change: in particular, a <div> has to become a <a>, due to the presence of a link.
The end result should be like this:
<div class="container">
<div class="a b c">
content
</div>
<div class="a b c">
content
</div>
<a href="#" class="a b c">
content
</div>
</div>
my data structure is something like this:
'element1':{
'properties' = 'properties',
'isLink' = 'true'
},
'element2':{
'properties' = 'properties',
'isLink' = 'false'
},
I am printing the <div> or the <a> with a loop of Angular, but can't find a clean way to tell the code something like "if 'isLink' = 'true' print an <a>, else print a <div>".
The closest solution I've found is this one below, which prints a useless span that breaks all the CSS:
<div class="container>
<span ng-repeat="element in row.element">
<div ng-if="element.isLink == false">
content
</div>
<a ng-if="element.isLink == true">
content
</a>
</span>
</div>
Has anyone a solution to make it cleaner?
Thank you all.
You can do that by using ng-repeat-start and ng-repeat-end:
<div class="container">
<div ng-repeat-start="item in array" ng-if="!item.isLink">...</div>
<a ng-repeat-end ng-if="item.isLink" href="#">...</a>
</div>
I would replace the parent span with a div element because divs have
display: block;
by default, while spans have
display: inline;
and probably that is why your css is breaking. Your html/angularjs code seems perfectly fine to me. Try to solve your css problem instead.

AngularJS Linky set ng-click on all links

I'm using AngularJS linky filter to make links on a snippet, and it works great, but I want to make call a custom function before executing the url.
So my question is, how can i set a ng-click attribute with linky filter ?
Hope it makes sense?
my code looks like:
<div class="item" href="#" ng-repeat="message in messages">
<p class="message" ng-bind-html="message.message | linky:'_blank':open(url)"></p>
</div>
Following on from gtlambert's comment, it should work. I've used it before. If you add the following, it will run the alert first, and the proceed to follow the link:
HTML
<p class="message" ng-bind-html="message.message | linky:'_blank':open(url)" ng-click="alertFunc()"></p>
</div>
JS
$scope.alertFunc = function() {
alert("doSomething");
}

How to use ng-switch in angularjs

I was trying to display some content using ng-switch:
<div ng-app ng-controller="ctrl" >
<div ng-repeat="list in statusList">
<p ng-switch on="list">
<span ng-switch-when="incorrect">
i am incorrect
</span>
<span ng-switch-when="stuff">
i am stuff
</span>
<span ng-switch-when="wrong">
i am wrong
</span>
<span ng-switch-default>
Correct
</span>
</p>
</div>
</div>
controller
function ctrl($scope){
$scope.statusList=["incorrect","wrong","stuff"];
}
Getting the output like this:
i am incorrect
i am wrong
i am stuff
But I need output to display in order what I specified. i.e.
i am incorrect
i am stuff
i am wrong
How can I do this, and one important point is we should not change the order in controller.
You can create custom order by function, something like this.
<div ng-repeat="list in statusList | orderBy:myValueFunction">
You can order it alphabetically (with filter), but I would not recommend it because you will probably add new element that are not ordered alphabetically.
ng-repeat iteration uses the order of your list. You can either define a custom filter to order the list as you want or use another approach:
Define a 'contains' filter, returning true if an array contains an element:
.filter('contains', [function() {
return function(array, element) {
return array.indexOf(element) > -1;
}
}])
Use ng-ifs for your template (no repeat):
<div ng-if="statusList | contains : 'incorrect'">i am incorrect</div>
<div ng-if="statusList | contains : 'stuff'">i am stuff</div>
<div ng-if="statusList | contains : 'wrong'">i am wrong</div>
This way you can define whatever order you want to have.
Plunkr: http://plnkr.co/edit/yaYodJAVCVQ55KbOrf3A?p=preview

Get id of parent element of currently clicked element in AngularJS

How do I get the id of a parent element of currently clicked element in AngularJS?
<div id="d8" class="menutitles ng-scope" ng-repeat="title in list">
<div class="right" ng-click="showsubmenu($event)">+</div>
<div class="left" ng-click="showsubmenu($event)">Unit 9</div>
</div>
How to get the value d8 in showsubmenu($event) function?
Below is what I tried but it doesn't work
$scope.showsubmenu=function(obj)
{
alert(obj.target.parent.attributes.id)
}
It should be parentNode, not just parent:
alert(obj.target.parentNode.id);
Also attributes is redundant as you can access id property directly.
But note, that since you have ngRepeat, it will create invalid markup, since ids are going to be duplicated. You probably want to fix this too, maybe like this or use classes:
<div id="d8{{$index}}" class="menutitles ng-scope" ng-repeat="title in list">
<div class="right" ng-click="showsubmenu()">+</div>
<div class="left" ng-click="showsubmenu()">Unit 9</div>
</div>
<div id="d8" class="menutitles ng-scope" ng-repeat="title in list">
<div class="right" ng-click="showsubmenu()">+</div>
<div class="left" ng-click="showsubmenu()">Unit 9</div>
</div>
It should be enough :D
function showsubmenu($event){
$($event.target).parent();
}
Have a nice day
As an addition to Pedro's answer I'd say that using the jQuery method closest would be preferable.
function showsubmenu($event){
var parent = $($event.target).closest('.yourclass'); // or any selector you prefer
}

Angular.js ng-switch-when not working with dynamic data?

I'm trying to get Angular to generate a CSS slider based on my data. I know that the data is there and am able to generate it for the buttons, but the code won't populate the ng-switch-when for some reason. When I inspect the code, I see this twice (which I know to be correct as I only have two items):
<div ng-repeat="assignment in assignments" ng-animate="'animate'" class="ng-scope">
<!-- ngSwitchWhen: {{assignment.id}} -->
</div>
My actual code:
<div ng-init="thisAssignment='one'">
<div class="btn-group assignments" style="display: block; text-align: center; margin-bottom: 10px">
<span ng-repeat="assignment in assignments">
<button ng-click="thisAssignment = '{{assignment.id}}'" class="btn btn-primary">{{assignment.num}}</button>
</span>
</div>
<div class="well" style="height: 170px;">
<div ng-switch="thisAssignment">
<div class="assignments">
<div ng-repeat="assignment in assignments" ng-animate="'animate'">
<div ng-switch-when='{{assignment.id}}' class="my-switch-animation">
<h2>{{assignment.name}}</h2>
<p>{{assignment.text}}</p>
</div>
</div>
</div>
</div>
</div>
EDIT: This is what I'm trying to emulate, though with dynamic data. http://plnkr.co/edit/WUCyCN68tDR1YzNnCWyS?p=preview
From the docs —
Be aware that the attribute values to match against cannot be expressions. They are
interpreted as literal string values to match against. For example, ng-switch-when="someVal"
will match against the string "someVal" not against the value of the expression
$scope.someVal.
So in other words, ng-switch is for hardcoding conditions in your templates.
You would use it like so:
<div class="assignments">
<div ng-repeat="assignment in assignments" ng-animate="'animate'">
<div ng-switch="assignment.id">
<div ng-switch-when='1' class="my-switch-animation">
<h2>{{assignment.name}}</h2>
<p>{{assignment.text}}</p>
</div>
</div>
</div>
Now this might not fit your use case exactly, so it's possible you'll have to rethink your strategy.
Ng-If is probably what you need — also, you need to be aware of "isolated" scopes. Basically when you use certain directives, like ng-repeat, you create new scopes which are isolated from their parents. So if you change thisAssignmentinside a repeater, you're actually changing the variable inside that specific repeat block and not the whole controller.
Here's a demo of what you're going for.
Notice I assign the selected property to the things array (it's just an object).
Update 12/12/14: Adding a new block of code to clarify the use of ng-switch. The code example above should be considered what not to do.
As I mentioned in my comment. Switch should be thought about exactly like a JavaScript switch. It's for hardcoded switching logic. So for instance in my example posts, there are only going to be a few types of posts. You should know a head of time the types of values you are going to be switching on.
<div ng-repeat="post in posts">
<div ng-switch on="post.type">
<!-- post.type === 'image' -->
<div ng-switch-when="image" class="post post-image">
<img ng-src="{{ post.image }} />
<div ng-bind="post.content"></div>
</div>
<!-- post.type === 'video' -->
<div ng-switch-when="video" class="post post-video">
<video ng-src="{{ post.video }} />
<div ng-bind="post.content"></div>
</div>
<!-- when above doesn't match -->
<div ng-switch-default class="post">
<div ng-bind="post.content"></div>
</div>
</div>
</div>
You could implement this same functionality with ng-if, it's your job to decide what makes sense within your application. In this case the latter is much more succinct, but also more complicated, and you could see it getting much more hairy if the template were any more complex. Basic distinction is ng-switch is declarative, ng-if is imperative.
<div ng-repeat="post in posts">
<div class="post" ng-class="{
'post-image': post.type === 'image',
'post-video': post.type === 'video'">
<video ng-if="post.type === 'video'" ng-src="post.video" />
<img ng-if="post.type === 'image'" ng-src="post.image" />
<div ng-bind="post.content" />
</div>
</div>
Jon is definitely right on. Angular does not support dynamic ngSwitchWhen values. But I wanted it to. I found it actually exceptionally simple to use my own directive in place of ngSwitchWhen. Not only does it support dynamic values but it supports multiple values for each statement (similar to JS switch fall-throughs).
One caveat, it only evaluates the expression once upon compile time, so you must return the correct value immediately. For my purposes this was fine as I was wanting to use constants defined elsewhere in the application. It could probably be modified to dynamically re-evaluate the expressions but that would require more testing with ngSwitch.
I am use angular 1.3.15 but I ran a quick test with angular 1.4.7 and it worked fine there as well.
Plunker Demo
The Code
module.directive('jjSwitchWhen', function() {
// Exact same definition as ngSwitchWhen except for the link fn
return {
// Same as ngSwitchWhen
priority: 1200,
transclude: 'element',
require: '^ngSwitch',
link: function(scope, element, attrs, ctrl, $transclude) {
var caseStms = scope.$eval(attrs.jjSwitchWhen);
caseStms = angular.isArray(caseStms) ? caseStms : [caseStms];
angular.forEach(caseStms, function(caseStm) {
caseStm = '!' + caseStm;
ctrl.cases[caseStm] = ctrl.cases[caseStm] || [];
ctrl.cases[caseStm].push({ transclude: $transclude, element: element });
});
}
};
});
Usage
Controller
$scope.types = {
audio: '.mp3',
video: ['.mp4', '.gif'],
image: ['.jpg', '.png', '.gif'] // Can have multiple matching cases (.gif)
};
Template
<div ng-switch="mediaType">
<div jj-switch-when="types.audio">Audio</div>
<div jj-switch-when="types.video">Video</div>
<div jj-switch-when="types.image">Image</div>
<!-- Even works with ngSwitchWhen -->
<div ng-switch-when=".docx">Document</div>
<div ng-switch-default>Invalid Type</div>
<div>

Resources