6

I have a JSON Output of items - To display a single item i use ng-repeat="item in items". And I can access the currently logged in user object with user

Each item can belong to multiple wishlists of users. If a user adds an item to his wishlist the user_id gets saved within item.wishlists

The JSON Output of a single item looks simplified something like this:

{"id":1,"title":"This is a tile","wishlists":[{"user_id":2},{"user_id":3}]}

when i do user.id i get the ID of the current logged in user.

Now I want to use ng-if within the ng-repeat="item in items" to check if the current_user has already added this item to his wishlist, and then to display this user an "remove from wishlist" button instead of "add to wishlist".

My not working approach is:

<div ng-repeat="item in items">
  <h3>{{item.title}}</h3>
  <a href="" ng-click="addToWishlist(item)" ng-if="item.wishlists.indexOf(user.id) == -1">Wish</a>
  <a href="" ng-click="removeFromWishlist(item)" ng-if="item.wishlists.indexOf(user.id) > -1">Undo-Wish</a>
</div>

Can I use indexOf like this? Or what would be the correct angularJS way to check if user.id is within item.wishlists user_id's?

Update:

View: (ctrl.hasWished changes the icon from 'favorite' if true to 'favorite_border' if false)

<div ng-repeat="item in items">
      <md-button class="md-icon-button feed-wish md-warn" aria-label="Wish" ng-click="ctrl.toggleWish(item)">
        <i class="material-icons">favorite{{ctrl.hasWished(item) ? '' : '_border' }}</i>
      </md-button> 
</div>

Controller:

  // Check if User has wished this item already: 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
    });
  };

  this.toggleWish = function(item) {
    if (!this.hasWished) {
      this.hasWished = true;
      items.wish(item); // sends PUT request
      ToastService.show(item.product + ' added to Wish List');
    } else {
      this.hasWished = false;
      items.unWish(item); // sends DELETE request
      ToastService.show(item.product + ' removed from Wish List');
    }
  }

When I click on the button if its false before, it sends the PUT request, and I also get the ToastService message "added..", if I click again it sends the DELETE request and the ToastService message "removed..". But the Icon doesn't change from "favorite" to "favorite_border". When I reload the page the right Icons are displayed.

I also have this error message within the console:

