Pagination repeater has an active class that i must move when pagination button clicked - angularjs

Take the following pagination html and repeater:
<ul id="ProductListPagination" class="pagination">
<li class="disabled"><span aria-hidden="true">«</span><span class="sr-only">Previous</span></li>
<li ng-repeat="n in PageCount" ng-class="{active: n==1}"><a ng-click="Paginate( n )" href="#"><% n %> <span class="sr-only">(current)</span></a></li>
</ul>
How do I go about moving the active class on the repeater when one of the pagination buttons are pressed... Is there a built in angular way?...
context:
If there is no built in way in angular, how do I pass the dom element through to the Paginate( n ) function?
I have the receiving function:
$scope.Paginate = function( obj, page ){
// Remove currently active button's active class
$( "#ProductListPagination li.active" ).each( function() {
$( this ).removeClass( "active" );
} );
// Add to element just clicked on
$( obj ).parent().addClass( "active" );
...
}
And the html to go with that, you'll see I tried passing in this.
<li style="cursor:pointer" ng-repeat="n in PageCount" ng-class="{active: n==1}"><a ng-click="Paginate( this, n )" href="#"><% n %> <span class="sr-only">(current)</span></a></li>
But that doesn't work as this, is not a dom element.

Directives
This could be done with Directives, these allow you to define custom markup ie. an element, an attribute name, it even can hook onto class names. Then from that you can attach all sorts of stuff.
https://egghead.io/lessons/angularjs-first-directive
So you could make a "Pagination" directive.
and that would then be used something like:
<my-pagination pages="arrayInScope">
You can then provide an external template or even a string of markup (eww) for what needs to be either IN this element or to replace this element.
Another Solution
But this is a quick way and I guess it doesn't really need any more over complicating anyway.
Example on CodePen
From the markup I am calling the paginate function parsing ng-repeat's provided $index. In the paginate function in the js i then set it.
As angular digests this: ng-class="{ active : page == current }" will then re-evaluate.
But if you need access to the element for some reason then use directives. jQuery is best avoided when using Angular, if it is just a class change or a visibility toggle etc. then its best to let Angular do it for ya'
Hope that helps.

Related

Angular.js ng-repeat unique identifier

