angularjs state cache false prevents re-attaching canvas to div

12 Views Asked by At

I discovered an issue where user goes to PageA, user clicks a button that opens a Modal, the modal initiates a rest API call and with info returned from the call is used to create a qrCode and attach the qrCode as a <canvas> to a DIV created in an ng-repeat. The modal will refresh the qrCodes every 5 minutes (based on timestamp). While on PageA the user can open/close the modal with out issue. All seems good.

However, when the user closes the modal and navigates away from PageA, then back to PageA and opens the modal again the qrCodes are created but can't attach to the DIVs anymore. When the modal is opened I can see the API call happen again, I can see other info from the API call populating specific fields, and as best I can tell the qrCode function is creating the image but it is not able to attach the DIVs again. Every 5 minutes I see the API called again but the new qrCode images never get attached again. In the source code I can see the proper DIVs that the <canvas> are supposed to attach to - so the DIV elements do exist.

After hours upon hours of troubleshooting this I saw my state controller for this page with cache: false. I need it set to false for a variety of reasons. But on a whim I set cache: true for this page and tested again....this time the problem didn't happen. I could navigate away from PageA and back again and the qrCode images were now attaching properly to the DIVs and refreshing correctly every 5 minutes (and verified they were new qrCode images).

What is happening here? I need the page cache: false for other reasons, but the only way I can get this qrCode issue to work properly is to set cache: true. How can I resolve this?

STATE:

  .state('tab.clubs', {
    cache: true,
    url: '/clubs',
    params: tabParams,
    views: {
      'tab-clubs': {
        templateUrl: 'templates/tab-clubs.html',
        controller: 'ClubCtrl'
      }
    }
  })
  .state('tab.club-detail', {
    cache: true,    // true: everything works, false: fails after routing away/back to club-detail
    url: '/clubs/:ceID',
    params: tabParams,
    views: {
      'tab-clubs': {
        templateUrl: 'templates/detail-clubs.html',  //this is the page with the modal on it
        controller: 'ClubDetailCtrl'
      }
    }
  })

controller:

  $scope.ticketTimer ;
  $scope.ticketCSS = {} ;
  $scope.genQRCode = function() {
    $scope.refreshTickets = 1 ;
    clubService.getTickets($scope.club.cID,$scope.club.clID,$scope.club.ceID)
    .then(function(response) {
      $scope.refreshTickets = 0 ;
      if (response.success == true) {
        for (var x=0;x<response.tickets.length;x++) {
          var ticket = response.tickets[x] ;
            $scope.ticketCSS[ticket.ticketID] = {} ;
          // clear existing <canvas> from ticketDIV otherwise
          // the jQuery qrcode will just add more qrCodes to the DIV
          angular.element('#ticketID_'+ticket.ticketID).empty() ;
          if (ticket.scode == 0) {
            var ts = new Date(new Date().getTime() + 5 * 60000).toISOString().slice(0,19).replace("T"," ") ;
            $scope.ticketCSS[ticket.ticketID].status = "Open" ;
            $scope.ticketCSS[ticket.ticketID].stampTitle = "Refresh" ;
            $scope.ticketCSS[ticket.ticketID].stampText = ts ;
            $scope.ticketCSS[ticket.ticketID].img = "img/LiveScanner.gif" ;
            $scope.ticketCSS[ticket.ticketID].stampColor = "black" ;
          } else if (ticket.scode == 1) {
            var ts = new Date(ticket.sdate).toISOString().slice(0,19).replace("T"," ") ;
            $scope.ticketCSS[ticket.ticketID].status = "Scanned" ;
            $scope.ticketCSS[ticket.ticketID].stampTitle = "Scanned" ;
            $scope.ticketCSS[ticket.ticketID].stampText = ts ;
            $scope.ticketCSS[ticket.ticketID].stampColor = "green" ;
            $scope.ticketCSS[ticket.ticketID].img = "img/scanned1.png" ;
          } else if (ticket.scode == 2) {
            var ts = new Date(ticket.sdate).toISOString().slice(0,19).replace("T"," ") ;
            $scope.ticketCSS[ticket.ticketID].status = "Expired" ;
            $scope.ticketCSS[ticket.ticketID].stampTitle = "Expired" ;
            $scope.ticketCSS[ticket.ticketID].stampText = ts ;
            $scope.ticketCSS[ticket.ticketID].img = "img/expired.png" ;
            $scope.ticketCSS[ticket.ticketID].stampColor = "red" ;
          }
          if (ticket.ticketRefund == 0) {
            var refund = "No" ;
          } else {
            var refund = "Yes" ;
          }
          $scope.ticketCSS[ticket.ticketID].refunds = refund ;
          $scope.ticketCSS[ticket.ticketID].trans = "No" ;
          $scope.ticketCSS[ticket.ticketID].desc = ticket.ticketDesc ;
          
          // generate qrCode, this attaches a canvas to the specified element:
          // http://jeromeetienne.github.com/jquery-qrcode
          jQuery('#ticketID_'+ticket.ticketID).qrcode({width:150,height:150,text:ticket.liveCode}) ;
        }
      }
    }) ;

    // create timer to refresh tickets in 5 minutes
    $scope.ticketTimer = $timeout(function(){
      $scope.genQRCode() ;
    },300000) ;  // 5 minutes = 300000
  } ;
  
  
  $ionicModal.fromTemplateUrl('ticketModal.html', {
    scope: $scope,
    animation: 'slide-in-up',
    backdrop: 'static',
    backdropClickToClose:false,
    focusFirstInput: false 
  }).then(function(modal) {
    $scope.tixModal = modal;
  });
  
  
  $scope.openTicketModal = function(id) {
    
    // this interval is need to let ng-repeat in modal populate
    // before triggering the genQRCode() function - otherwise, the genQRCode()
    // will create the code and try to attach the `canvas` to the DIV
    // before the div is actually rendered - causing it to fail.
    //
    // this is needed because modal template/scripts are loaded into memory
    // when page/controller is loaded (even before modal is opened) causing genQRCode()
    // to initialize cause the same DIV not yet created issue above.  Not certain how to get
    // ng-repeat to call function on $last element AFTER the modal is opened. bizarre behavior.
    $scope.ticketInit = $interval(function() {
      var tid = $scope.club.tickets.length-1 ;
      var tik = $scope.club.tickets[tid] ;
      //if (angular.element(document.querySelector('#ticketID_'+tid))) {
      if (angular.element('#ticketID_'+tik.ticketID).length > 0) {
        $interval.cancel($scope.ticketInit) ;
        $scope.genQRCode() ;
      }
    },100) ;
    //$scope.tixModal.show() ;
    $rootScope.globalModalShow($scope.tixModal) ;
  } ;
  $scope.closeTicketModal = function(id) {
    if ($scope.ticketTimer !== null) {
      $timeout.cancel($scope.ticketTimer) ;
    }
    //$scope.tixModal.hide() ;    
    $rootScope.globalModalClose($scope.tixModal) ;
  }  ;