TypeError: Cannot read property 'id' of null
    at http://ruby-on-rails-140223.nitrousapp.com:3000/assets/auctions/auctionCtrl-f2cb59b8ad51e03a2264ef2c6e759a54.js?body=1:33:52
    at Array.some (native)
    at hasWished (http://ruby-on-rails-140223.nitrousapp.com:3000/assets/auctions/auctionCtrl-f2cb59b8ad51e03a2264ef2c6e759a54.js?body=1:32:30)
    at fn (eval at <anonymous> (http://ruby-on-rails-140223.nitrousapp.com:3000/assets/angular/angular-81b9b668c3f93c8ccb76b27ad984d9b6.js?body=1:13323:15), <anonymous>:4:318)
    at Object.expressionInputWatch [as get] (http://ruby-on-rails-140223.nitrousapp.com:3000/assets/angular/angular-81b9b668c3f93c8ccb76b27ad984d9b6.js?body=1:14303:31)
    at Scope.$digest (http://ruby-on-rails-140223.nitrousapp.com:3000/assets/angular/angular-81b9b668c3f93c8ccb76b27ad984d9b6.js?body=1:15819:40)
    at Scope.$apply (http://ruby-on-rails-140223.nitrousapp.com:3000/assets/angular/angular-81b9b668c3f93c8ccb76b27ad984d9b6.js?body=1:16098:24)
    at done (http://ruby-on-rails-140223.nitrousapp.com:3000/assets/angular/angular-81b9b668c3f93c8ccb76b27ad984d9b6.js?body=1:10547:47)
    at completeRequest (http://ruby-on-rails-140223.nitrousapp.com:3000/assets/angular/angular-81b9b668c3f93c8ccb76b27ad984d9b6.js?body=1:10745:7)
    at XMLHttpRequest.requestLoaded (http://ruby-on-rails-140223.nitrousapp.com:3000/assets/angular/angular-81b9b668c3f93c8ccb76b27ad984d9b6.js?body=1:10686:9)

and when clicking also this error:

TypeError: v2.hasWished is not a function
    at fn (eval at <anonymous> (http://ruby-on-rails-140223.nitrousapp.com:3000/assets/angular/angular-81b9b668c3f93c8ccb76b27ad984d9b6.js?body=1:13323:15), <anonymous>:4:318)
    at Object.expressionInputWatch [as get] (http://ruby-on-rails-140223.nitrousapp.com:3000/assets/angular/angular-81b9b668c3f93c8ccb76b27ad984d9b6.js?body=1:14303:31)
    at Scope.$digest (http://ruby-on-rails-140223.nitrousapp.com:3000/assets/angular/angular-81b9b668c3f93c8ccb76b27ad984d9b6.js?body=1:15819:40)
    at Scope.$apply (http://ruby-on-rails-140223.nitrousapp.com:3000/assets/angular/angular-81b9b668c3f93c8ccb76b27ad984d9b6.js?body=1:16098:24)
    at HTMLButtonElement.<anonymous> (http://ruby-on-rails-140223.nitrousapp.com:3000/assets/angular-touch/angular-touch-b4d02ed602708a1c93b51087222e0363.js?body=1:478:13)
    at HTMLButtonElement.eventHandler (http://ruby-on-rails-140223.nitrousapp.com:3000/assets/angular/angular-81b9b668c3f93c8ccb76b27ad984d9b6.js?body=1:3299:21)
1
  • try to use (item.wishlists|filter:{user_id:user.id}).length==0 Commented Nov 16, 2015 at 21:17

3 Answers 3

4

Filter should works:

<div ng-repeat="item in items">
  <h3>{{item.title}}</h3>
  <a href="" ng-click="addToWishlist(item)" ng-if="!(item.wishlists|filter:{user_id:user.id}).length">Wish</a>
  <a href="" ng-click="removeFromWishlist(item)" ng-if="(item.wishlists|filter:{user_id:user.id}).length">Undo-Wish</a>
</div>
Sign up to request clarification or add additional context in comments.

3 Comments

i would avoid putting so much logic into the view, remember seperation of concerns, this code would be better on a function inside a controller
We have to add two ng-repeat blocks with same filters if we'll want to render wished/notWished user lists, and all will looks ok. It's not a logic. It just view options. But I agree, in any cases this will need in optimization. Then datasource will return bool mark for this in answer.
@RanSasportas yes, I think I will implement your the logic of your example within the controller later, for now its just a simple solution to build the remove function in the backend.
2

you cannot use indexOf function for this task. instead i recommand you to create a function on the scope within the controller which checks if the wishlist contains the user in that manner -

function isUserOnWishlist(item) {
    return item.wishlists.some(function(wishlist) {
        return wishlist.user_id === user.id; // ( on $scope ? )
    });
}

on the view -

<a href="" ng-click="addToWishlist(item)" ng-if="!isUserOnWishlist(item.wishlists)">Wish</a>
<a href="" ng-click="addToWishlist(item)" ng-if="isUserOnWishlist(item.wishlists)">Undo-Wish</a>

the isUserOnWishlist function will iterate on each user on the wishlist and return true only if it will find atleast one wishlist which passes this - wishlist.user_id === user.id. you could have used the indexOf function if the wishlist was a list of int's instead list of objects.

i recommend you check out the MDN js array functions documentation, you can find there explanation about all the cool array functions thats comes out of the box with js.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array

good luck.

3 Comments

Hi, I am using this now it works, but if the wishlist is empty, i get this error: TypeError: Cannot read property 'id' of null at Array.some (native) - how can i solve this?
Are you sure the user variable isn't null?
yes, I am using return wishlist.user_id === Auth._currentUser.id instead of user.id. (I will update my question to show you what I am doing alltogether right now)
1

Don't use indexOf, check this post: "There is one final aspect to consider: equality. The indexOf method uses strict equality (===) to determine if an element matches your search. This works great for primitives like strings and numbers. But you may have difficulty using indexOf to search for a given object or array" finding-array-elements-with-array-indexof

You can use $filter API Reference / ng / filter components in ng / filter, or maybe, you should create a function in your controller passing as a paremeters the current item and the user.id to do this or create a custom directive. Check this if you want an idea for your custom function: how-to-find-index-of-an-item-in-javascript-object-array/

Regards

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.