Compare two set of objects - angularjs

I have scope called $scope.users which returns all the users in my app like so,
[{"id":1,"name":"Peter Boomsma"},
{"id":2,"name":"Jan Jansen"},
{"id":3,"name":"Kees Keesen"},
{"id":4,"name":"Piet Pietersen"}]
Then I have a scope called $scope.current_user which returns the current users, and the id's of the users he's following,
{"id":4,"name":"Piet Pietersen","following":[
{"id":1},{"id":2},{"id":4}]
}
I have a ng-repeat that shows all the users, and a followUser action per user,
%ul{"ng-repeat" => "user in users"}
%li
name: {{ user.name }}
%a{"ng-click" => "followUser(user)"} Follow user.
The problem is that I can't differentiate between a user that's already being followed, and a user that's not being followed. So the action followUser is always there. I would like to give the users the current user is following a different action, such as unfollowUser.
I got the id's of the users that the current user is following, but I don't know how to give those users a other button. Something like unfollowUser.
* update *
I've got it working (somewhat at least), by using Mihail his suggestion,
When I go to my user template I load the userCtrl,
usersService.loadUsers().then(function(response) {
$scope.users = response.data;
angular.forEach(response, function(user){
$scope.user = user
$scope.isFollowed = function(userId) {
var following = $scope.current_user.following;
for (var i=0; i<following.length; i++) {
if (following[i].id == userId) {
return true;
}
}
return false;
}
})
})
And in my template I have,
%ul{"ng-repeat" => "user in users"}
%li
name: {{ user.name }}
%a{"ng-click" => "followUser(user)", "ng-show" => "!isFollowed(user.id)"} follow
%a{"ng-click" => "unfollowUser(user)", "ng-show" => "isFollowed(user.id)"} unfollow
This works fine. When a user is followed then the unfollowUser function is used. The problem is that when I unfollow the user the view doesn't get updated. I have to refresh the page to see the effect of the unfollow action.
I've tried putting a init at the end of the unfollowUser action like so,
$scope.unfollowUser = function(user){
unfollowFriend.unfollowFriend(user).then(function(){
},function(){
}).then(init);
Notification.success(user.name + ' is verwijderd als vriend.');
}
The init,
var init = function(){
console.log ('renew')
usersService.loadUsers().then(function(response) {
$scope.users = response.data;
angular.forEach(response, function(user){
$scope.user = user
$scope.isFollowed = function(userId) {
var following = $scope.current_user.following;
for (var i=0; i<following.length; i++) {
if (following[i].id == userId) {
return true;
}
}
return false;
}
})
})
}
I get the renew message in my browser log, but the view isn't updated.

I am not good at haml, so I will write a simple html example:
<div ng-repeat="user in users">
<div ng-show="isFollowed(user.id)" ng-click="unfollowUser(user)">unfollow</div>
<div ng-show="!isFollowed(user.id)" ng-click="followUser(user)">follow</div>
</div>
$scope.isFollowed = function(userId) {
var following = $scope.current_user.following;
for (var i=0; i<following.length; i++) {
if (following[i].id == userId) {
return true;
}
}
return false;
}

You can make a function that check if a given user is already followed like:
$scope.isFollowed = function (userID) {
//check if the userID is in$scope.current_user.following
//return a boolean
}
then in the HTML code you can check based on this function if to show followUser or unfollowUser.

Related

How can I bind a select options list to an ng-repeat with a filter?

