Angular JS ng-repeat and the 'this' - angularjs

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.

Related

Removing items from an array with Angular

Yes, it has been asked before, and I've read all the answers but nothing seems to work. So I'm asking for an extra pair of eyes to see if you can find any singularity in my code that is making it not work as it should. (I tried this code and logic somewhere else and it seems to work fine). No errors in the console by the way
I am simply trying to remove an item from the view when someone clicks the x on the picture.
Here is the controller
app.controller('galleryController', ['$scope', '$http', function($scope, $http) {
$http.get('data/galleries.json').success(function(data){
$scope.galleries = data;
}).error(function(error) {
console.log(error);
});
$scope.removeGalleryItem=function(gallery){
var removedGallery = $scope.galleries.indexOf(gallery);
$scope.galleries.splice(removedGallery, 1);
};
}]);
and my view
<div class="col-xs-12 col-md-3" ng-repeat="gallery in galleries" >
<a class="gallery-item" ng-href="{{gallery.img}}" ng-class="{true:'active',false:''}[checked]"
title="Nature Image 1" >
<div class="image">
<img ng-src="{{gallery.img}}" alt="Nature Image 1"/>
</div>
<div class="meta">
<strong>{{gallery.title}}</strong>
<span>{{gallery.desc}}</span>
</div>
</a>
<ul class="gallery-item-controls">
<li><label class="check"><input type="checkbox" class="icheckbox" ng-model="checked" /></label></li>
<li><span class="gallery-item-remove"><i class="fa fa-times" ng-click="removeGalleryItem(gallery)"></i></span></li>
</ul>
</div>
Angular 1.5.8
Thanks
You can pass an $index in your click function like this.
<i class="fa fa-times" ng-click="removeGalleryItem(galleryItem, $event , $index)">
and use $scope.galleries.splice(index, 1); inside your click function removeGalleryItem, make sure you have index parameter too like this.
$scope.removeGalleryItem = function(gallery , event, index){
$scope.galleries.splice(index, 1);
};
Hope this helps..
After doing some research I think the problem is that galleryController is defined somewhere in your markup but the elements in the gallery are not inside of where that controller is defined.
Referring to http://joli.sitedev.online/#/gallery. In file slidesController.js where galleryController is defined I put a break here and the code pauses:
I also put a break point here but the code does not pause when clicking on a delete button:
Looking at the markup I can't see any sign of ng-controller="galleryController" so I can't see how galleries in the ng-repeat is populated:
Maybe it is through this:
If it is through that then it would explain things as that directive has its own controller. Any further information would help and I'm sure we can clear this up.
If I understood correctly your question, if you want to delete a particular element both from DOM and Array containing these particular elements you can do the following:
<!-- Intercept that particular Element with $event-->
<i class="fa fa-times" ng-click="removeGalleryItem(galleryItem, $event)">
Lets supposing you are repeating some galleryItems and they have a name property.
And on your controller:
$scope.removeGalleryItem(galleryItem, $event){
//Save galleryItem Name
var itemName = $($event.currentTarget).name(); // if it has it on html
var itemName = galleryItem.name; // if it has a property
//Get the target and remove it
$($event.currentTarget).remove();
//Using lodash, loop through your array and remove that exact object from it.
//Ofc you can do a normal loop through it.
$scope.galleries = _.remove($scope.galleries, function(n) {
return n != itemName;
});
//Then, if the change does not happen in your DOM
$scope.$apply();
}
Hope I've been helpful.
I have made some changes to fix this issue and you can check it at this link.
The problem here was that there was a typo in the html snippet that was calling the removeGalleryItem(galleryItem, $event). The parameter name should have been gallery, not galleryItem, since there is no such object by the name galleryItem, hence inside this method, the parameter value was undefined. Once I fixed it, I was able to get the gallery object within the removeGalleryItem method and the following code worked absolutely fine:
$scope.removeGalleryItem=function(gallery){
var selectedGallery = $scope.galleries.indexOf(gallery);
$scope.galleries.splice(selectedGallery, 1);
};
Also note that, I have removed the $event attribute from the method declaration and from the html method call as we didn't need it in the above mentioned approach.
<i class="fa fa-times" ng-click="removeGalleryItem(gallery)"></i>

angularjs how to trigger changes on object in scope

I have the $scope object (array of objects) like this
$scope.parts = [];
(content of $scope.parts is changing during 'run-time', not just filled once per page load)
Later, it some custom directive i show those parts in such manner:
<li ng-repeat="part in parts">
<span>{{part.name}}
<i class="fa fa-check"
tooltip="some tooltip"
...
</i>
</span>
</li>
According to some logic, i want to change 'fa-' class and tooltip text.
I can do it like this
<i class="fa"
ng-class="haveDescr(part.name)"
//and in directive's controller
$scope.haveDescr = function (partName) {
return someCondition ? 'fa-check' : 'fa-question-circle';
};
and so on for the tooltip, and... for every attribute i want to change?
Is there a better way, than to write a scope "check-function" for every attribute? How can i trigger changes in every single part/property of $scope.parts and do the DOM changes described above? What is the right "angular way" for this? Or, maybe it is possible to 'intercept' ng-repeat action and do everything there?
You can use ng-class with an 'object' expression.
<i class="fa" ng-class="{'fa-check' : part.name, 'fa-question-circle' : !part.name}">
You can use ng-class and title
<i ng-class="{'fa-check':showFaCheck(part.name), 'fa-question': !showFaCheck(part.name) }" title="{{getTooltip(part.name)}}"/>
Fiddle http://jsfiddle.net/4PYZa/303/

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

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.

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

Adding new form fields dynamically in ngView - Angularjs

I use routing to load different templates into a ngView. One of the templates has a simple controller which contains an array of contacts. What I'm trying to do here is as simple as by clicking a link (ngclick) call a function and add a new object into the array which I expect will be reflected in my UI.
It's something like this:
$scope.contacts = [{name='', email=''}];
<li ng-repeat="con in contacts">
<input type="text" ng-model="con.name"/>
<input type="email" ng-model="con.email"/>
</li>
<li>
<a href ng-click="addContact()">add</a>
</li>
$scope.addContact = function() {
$scope.contacts.push( {name='', email=''} ); //-- i can use either $scope or this to reference the array and works.
}
So, the UI renders well with the initial value, the addContact function is invoked on click and I see the value is pushed (length = 2) but then the function ends the array seems to be reset to one element (lenght = 1) after angularjs evaluation.
I'm not sure if this is occurring because I use ngView. I mean, I reviewed this example (http://code.angularjs.org/1.0.3/docs/api/ng.directive:ngController) and I don't see much differences of what I'm trying to do here, the diff is that I use routing with ngView.
fiddle: http://jsfiddle.net/fdDph/
Help is much appreciated.
In your Fiddle, you are resetting the array length to 1 in the ng-show:
<span ng-hide="contacts.length = 1">
Do this and it will work:
<span ng-hide="contacts.length == 1">
{name='', email=''} is wrong syntax btw, it should be {name:'', email:''}

Resources