SuperVeetz
SuperVeetz

Reputation: 2176

AngularJS Using $timeout giving undefined error

Im trying to make one of these memorization games, where u need to flip 2 cards over and try to match the images on the other side otherwise they both turn back over.

My code is working fine, except that I need to add a delay/pause before the 2 cards get flipped back over if no match is made. I'm trying to use $timeout, but i'm getting an error: TypeError: Cannot set property 'isFlipped' of undefined

HTML

<body ng-controller="MainCtrl as main">
<figure ng-class="{true: 'flipped', false: 'not-flipped'}[card.isFlipped]" class="card" 
        ng-repeat="card in cards" ng-click="flipCard(card.id, card.pair)">
  <img ng-src="img/cardback1.png" class="back"></img>
  <img ng-src="{{card.img}}" class="front"></img>
</figure>
</body>

MyCtrl

angular.module('CardApp').controller('MainCtrl', ['$scope', '$timeout', function($scope, $timeout) {

  $scope.cards = [
    {
      img: 'img/chantal1.jpg',
      id: 1,
      isFlipped: false,
      pair: 1,
      matched: false,
    },
    {
      img: 'img/chantal1.jpg',
      id: 2,
      isFlipped: false,
      pair: 1,
      matched: false,
    },
    {
      img: 'img/chantal2.jpg',
      id: 3,
      isFlipped: false,
      pair: 2,
      matched: false,
    },
    {
      img: 'img/chantal2.jpg',
      id: 4,
      isFlipped: false,
      pair: 2,
      matched: false,
    },
    {
      img: 'img/chantal3.jpg',
      id: 5,
      isFlipped: false,
      pair: 3,
      matched: false,
    },
    {
      img: 'img/chantal3.jpg',
      id: 6,
      isFlipped: false,
      pair: 3,
      matched: false,
    },

    ];

  $scope.unflipCards = function(k, i) {
    $scope.cards[k].isFlipped = false;
    $scope.cards[i].isFlipped = false;
  };

  $scope.flipCard = function(id, pair) {

    var cards = $scope.cards;

    for(var i = 0, j = cards.length; i < j; i++) {
      // find card id in cards that is not matched
      if(cards[i].id == id && cards[i].matched == false) {
        // flip card
        cards[i].isFlipped = !cards[i].isFlipped;
        // check if any other cards are flipped
        for(var k = 0, j; k < j; k++) {
          // if we find another card that is flipped and is not yet matched
          if(cards[k].isFlipped == true && cards[k].matched == false && cards[k].id != id) {
            // check if this card is a pair with the current card
            if(cards[k].pair == pair) {
              // set matched to true
              cards[k].matched = true;
              cards[i].matched = true;
            } else {
              $timeout(function(){$scope.unflipCards(k, i)}, 1000); // undefined error
              //$scope.unflipCards(k, i); // works
            }
          }
        }
      }
    }

  };



}]);

I will try to reproduct in JSFiddle now

Upvotes: 1

Views: 551

Answers (4)

Icycool
Icycool

Reputation: 7179

While I don't know the reason of $timeout not working, I'd suggest simplify the whole code by passing the card object itself instead of its attribute.

ng-click='flipCard(card)'

$scope.flipCard = function(card) {
    if (card.matched) return;
    card.isFlipped = true;

    var cards = $scope.cards;
    for (var i=0; i<cards.length; i++) {
         var card2 = cards[i];

         // skip if it is self
         if (card2 == card) continue;

         // check if any other cards are flipped
         if (card2.isFlipped) {
             if (card.pair == card2.pair) {
                 card.matched = card2.matched = true;
             }
             else {
                 $timeout(function(){
                     card.isFlipped = card2.isFlipped = false;
                 }, 1000);
             }

             break;
         }
    }
}

Upvotes: 1

Nikhil Aggarwal
Nikhil Aggarwal

Reputation: 28445

The error occurs because k & i are undefined. You can achieve the desired results by updating your code from

$timeout(function(){$scope.unflipCards(k, i)}, 1000);

to

$timeout(function(){var x=k;var y=i;$scope.unflipCards(x, y)}, 1000);

For reference - http://plnkr.co/edit/o1namfsvFZbWlffmQsvd?p=preview

Upvotes: 2

Sebastian Nette
Sebastian Nette

Reputation: 7812

By the end of the cycle, k and i are already one above the last cards index. So both $scope.cards[k] and $scope.cards[i] don't exist and are therefor undefined.

You could do something like this:

$scope.unflipCardsTimeout = function(k, i) {
    $timeout(function(){$scope.unflipCards(k, i)}, 1000);
};

and in your loop replace:

$timeout(function(){$scope.unflipCards(k, i)}, 1000);

with:

$scope.unflipCardsTimeout(k, i);

Upvotes: 6

Kalhan.Toress
Kalhan.Toress

Reputation: 21901

here $timeout is execute after the for loop finished because timeout function will call in the next digest cycle. so at that point there is no k and i exists so they are undefined.

Upvotes: 0

Related Questions