Removing items from an array with Angular - angularjs

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>

Related

Removing element from ng-repeat does not allow to remove or hide element from DOM

I have below code to display records using ng-repeat:
<div class="gallery">
<div ng-cloak
ng-repeat="photo in photos| orderBy:'-id'"
ng-mouseLeave = "delete_btn = !delete_btn"
ng-mouseEnter = "delete_btn = !delete_btn"
class="gallery_block"
id="photo_block_[[photo.id]]">
<span title="delete photo" ng-show="delete_btn" class="delete_btn_span" rel="[[photo.id]]" id="delete_photo_[[photo.id]]">
<img src="{{asset('frontend/images/cross_icon.png')}}">
</span>
<p>
<a href="javascript:;">
<img ng-click="showImagePopup([[photo.path_popup_thumbnail]]);" src={{$public_path}}./image.php?width=149&height=109&cropratio=2:1.4&image=[[photo.path_popup_thumbnail]] alt="">
</a>
{{--<span> </span>--}}
</p>
</div>
</div>
I have to delete records for that I first have to call a function using $http and then I remove element from photos array:
var index = $scope.photos.indexOf($('#photo_block_'+id));
$scope.photos.splice(index, 1);
Then I remove element from DOM:
$('#photo_block_'+id).remove();
Update
I have called deletePhoto function using jqyery like below:
$(".gallery").on('click','span.delete_btn_span',function()
{
$scope.deletePhoto($(elem), $(elem).attr('rel'));
});
deletePhoto function further do all the work of deleting elements etc.
But it does not remove element. Where as all the selectors are fine.
Is removing element from photos array causing problem?
With angular JS, you have to deal with data. So you should put an ng-click="delete(photo)" on your span.
and create a method in your controller :
$scope.delete = function(photo) {
// delete froms scope.photos here...
}
to delete the photo from the list.
Your code does not work because $scope.photos.indexOf($('#photo_block_'+id)) surely return -1, because you're looking for $('#photo_block_'+id), which is a jquery object in a list of simple json object (i suppose).
So the splice method does anything, and the $scope.photos list remain unchanged, so the view is not changed, because it's build from the list by the ng-repeat directive.
ps: you should never manipulate dom element from controller, if you need to do that, you should create directive.

AngularJS - One controller on two sibling DOM elements

I'm trying to do a very simple thing. I'm displaying a list of values with Edit links beside them. Clicking the edit link reveals a form that lets you update the value.
(I've simplified the question so the items just have one field "name". My actual use case has more fields, but is canonically equivalent.)
I've run into something that looks like a bug in Angular, but given my limited experience with it, I'm not so sure. Here's how I'm trying to do it:
<div ng-repeat-start="item in items" ng-controller="ItemCtrl as ctrl" ng-hide="ctrl.isEditing">
<span>Name: {{item.name}}.</span>
<a href='#' ng-click='ctrl.startEditing()'>Edit</a>
</div>
<div ng-repeat-end ng-show="ctrl.isEditing">
<input type='text' ng-model='item.name'/>
<a href='#' ng-click='ctrl.save()'>Save</a>
</div>
My controller looks like this:
app.controller('ItemController', function() {
this.isEditing = false;
this.startEditing = function() { this.isEditing = true; }
this.save = function() { this.isEditing = false; }
});
Clicking on Edit link calls the right controller function, and the first div hides. But the second div does not hide.
When I rearrange the code to look like this (essentially wrapping the two divs with a wrapper element), all is well.
<div ng-repeat="item in items" ng-controller="ItemCtrl as ctrl">
<div ng-hide="ctrl.isEditing">
<span>Name: {{item.name}}.</span>
<a href='#' ng-click='ctrl.startEditing()'>Edit</a>
</div>
<div ng-show="ctrl.isEditing">
<input type='text' ng-model='item.name'/>
<a href='#' ng-click='ctrl.save()'>Save</a>
</div>
</div>
Any idea what is technically wrong with the first version? Note that the <input> boxes do get populated with the right values from item.name.
PS: There's a reason why I'm trying to keep the two divs siblings: in my use case, they are actually implemented as two trs which are supposed to appear right below each other in a table.
It's not a bug from angular but it is quite logical.
<div ng-repeat-start="item in items" ng-controller="ItemCtrl as ctrl" ng-hide="ctrl.isEditing">
<span>Name: {{item.name}}.</span>
<a href='#' ng-click='ctrl.startEditing()'>Edit</a>
</div>
<div ng-repeat-end ng-show="ctrl.isEditing">
<input type='text' ng-model='item.name'/>
<a href='#' ng-click='ctrl.save()'>Save</a>
</div>
If you see the above code you have injected controller only to the first div so obviously sibling div doesn't know what is ctrl or ItemCtrl until and unless you do as in you second way.
So if you want to achieve it as sibling, if you are using routing then add the controller attribute in your route path.
So that the controller will be active for that entire template and you can achieve what you want.
Hope it helps.
Thats because controller has its own scope. When you placed controller ONLY on your first div controllers' scope is limited to only this one div. When you wrapped both your divs inside another and place controller on wrapper, controllers' scope now is all that inside this wrapper. So in this case this works fine and this is not the angular bug

Is it possible to delete all scope variable of a controller? AngularJS

I am new to AngularJS and i dont know is it possible to delete all scope variables of a controller.I am using ng-controller with ng-repeat, like this.
<div ng-controller="main">
<div ng-repeat="x in list" ng-controller="test">
<input type="text" ng-model="text">
<span ng-click="remove($index)"> x </span>
<div>
</div>
JS
myapp.controller('main',function($scope){
$scope.list=[1,2,3,4]
})
myapp.controller('test',function($scope){
$scope.text="untitiled"
})
I want to remove the clicked scope.Can anyone help me or please suggest me a better way. Thanks
The question isn't very clear, but it looks like you may want to remove the item after clicking. Since you are passing into the remove function the index, you can splice it out. The DOM will autoupdate and remove that from the list:
$scope.remove = function(i) {
$scope.list.splice(i,1);
console.log($scope.list);
}
In the event you are doing something different in that you only want to hide it, you would push the index onto another array and then use something like ng-show or ng-hide.
$scope.remove2 = function(i) {
$scope.hideList.push(i);
}
$scope.shouldHide = function(i) {
return $scope.hideList.indexOf(i)!=-1;
}
<div ng-repeat="number in list2" >
{{number}}
<span ng-hide='shouldHide($index)' ng-click="remove2($index)"> x </span>
</div>
Here is a simple example of both scenarios. In real life, usually we are dealing with arrays of objects and what you might be doing is setting a property on one of the objects to hidden and controlling it that way.
Demo: http://plnkr.co/edit/G7UINKUCBJ4yZhQNtuJ2?p=info
If you actually want to remove all the keys from the scope:
function removeKeys() {
for(key in $scope) {
if (key.substr(0,1)!='$' && key!='this')
delete $scope[key];
}
}

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.

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