HTML:

  <div id="ticketContainer" ng-show="!refreshTickets" style="width:90%;margin-left:5%;margin-top:10px;overflow-y:hidden;">
    <div ng-repeat="ticket in club.tickets" id="ticketDiv_{{ticket.ticketID}}" style="position:relative;font-weight:bolder;min-width:100%;border:1px solid black;margin-bottom:20px;;" class="">
      <div id="ticketCount_{{ticket.ticketID}}" style="text-align:center;">{{$index+1}} of {{club.tickets.length}}</div>
      <div id="ticketScanner_{{ticket.ticketID}}" class="centerDivContents" style="position:absolute;margin-top:10px;">
        <img id="ticketImg_{{ticket.ticketID}}" ng-src="{{ticketCSS[ticket.ticketID].img}}">
      </div>
      <div id="ticketID_{{ticket.ticketID}}"class="centerDivContents" style="margin-top:20px;" ></div>
      <div style="margin:15px;">
        <div id="ticketName_{{ticket.ticketID}}" style="">Type: {{ticket.ticketName}}</div>
        <div id="ticketStatus_{{ticket.ticketID}}" style="">Status: <span style="color:{{ticketCSS[ticket.ticketID].stampColor}}">{{ticketCSS[ticket.ticketID].status}}</span></div>
        <div id="ticketStamp_{{ticket.ticketID}}" style="">{{ticketCSS[ticket.ticketID].stampTitle}}: <span style="color:{{ticketCSS[ticket.ticketID].stampColor}}">{{ticketCSS[ticket.ticketID].stampText}}</span></div>
        <div id="ticketRefund_{{ticket.ticketID}}" style="">Refunds: <span style="">{{ticketCSS[ticket.ticketID].refunds}}</span></div>
        <div id="ticketTrans_{{ticket.ticketID}}" style="">Transfers: <span style="">{{ticketCSS[ticket.ticketID].trans}}</span></div>
        <div id="ticketDesc_{{ticket.ticketID}}" style="white-space:break-spaces;">Desc: {{ticket.ticketDesc}}</div>
      </div>
    </div>
  </div>
0

There are 0 best solutions below