I am trying to show/hide buttons on a ng-repeat (a simple table). A delete button replaced by a conform button.
Here is my code
..... Angular stuff .....
function ContactsCtrl($scope, $http) {
$scope.order = '-id';
$scope.currentPage = 0;
$scope.pageSize = 15;
$http.get('/events/<%= #event.id -%>/contacts.json').success(function(data) {
$scope.contacts = data;
$scope.numberOfPages=function(){
return Math.ceil($scope.contacts.length/$scope.pageSize);
}
});
$scope.clickDelete = function(e,t) {
console.log("delete");
// rest api stuff...
$scope.contacts.splice(e, 1); // This WORKS!
};
$scope.showDelete = function(e,t) {
e.showDeleteButton = true; // This DOES NOT
};
}
And in HTML:
<tr ng-repeat="contact in contacts | filter:search | orderBy:order | startFrom:currentPage*pageSize | limitTo:pageSize">
<td>{{contact.email}}</td>
...
<td>delete
confirm
</td>
</tr>
You don't appear to be returning a value from the showDelete function. It also looks like there is a property on the JSON object 'showDeleteButton' which you could bind to directly.
Example plnkr: http://plnkr.co/edit/eZTFyw9tGeWEfYw0U0I8
It seems like what you are trying to do is have the delete button just set a flag that will show the confirm button which will actually perform the delete, correct? ng-repeat creates a new child scope for each element, so you could just set a 'confirmable' flag on the child scope and use that (fiddle):
<a ng-click="confirmable = true">delete</a>
<a ng-show="confirmable" ng-click="clickDelete(contact)">confirm</a>
<a ng-show="confirmable" ng-click="confirmable = false">cancel</a>
Also it looks like you're passing the contact object to your clickDelete function and using it as an index into the array so I don't know why that works. The fiddle uses indexOf to find the index to delete.
This is how I did it:
JavaScript:
$scope.clickDelete = function(contact,i) {/* ... */ $scope.contacts.splice(i, 1);};
$scope.clickShowConfirm = function(contact) {contact.showdelete = true;};
$scope.clickCancel = function(contact) {contact.showdelete = false;}
$scope.showOrHide = function(contact) {return contact.showdelete;};
HTML:
delete
ok
cancel
Related
Within a ng-repeat="item in items" I have a Wishlist boolean button which can be true or false.
<div ng-repeat="item in items" ng-controller="ItemCtrl as ctrl">
<md-button class="md-icon-button" ng-click="ctrl.toggleWish(item)">
<i class="material-icons">{{ctrl.hasWished(item) ? 'favorite' : 'favorite_border' }}</i>
</md-button>
</div>
with the hasWished(item) function, I check if the currently logged in User id (Auth._currentUser.id) is within the user ids stored within the wishlist of the item.
The JSON output of a single item looks simplified like this:
{"id":1,"title":"This is a tile","wishes":2,"wishlists":[{"user_id":2},{"user_id":3}]}
So the hasWished(item) function should return true or false:
this.hasWished = function(item) {
return item.wishlists.some(function(wishlist) {
return wishlist.user_id === Auth._currentUser.id // returns true if currently logged in user id is on wishlist
});
};
That is working so far. With the toggleWish function I want to toggle the hasWished(item) from true to false, or vice versa:
this.toggleWish = function(item) {
if (!this.hasWished(item)) {
this.hasWished = function(item){return true};
items.wish(item); // sends PUT request to API
item.wishes += 1;
ToastService.show(item.title + ' added to Wish List');
} else {
this.hasWished = function(item){return false};
items.unWish(item); // sends DELETE request to API
item.wishes -= 1;
ToastService.show(item.product + ' removed from Wish List');
}
};
Currently toggleWish does toggle, but toggles all items within ng-repeat="item in items" to true or false (depending if they were true or false before). instead of only the single item, where the toggleWish(item) button was clicked.
I need to somehow set hasWished for each single item to true or false - independent of the other items, while still checking if the user has wished this item previously (with the current hasWished function).
I am stuck here and I appreciate your advise!
this.hasWished = function(item){return true};
is a function in the controller, so it is not something specific for an item.
You should store a flag in the item. Something like
this.toggleWish = function(item) {
if (!this.hasWished(item)) {
items.wish(item); // sends PUT request to API
item.hasWished = true;
item.wishes += 1;
ToastService.show(item.title + ' added to Wish List');
} else {
items.unWish(item); // sends DELETE request to API
itme.hasWished false;
item.wishes -= 1;
ToastService.show(item.product + ' removed from Wish List');
}
};
and then in the html use that flag to show or not
{{item.hasWished ? 'favorite' : 'favorite_border' }}
After you fetch your JSON, compute flags that your template can use.
Then toggle those flags.
HTML
<div ng-repeat="item in items" ng-controller="ItemCtrl as ctrl">
<md-button class="md-icon-button" ng-click="ctrl.toggleWishFlag($index)">
<i class="material-icons">
{{ctrl.flags.wish[$index] ? 'favorite' : 'favorite_border' }}
</i>
</md-button>
</div>
Compute your flags:
var self = this;
this.computeFlags = function(items) {
self.flags = self.flags || {};
self.flags.wish = [];
for (var i=0; i<items.length; i++) {
self.flags.wish.push(
self.hasWished(items[i]);
);
};
};
Toggle your flags:
this.toggleWishFlag = function(index) {
var item = items(index);
if (!self.flags.wish[index]) {
self.flags.wish[index] = true;
// send PUT request to API
ToastService.show(item.title + ' added to Wish List');
} else {
self.flags.wish[index] = false;
// send DELETE request to API
ToastService.show(item.product + ' removed from Wish List');
}
};
Notice my use of the $index special property in the HTML. For more information on that see the AngularJS ngRepeat API Docs.
I have a condition that needs to be checked in my view: If any user in the user list has the same name as another user, I want to display their age.
Something like
<div ng-repeat="user in userList track by $index">
<span class="fa fa-check" ng-if="user.isSelected"></span>{{user.firstName}} <small ng-if="true">{{'AGE' | translate}} {{user.age}}</small>
</div>
except I'm missing the correct conditional
You should probably run some code in your controller that adds a flag to the user object to indicate whether or not he/she has a name that is shared by another user.
You want to minimize the amount of logic there is inside of an ng-repeat because that logic will run for every item in the ng-repeat each $digest.
I would do something like this:
controller
var currUser, tempUser;
for (var i = 0; i < $scope.userList.length; i++) {
currUser = $scope.userList[i];
for (var j = 0; j < $scope.userList.length; j++) {
if (i === j) continue;
var tempUser = $scope.userList[j];
if (currUser.firstName === tempUser.firstName) {
currUser.showAge = true;
}
}
}
html
ng-if='user.showAge'
Edit: actually, you probably won't want to do this in the controller. If you do, it'll run every time your controller loads. You only need this to happen once. To know where this should happen, I'd have to see more code, but I'd think that it should happen when a user is added.
You can simulate a hashmap key/value, and check if your map already get the property name. Moreover, you can add a show property for each objects in your $scope.userList
Controller
(function(){
function Controller($scope) {
var map = {};
$scope.userList = [{
name:'toto',
age: 20,
show: false
}, {
name:'titi',
age: 22,
show: false
}, {
name: 'toto',
age: 22,
show: false
}];
$scope.userList.forEach(function(elm, index){
//if the key elm.name exist in my map
if (map.hasOwnProperty(elm.name)){
//Push the curent index of the userList array at the key elm.name of my map
map[elm.name].push(index);
//For all index at the key elm.name
map[elm.name].forEach(function(value){
//Access to object into userList array with the index
//And set property show to true
$scope.userList[value].show = true;
});
} else {
//create a key elm.name with an array of index as value
map[elm.name] = [index];
}
});
}
angular
.module('app', [])
.controller('ctrl', Controller);
})();
HTML
<body ng-app="app" ng-controller="ctrl">
<div ng-repeat="user in userList track by $index">
<span class="fa fa-check"></span>{{user.name}} <small ng-if="user.show">{{'AGE'}} {{user.age}}</small>
</div>
</body>
Please see this JS fiddle link.
http://jsfiddle.net/4Dpzj/174/
This is the logic for group by
app.filter('groupBy', ['$parse', function ($parse) {
return function (list, group_by) {
var filtered = [];
var prev_item = null;
var group_changed = false;
// this is a new field which is added to each item where we append "_CHANGED"
// to indicate a field change in the list
//was var new_field = group_by + '_CHANGED'; - JB 12/17/2013
var new_field = 'group_by_CHANGED';
// loop through each item in the list
angular.forEach(list, function (item) {
group_changed = false;
// if not the first item
if (prev_item !== null) {
// check if any of the group by field changed
//force group_by into Array
group_by = angular.isArray(group_by) ? group_by : [group_by];
//check each group by parameter
for (var i = 0, len = group_by.length; i < len; i++) {
if ($parse(group_by[i])(prev_item) !== $parse(group_by[i])(item)) {
group_changed = true;
}
}
}// otherwise we have the first item in the list which is new
else {
group_changed = true;
}
// if the group changed, then add a new field to the item
// to indicate this
if (group_changed) {
item[new_field] = true;
} else {
item[new_field] = false;
}
filtered.push(item);
prev_item = item;
});
return filtered;
};
I want to group all the products together.
what changes i need to do ?
I come up with this in my mind. Without using any custom filters.
I simply use this ng-repeat syntax :
ng-repeat="(key,item) in MyList | orderBy:orderKey"
Thanks to it i can get the key to compare the value with the previous object.
Here is my ng-show attribute. It can be improved by sorting the list somewhere else (like in the controller)
<h2 ng-show="(MyList | orderBy:orderKey)[key-1][orderKey] !== (MyList | orderBy:orderKey)[key][orderKey]"
Thanks to this you can populate your var "orderKey" with any of your attribute name and this will works.
See it working in this JSFiddle
Hope it helped.
EDIT :
I think it would be a bit cleaner to use a temporary list to manage the visual order (see it in this JSFiddle):
JS :
$scope.orderList = function(){
$scope.orderedList = $filter('orderBy')($scope.MyList,$scope.orderKey);
}
HTML :
ng-change="orderList()" To trigger the list sort
The cleaner ng-repeat / ng-show
<div ng-repeat="(key,item) in orderedList">
<h2 ng-show="orderedList[key-1][orderKey] !== orderedList[key][orderKey]">{{item[orderKey]}} </h2>
<ul>
<li>{{item.ProductName}}</li>
</ul>
</div>
Have a look at this:
http://jsfiddle.net/4Dpzj/176/
<div ng-repeat="item in MyList | orderBy:['SubCategoryName','BrandName'] | groupBy:['SubCategoryName']" >
<h2 ng-show="item.group_by_CHANGED">{{item.SubCategoryName}} </h2>
<ul>
<li>{{item.ProductName}} --- {{item.BrandName}}</li>
</ul>
</div>
I made task table through the ng-repeat, Each task in table can can be modify. Task table will have to updated with updated task. So for this we need to access particular ng-repeat element. I want to know how to access particular ng-repeat element and update this with new task ng-click=editTask().
Please see $scope.editTask, Here I want to update inside $http.put(uri, data).
Workflow:
ng-click=beginTask(task) opens dialog, In dialog there is ng-click=editTask(), which will modify the task through $http.put...
Please see DEMO
<tr ng-repeat="task in tasks">
<td>{{task.title}}</td>
<td>{{task.description}}</td>
<td>
<a class="btn" data-toggle="modal" ng-click="beginEdit(task)">Edit</a>
</td>
</tr>
Angularjs code
$scope.beginEdit=function(task){
$scope.title = task.title;
$scope.description=task.description;
$scope.done=task.done;
$scope.uri=task.uri;
$scope.index=$scope.tasks.indexOf(task);
$('#edit').modal('show');
};
$scope.editTask = function() {
title=$scope.title;
description=$scope.description;
done=$scope.done;
uri=$scope.uri;
$('#edit').modal('hide');
var i=$scope.index;
var data={title: title, description: description, done: done };
$http.put(uri, data)
.success(function(){
alert("Success");
});
};
Please check this - : http://plnkr.co/edit/lVkWEsAGVLTY7mGfHP5N?p=preview
Add
$scope.tasks[$scope.index] = data;
In editTask
$scope.editTask = function(obj) {
alert($scope.title);
title = $scope.title;
description = $scope.description;
done = $scope.done;
uri = $scope.uri;
$('#edit').modal('hide');
var i = $scope.index;
var data = {
title: title,
description: description,
done: done
};
alert("uri" + uri);
alert(data.title);
$scope.tasks[$scope.index] = data; // For updating value
$http.put(uri, data)
.success(function() {
//tasks[i].uri(data.uri);
alert("Success");
});
};
I am attempting to create infinite scrolling on my web page using an example I found. However, the page fills up completely with all the items instead of just showing several items at a time. In other words it is not doing infinite scrolling. I noticed in some of the examples they parsed out data in chunks but in the real world how are you supposed to do that?
Below is my html code:
<table class="table table-striped table-bordered"><tr>
<th style="text-align:center;">User ID</th> <th>Username</th><th>Rank</th>
<th>Posts</th><th>Likes</th> <th>Comments</th> <th>Flags</th><th>Status</th><th>Action</th></tr>
<tr><td class="center">
<div ng-app='scroll' ng-controller='Scroller'>
<div when-scrolled="loadMore("")">
<div ng-repeat='item in items'>
<span>{{item.id}}
<span style="position:absolute;left:140px;">{{item.username}}</span>
<span style="position:absolute;left:290px;">{{item.rank}}</span>
<span style="position:absolute;left:360px;">{{item.posts}}</span>
<span style="position:absolute;left:440px;">{{item.likes}}</span>
<span style="position:absolute;left:530px;">{{item.comments}}</span>
<span style="position:absolute;left:640px;">{{item.flags}}</span>
<span class="label label-success" style="position:absolute;left:710px;">Active</span>
<a style="position:absolute;left:790px;" class="btn btn-info" style="width:30px" ng-href='/admin/userDetail?userid={{item.id}}'>
View Detail</a>
<hr>
</div>
</div>
</div>
</td></tr>
</table>
Below is my angularjs code:
function Scroller($scope, $http, $q, $timeout) {
$scope.items = [];
var lastuser = '999999';
$scope.loadMore = function(type) {
todate = document.getElementById("charttype").value;
var url = "/admin/getusers?type=" + todate + "&lastuser=" + lastuser;
var d = $q.defer();
$http({
'url': url,
'method': 'GET'
})
.success(function (data) {
var items = data.response;
for (var i = $scope.items.length; i < items.length; i++) {
$scope.items.push(items[i]);
count++;
if (count > 100)
{
lastuser = $scope.items[i].id;
break;
}
d.resolve($scope.items);
d.promise.then(function(data) {
});
}
)
.error(function (data) {
console.log(data);
});
return d.promise;
};
$scope.loadMore();
}
angular.module('scroll', []).directive('whenScrolled', function() {
return function(scope, elm, attr) {
var raw = elm[0];
alert("scroll");
elm.bind('scroll', function() {
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) {
scope.$apply(attr.whenScrolled);
}
});
};
});
My question is why does my web page show all 3200 lines initially rather than allowing me to do infinite scrolling. You will notice I put an alert in the scroll module and it is never displayed. Do I have to incrementally read my database? Any help is appreciated.
You are adding all of the items returned from your API call into $scope.items.
for (var i = 0; i < items.length; i++) {
$scope.items.push(items[i]);
}
Don't you want to add only a subset of those items?
P.S. Might help if you create a Plunkr to show the specific problem.
EDIT:
Based on your comment about the directive not working, I put together this Plunkr, which is a copy of your code but with the $http get code ripped out. The "scroll" alert fires here. I think you're just missing a closing bracket on your for loop (since I don't have your API endpoint to test against, I can't actually run your code live).
EDIT 2:
I'm not sure why you aren't seeing the function fire correctly on scroll. I've set up another plunker where I've changed the result of the scroll event firing to show an alert and load more items from a data variable, so you can see that the scroll event is firing correctly and it will load more items.