I am using ng-repeat on a <tr> tag to populate the <td> tags with data pulled from mysql and converted into Json. This works just fine. However, one of the <td> tags that I'm using contains a button.
What I would like to do, is have each of these buttons identified somehow in the DOM, so that I can target then with specific requests.
Example: Page loads, ng-repeat repeats a button 4 times. Each of these buttons would have an ng-click attached to it. I want each of them to open and filter different information in a json file.
Am I correct in assuming that ng-repeat would simply open the same item for each button, and how would I go about making them seperate? thanks.
You can do something like this on the front-end:
<button ng-repeat="item in items track by $index" ng-click="someFunction($index)" >Something happens</button>
Then in your controller:
$scope.someFunction = function (index) {
if (index === 1):
// etc.
else...
// Or use switch, whichever works for you.
You could create the specific function on each item in the array.
<button ng-repeat="button in buttons" ng-click="button.functionName()">{{button.name}}</function>
There's $index for that. It's a very good habit to take for any of your ng-repeat. Also don't forget bind-once if your buttons UI isn't subject to modifications once the DOM has loaded.
<ul>
<li ng-repeat="page in pages">
<a ng-class="{ testClass: $index == pageNumber }" ng-click="setPageNumber($index)">{{ page }} - index is : {{$index}}</a>
</li>
</ul>
http://jsfiddle.net/bonatoc/4z1t4gsm/3/
Also you could do (using bind-once):
<button
ng-repeat="button in ::buttons track by $index"
id="button_{{$index}}"
class="{{button['css_class']}}"
...given your buttons were a JSON object as well (beware, ng-repeat likes arrays, not objects. Arrays of objects are fine):
$scope.buttons = [
0: {
'css_class': someClass,
'functionToTrigger': function(...
// ...

Programmatically Open Bootstrap UI Accordion Generated by Nested Ng-Repeat when Filtered Array is Not Empty

I have a Bootstrap-UI accordion group that generates individual accordion menus using ng-repeat. The content under each accordion is also generated using a nested ng-repeat.
<accordion close-others="false">
<span data-ng-repeat="category in categories">
<accordion-group is-open="filterText.length > 0">
<accordion-heading>{{category}}: {{reportList.length}} Items </accordion-heading>
<div>
<ul>
<li data-ng-repeat="report in reportList = (getReports($parent.$index) | filter: filterText)">{{report}}</li>
</ul>
</div>
</accordion-group>
</span>
</accordion>
The content generated by the second ng-repeat needs to be searchable. When the search is executed, accordions that contain matching values should open and display the matching values. I know that the outside ng-repeat sees the filtered array and its length because i can display the length value in the view (i.e. {{reportList.length}} updates when the filter executes).
The problem comes when i try to put the reportList.length value in the is-open attribute of an <accordion-group>. Using is-open="reportList.length" seems to pass the literal into the directive. In desperation, I tried using is-open="{{reportList.length}}" but that throws the expected syntax error.
Here's a plunker that shows what i have working as well commented out lines that show the way i think it should work (lines 22 & 23). Any suggestions are more than welcome.
Your binding is-open inside of an ng-repeat which creates a child scope for each item (category). You need to bind to $parent.filterText.length as filterText is not a category property.
What you bind the is-open to, Angular needs to be able to assign to it, not just evaluate it. It can evaluate an expression like "foo == 5" but it cannot assign to it.
What I do is create a variable and bind to that, then the accordion can change it, and I can change it, and everybody's happy.
$scope.accordionSettings = {
IsOpen: {
Section1: true,
Section2: false
}};
In the markup:
<div uib-accordion-group is-open="accordionSettings.IsOpen.Section1">
... more markup ...
<div uib-accordion-group is-open="accordionSettings.IsOpen.Section2">

Angular JS ng-repeat and the 'this'

i'm new in AngularJS, but did some jQuery before. i've got a problem to understand how to get the clicked element / it's parent to make some changes like change the text, an icon or a class in the item where i made the click.
the simple HTML:
<ul ng-controller="basketCtrl">
<li ng-repeat="item in item">
<button ng-click="addToBasket(Itemid,this,whatever)">
<i class="myBasketicon">
<span>Buy now</span>
</button>
</li>
</ul>
what i want to do:
$scope.addTobasket = function (id, elem, whatever){
// to some JSON-Server-stuff - that works perfect
// now my problems, :
//change this -> myBasketIcon -> myOKicon
//change this -> span text Buy now-> Thanks for buying
// give the this -> li an class => 'changed'
}
I really tried a lot, f.e with ng-model in the tags, arrays... search the web half the day... but didn't find anything that matches my problem.
Maybe it's just the way of thinking not the angular way... so please help :O)
Kind regard from Hamburg, Germany
Timo
You should be able to do this by changing a property (angular way), no need to access the element in the ng-click handler,and using ng-class and angular binding on that property.
<ul ng-controller="basketCtrl">
<li ng-repeat="item in items" ng-class="{'changed': item.added}">
<button ng-click="addToBasket(item)">
<i ng-class="{'myBasketicon':!item.added,'myOKicon':item.added }">
<span>{{item.added ? "Thanks for buying" : "Buy now"}}</span>
</button>
</li>
</ul>
and in your handler just do:
$scope.addTobasket = function (item){
item.added = true;
}
Most cases, whole purpose of using angular is to avoid DOM manipulation and let angular manage it, you just deal with the models/viewmodels and bindings.
You should add methods for the icon class and text that change their results based on the state of the object, or use custom a custom directive. You definitely don't want to be doing any DOM manipulation (changing text/classes etc) the way you would have done with jQuery.
For the method-based approach, something like this for your markup:
<li ng-repeat="item in item">
<button ng-click="addToBasket(item)">
<i ng-class="getClass(item)">
<span>{{getMessage(item)}}</span>
</button>
</li>
and on your controller:
.controller('ShoppingListCtrl', function($scope) {
$scope.getClass = function(item) {
return item.inBasket ? 'myOkIcon' : 'myBasketIcon';
};
$scope.getMessage = function(item) {
return item.inBasket ? 'Thanks for buying' : 'Buy now';
};
})
This could also be done with a custom directive which is a super powerful way to do things (and definitely worth figuring out) but may be overkill for just starting out. If you find you are adding a lot if methods for doing these sorts of things go with directives.

Getting the jQuery object that has been clicked on in angular via $event

I have a list like this. It is pre rendered and so cannot make use of anything attached to ng-repeat.
<li ng-class="{ 'active': 1 == selectedIndex }">
Item 1
</li>
I want to be able to toggle the class of the <li> when the a is clicked.
Looking at some other answers to similar questions on here, it seems as though there is a variable associated with ng-repeat which means you can use an $index variable to achieve this. As this list is pre-rendered this is not available and so I guess I have to do it the jQuery way.
I see that I have access to an $event object but event.target only gives me the DOM element, I would like to be able to convert it into a jQuery object. Is this possible?
You have to think about this in a different way than you would normally. You can't modify the DOM within angular like you do with plain old jquery. Here is what you should do:
<li ng-class="{ active: selectedItem == item }" ng-repeat="item in list">
{{ item.name }}
</li>
Then in your controller:
$scope.selectedItem = null; // if this is loaded from a service then you can set it after it loads.
$scope.itemSelected = function( item ) {
$scope.selectedItem = item;
}
No need to play around with indexes, jquery, or one off code.
Charlie

How do I use ng-class without ng-repeat?

I am relatively new to AngularJS and am loving every moment of it! I have an unordered list of products. When the user hovers over them, I need the list element to have an active class set on them. I am currently doing it this way:
<ul>
<li ng-repeat="product in products" ng-mouseover="showDetails(product, $event)" ng-class="{active: current == product}">{{product}}</li>
</ul>
And my showDetails is as follows:
function showDetails(product, $event) {
$scope.current = product;
...some other logic...
}
Now, everything is working just fine, however, I was wondering if it was possible to set the class without the ng-repeat and having no product variable to bind to?
<ul>
<li ng-mouseover="showDetails($event)" ng-class="{<i don't know what to put here> }">foo</li>
<li ng-mouseover="showDetails($event)" ng-class="{<i don't know what to put here> }">bar</li>
<li ng-mouseover="showDetails($event)" ng-class="{<i don't know what to put here> }">A</li>
<li ng-mouseover="showDetails($event)" ng-class="{<i don't know what to put here> }">B</li>
</ul>
How should I write my showDetails function to set the class this time? My first try is:
function showDetails($event) {
var text = $event.target.textContent;
$scope.current = text
}
But what do I do in the ng-class attribute?
If a pure CSS solution is not possible then you can create a directive and toggle a CSS class using JQuery within that directive. Apply the directive on the ul as an attribute or class. In that directive you can do
iElement.find("li").hover(...

Resources