1

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

5
  • do you mean k and i undefined ? Commented Jun 26, 2015 at 10:29
  • yes k and i are undefined i guess, but they work when i'm not using $timeout Commented Jun 26, 2015 at 10:29
  • Reference (possible duplicate): JavaScript closure inside loops – simple practical example Commented Jun 26, 2015 at 10:36
  • no this is asynchronous event problem Commented Jun 26, 2015 at 10:38
  • @K.Toress Have a look at the linked topic, it is about asynchronous execution. Commented Jun 26, 2015 at 10:40

4 Answers 4

6

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);
Sign up to request clarification or add additional context in comments.

Comments

2

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

Comments

1

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;
         }
    }
}

Comments

0

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.

4 Comments

hmm.. do you know how i can fix this?
why do you need it inside timeout ?
honestly, my first time using $timeout, this is how i saw it in documentation
i need some delay, so that the card flips over, then flips back over if not a match

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.