I have a drop down select tag that looks like this:
<select class="pull-right" id="editActionChargeSelect"
ng-model="data.editAction.actionType1.actionChargeId"
ng-change="addAnEditSelectionToModelCollection(data.editAction.actionType1.actionCharges, 'editActionChargeSelect')">
<option value="{{actionCharge.id}}")
ng-repeat="actionCharge in data.actionCharges | selectFilter:data.editAction.actionType1.actionCharges">
{{actionCharge.displayValue}}
</option>
</select>
The ng-repeat passes an array, data.actionCharges stored to my selectFilter filter which looks like this:
angular.module("customFilters", [])
.filter("selectFilter", function () {
return function (data, modelCollection)
{
var results = [];
for (var i = 0; i < data.length; i++) {
var inModelCollection = false;
for (var j = 0; j < modelCollection.length; j++) {
if (data[i].id == modelCollection[j].lookUpDetailId)
{
inModelCollection = true;
}
}
if (!inModelCollection) {
results.push(data[i])
}
//results.push(data[i]);
}
return results;
}
Then I have links in the UI where the user can add in and remove items into and from the data.actionCharges list. The select refelects this and the options appear and disappear to reflect the user's choices.
The only problem is that the filter is not initializing on load up. So if the list starts out empty everthing is great. The user will see their choices removed from the drop down and added into a UI table. And when they delete a row from the table the option is added back into the drop down, all through the filter.
The data.editAction model is loaded up when I click an edit button:
<a href="#" ng-click="getAction(action.id, 'edit')"
data-toggle="modal" data-target="#modalEditAction"
class="functionalLinks">
<i class="glyphicon glyphicon-pencil"></i>
EDIT
</a>
The edit button calls getAction to laod the model like this:
$scope.getAction = function (actionId, populateObject) {
$http.get(actionUrl + '/' + actionId)
.then(function (response) {
// Test front end exception message;
// throw "test exception";
switch (populateObject) {
case "details":
$scope.data.actionDetails = response.data;
break;
case "edit":
// populate editAction Object
$scope.data.editAction = response.data;
// sanitize dates for action type.
if (response.data.actionTypeId == 1) {
var actionEffectiveDate = undefined; // if you get null back from database, you'll keep this undefined
if (response.data.actionType1.actionEffectiveDate !== null) {
// only make an actual date if there is something stored
var actionEffectiveDate = new Date(response.data.actionType1.actionEffectiveDate);
}
$scope.sanitizedActionEffectiveDate = $filter('date')(actionEffectiveDate, "yyyy-MM-dd")
//$scope.disableSelectionsForEdit($scope.data.editAction.actionType1.actionsRecommendedByLer, "editActionRecommendedByLerSelect");
//$scope.disableSelectionsForEdit($scope.data.editAction.actionType1.actionCharges, "editActionChargeSelect");
}
break;
}
})
.catch(function (error) {
$scope.data.actionDetailsError = error;
});
}
But how do I get the select to reflect the model through the filter when I click the button to pull up the record.

Switch between 2 ng-shows

I have two elements with a ng-show in them,
%a.follow{"ng-click" => "followUser(user)", "ng-show" => "!isFollowed(user.id)"} follow
%a.unfollow{"ng-click" => "unfollowUser(user)", "ng-show" => "isFollowed(user.id)"} unfollow
It depends on the user.id which ng-show is being rendered in the template. So only one of the two ng-shows is displayed.
So for example a user wants to start following another user. Then the follow link is displayed.
%a.follow{"ng-click" => "followUser(user)", "ng-show" => "!isFollowed(user.id)"} follow
When a user clicks on it, I would like to hide the clicked ng-show, and show the unfollow ng-show so that the user can unfollow the just followed user.
The follow and unfollow user function,
$scope.followUser = function (user) {
followUser.create({
followed_id: user.id
}).then(init);
Notification.success(user.name + ' is toegevoegd als vriend.');
}
$scope.unfollowUser = function(user){
unfollowUser.unfollowUser(user).then(function(){
},function(){
}).then(init);
Notification.success(user.name + ' is verwijderd als vriend.');
}
And the isFollowed function,
usersService.loadUsers().then(function(response) {
$scope.users = response.data;
console.log ($scope.users)
angular.forEach(response, function(user){
$scope.user = user
$scope.isFollowed = function(userId) {
var following = $scope.current_user.following;
for (var i=0; i<following.length; i++) {
if (following[i].id == userId) {
return true;
}
}
return false;
}
})
})
I've tried building this,
<a ng-click="follow=false ;unfollow=true", ng-show="follow">Follow!</a>
<a ng-click="follow=true; unfollow=false", ng-show="unfollow">Unfollow!</a>
This does switch between the two ng-shows, but when I try to get the isFollowed(user.id), !isFollowed(user.id) in them the code crashes.
You should create single function to follow/unfollow, Here in the code snippet I have introduced a new property i.e. isFollowed to object user whose value is set using the isFollowed function.
Additionally, Don't overuse isFollowed(user.id) method, it will be huge performance hit.
HTML
<a ng-click="followUnfollowUser(user)"> {{ user.isFollowed : "Unfollow!" : "Follow!"}} </a>
Script
$scope.followUnfollowUser = function(user) {
//If followed - unfollow
if (user.isFollowed) {
unfollowUser.unfollowUser(user).then(function() {
user.isFollowed=!user.isFollowed
}, function() {
}).then(init);
Notification.success(user.name + ' is verwijderd als vriend.');
} else {
followUser.create({
followed_id: user.id
}).then(function() {
user.isFollowed=!user.isFollowed
}, function() {
}).then(init);
Notification.success(user.name + ' is toegevoegd als vriend.');
}
}
//Define method to check wheather current user is beign followed
var isFollowed = function(userId) {
var following = $scope.current_user.following;
for (var i = 0; i < following.length; i++) {
if (following[i].id == userId) {
return true;
}
}
return false;
}
//Fetch Users
usersService.loadUsers().then(function(response) {
$scope.users = response.data;
//Iterate and create isFollowed property
angular.forEach($scope.users, function(user) {
user.isFollowed = isFollowed(user.id);
})
})
Note: I'm not familiar with following syntax thus used standard HTML.
%a.follow{"ng-click" => "followUser(user)", "ng-show" => "!isFollowed(user.id)"} follow
Alrhgout Satpal did point me to the right direction and helped me with some code. His answer isn't complete. So I've decided that add the code I'm using for this function (made with the help of Satpal!).
I've created a followUnfollowUser function. But instead of having two .then(init) I have one init() at the end of the function. Having the two inits gave me some looping trouble.
$scope.followUnfollowUser = function(user) {
//If followed - unfollow
if (user.isFollowed) {
unfollowUser.unfollowUser(user).then(function() {
user.isFollowed=!user.isFollowed
}, function() {
})
Notification.success(user.name + ' is verwijderd als vriend.');
} else {
followUser.create({
followed_id: user.id
}).then(function() {
user.isFollowed=!user.isFollowed
}, function() {
})
Notification.success(user.name + ' is toegevoegd als vriend.');
}
init();
}
Then the init function,
var init = function () {
loadCurrent_user.loadCurrent_user().then(function(response) {
$scope.current_user = response.data;
});
usersService.loadUsers().then(function(response) {
$scope.users = response.data;
//Iterate and create isFollowed property
angular.forEach($scope.users, function(user) {
user.isFollowed = isFollowed(user.id);
})
})
var isFollowed = function(userId) {
var following = $scope.current_user.following;
for (var i = 0; i < following.length; i++) {
if (following[i].id == userId) {
return true;
}
}
return false;
}
}
First I load the current user so that the $scope.current_user gets updated when a user is being followed/unfollowed. And then I iterate through each user and create the isFollowed value using the isFollowed function.
And in my template I have,
%a{"ng-click" => "followUnfollowUser(user)"}
-# {{ user.isFollowed }}
{{ user.isFollowed ? "Unfollow user" : "Follow user"}}

angular push result to controller

(was not sure what to have as a title, so if you have a better suggestion, feel free to come up with one - I will correct)
I am working on an angular application where I have some menues and a search result list. I also have a document view area.
You can sort of say that the application behaves like an e-mail application.
I have a few controllers:
DateCtrl: creates a list of dates so the users can choose which dates they want to see posts from.
SourceCtrl: Creates a list of sources so the user can choose from which sources he/she wants to see posts from.
ListCtrl: The controller populating the list. The data comes from an elastic search index. The list is updated every 10-30 seconds (trying to find the best interval) by using the $interval service.
What I have tried
Sources: I have tried to make this a filter, but a user clicks two checkboxes the list is not sorted by date, but on which checkbox the user clicked first.
If it is possible to make this work as a filter, I'd rather continue doing that.
The current code is like this, it does not do what I want:
.filter("bureauFilter", function(filterService) {
return function(input) {
var selectedFilter = filterService.getFilters();
if (selectedFilter.length === 0) {
return input;
}
var out = [];
if (selectedFilter) {
for (var f = 0; f < selectedFilter.length; f++) {
for (var i = 0; i < input.length; i++) {
var myDate = input[i]._source.versioncreated;
var changedDate = dateFromString(myDate);
input[i]._source.sort = new Date(changedDate).getTime();
if (input[i]._source.copyrightholder === selectedFilter[f]) {
out.push(input[i]);
}
}
}
// return out;
// we need to sort the out array
var returnArray = out.sort(function(a,b) {
return new Date(b.versioncreated).getTime() - new Date(a.versioncreated).getTime();
});
return returnArray;
} else {
return input;
}
}
})
Date: I have found it in production that this cannot be used as a filter. The list of posts shows the latest 1000 posts, which is only a third of all posts arriving each day. So this has to be changed to a date-search.
I am trying something like this:
.service('elasticService', ['es', 'searchService', function (es, searchService) {
var esSearch = function (searchService) {
if (searchService.field === "versioncreated") {
// doing some code
} else {
// doing some other type of search
}
and a search service:
.service('searchService', function () {
var selectedField = "";
var selectedValue = "";
var setFieldAndValue = function (field, value) {
selectedField = field;
selectedValue = value;
};
var getFieldAndValue = function () {
return {
"field": selectedField,
"value": selectedValue
}
};
return {
setFieldAndValue: setFieldAndValue,
getFieldAndValue: getFieldAndValue
};
})
What I want to achieve is this:
When no dates or sources are clicked the whole list shall be shown.
When Source or Date are clicked it shall get the posts based on these selections.
I cannot use filter on Date as the application receives some 3000 posts a day and so I have to query elastic search to get the posts for the selected date.
Up until now I have put the elastic-search in the listController, but I am now refactoring so the es-search happens in a service. This so the listController will receive the correct post based on the selections the user has done.
Question is: What is the best pattern or method to use when trying to achieve this?
Where your data is coming from is pretty irrelevant, it's for you to do the hook up with your data source.
With regards to how to render a list:
The view would be:
<div ng-controller='MyController as myCtrl'>
<form>
<input name='searchText' ng-model='myCtrl.searchText'>
</form>
<ul>
<li ng-repeat='item in myCtrl.list | filter:myCtrl.searchText' ng-bind='item'></li>
</ul>
<button ng-click='myCtrl.doSomethingOnClick()'>
</div>
controller would be:
myApp.controller('MyController', ['ElasticSearchService',function(ElasticSearchService) {
var self = this;
self.searchText = '';
ElasticSearchService.getInitialList().then(function(list) {
self.list = list;
});
self.doSomethingOnClick = function() {
ElasticSearchService.updateList(self.searchText).then(function(list) {
self.list = list;
});
}
}]);
service would be:
myApp.service('ElasticSearchService', ['$q', function($q) {
var obj = {};
obj.getInitialList = function() {
var defer = $q.defer();
// do some elastic search stuff here
// on success
defer.resolve(esdata);
// on failure
defer.reject();
return defer.promise();
};
obj.updateList = function(param) {
var defer = $q.defer();
// do some elastic search stuff here
// on success
defer.resolve(esdata);
// on failure
defer.reject();
return defer.promise();
};
return obj;
}]);
This code has NOT been tested but gives you an outline of how you should approach this. $q is used because promises allow things to be dealt with asynchronously.

Filter on user id

I have join table that creates an association between my movie and user table. If I use,
"ng-repeat" => "movie in movies"
I get all the movies added by all the users. So I'm trying to filter out all the results that do not correspond with the users id.
This is the JSON result of my movies_users request,
{"id":4,
"title":"Creed",
"release_date":"2016-01-21",
"image":"/xSE4NBFDzqedwa4AIj99r1Z7ljF.jpg",
"user_id":null,
"created_at":"2015-11-14T12:07:43.434Z",
"updated_at":"2015-11-14T12:07:43.434Z",
"movie_id":"312221",
"users":[
{"id":2,"email":"jan#jan.com","provider":null,"uid":null,"name":"Jan Jansen","username":null},
{"id":1,"email":"peter#peter.nl","provider":null,"uid":null,"name":"Peter Boomsma","username":null}]
},
{"id":5,
"title":"Star Wars: Episode VII - The Force Awakens",
"release_date":"2015-12-17",
"image":"/fYzpM9GmpBlIC893fNjoWCwE24H.jpg",
"user_id":null,
"created_at":"2015-11-14T12:13:40.413Z",
"updated_at":"2015-11-14T12:13:40.413Z",
"movie_id":"140607",
"users":[
{"id":1,"email":"peter#peter.nl","provider":null,"uid":null,"name":"Peter Boomsma","username":null}]
}
As you can see the movies have the associated users in them, but I'm wondering how I can only show the movies that also have the users id in the view.
I had this,
.movie_container{
"ng-if" => "movie.user_id == user_id",
"ng-repeat" => "movie in movies | orderBy:'release_date'"
}
But now I've changed my database/back-end it doesn't work anymore.
EDIT
loadMovies function
movieService.loadMovies().then(function(response) {
$scope.movies = response.data;
$scope.checkUserExists = function(movie_users) {
for(var i = 0; i < movie_users.length; i++) {
if(movie_users[i].id === movie.user.id) {
return true;
}
}
return false;
}
})
Create a function which will be called inside the ng-if directive and pass the movie iterable's users array to it:
ng-if="checkUserExists(movie.users)"
Now, define the function in your controller:
$scope.checkUserExists(movie_users) {
for(var i = 0; i < movie_users.length; i++) {
if(movie_users[i].id === user_id) {
return true;
}
}
return false;
}

Alert and Confirmation dialog box AngularJs

I'm trying to implement Alert/Confirmation dialog boxes using the bootstrap modal service in my angular app. I want to make it generic, based on the parameter that I pass to the modal's controller it will either behave as the alert box where the user can just close it on reading the message on the modal and also it can behave like a confirmation dialog where the user should say either 'OK' or 'Cancel' I need to capture the response from the user which I'm able to. Now, the problem here is that i've list of items say in a grid and when ever user wants to delete an item from the list I need to show the confirmation message box and based on the user response I need to either delete the item or leave it if users wishes to leave it in the list, but my item is being deleted first and then the confirmation dialog is showing up, I've tried using the call back but still no use. Please help me if anyone came across this situation.
Method that shows the alert:
$scope.showAlertMessage = function (modalName,commands,callback)
{
var modalOptions = {};
var alertMessageText;
var okBtn;
var cancelBtn;
var autoCloseTimeout;
$scope.modalResp === false;
if (modalName === 'ItemNotEligible')
{
modalOptions['template'] = 'application/Views/ItemNotEligibleAlert.html';
modalOptions['cntrlr'] = 'itemAlertsController';
modalOptions['winClass'] = 'item-alert-win';
alertMessageText = commands.alertMessage.text;
okBtn=true;
cancelBtn = false;
autoCloseTimeout=commands.alertMessage.autoDismissalTimeout;
}
else if (modalName === 'ItemDelete')
{
modalOptions['template'] = 'application/Views/ItemNotEligibleAlert.html';
modalOptions['cntrlr'] = 'itemAlertsController';
modalOptions['winClass'] = 'item-alert-win';
alertMessageText = commands.alertMessage.text;
okBtn = true;
cancelBtn = true;
autoCloseTimeout = commands.alertMessage.autoDismissalTimeout;
}
var params = { alertMessage: alertMessageText};
var modalInstance=$modal.open({
templateUrl: modalOptions.template,
controller: modalOptions.cntrlr,
windowClass: modalOptions.winClass,
resolve: {
modalParams: function () {
return params;
}
}
});
modalInstance.result.then(function (selected) {
$scope.modalResp = selected; //Response from the dialog
});
callback($scope.modalResp);
}
Method where the delete item logic exists and call to show alert method is made
this.voidItem = function (skuid) {
alertMessage.text = 'Are you sure you want to remove <strong>' + itemdata[itmid].split('|')[0] + '</strong> from your list?';
$scope.showAlertMessage('ItemDelete', commands, function (userresp) {
if (userresp === true) {
var lineId = 0;
for (var i = itemListState.length - 1; i >= 0; i--) {
if (parseInt(itemListState[i].item) === itmid && Boolean(itemListState[i].isItemVoid) != true) {
lineId = itemListState[i].Id;
if (lineId != 0) {
break;
}
}
}
poService.DeleteItem(itmid, lineId, function (modal) {
virtualtable = modal;
$scope.itemCount = $scope.itemCount - 1;
$scope.createlist(itmid);
});
}
});
}
The problem is that you are executing the callback instead of chain the method to the result of the promise
modalInstance.result.then(function (selected) {
callback($scope.modalResp); //Once the promise is solved, execute your callback
$scope.modalResp = selected; //Why you need to save the response?
// If the user press cancel o close the dialog the promise will be rejected
}, onUserCancelDialogFn);
A more simple way:
modalInstance.result
.then(callback, onErrBack);

Resources