pedalpete
pedalpete

Reputation: 21556

toggling button in angular doesn't seem to be working

I've got an angular app where I'm saving a file to pdf. What I want to happen is that when the user clicks the save button, the save button disappers while the file is being made, so that the user doesn't press the button multiple times.

What I have is

function toggleSavingPdf(){
  return scope.savingPdf = !scope.savingPdf;
}

function saveToPdf(){
  toggleSavingPdf();

  var doc = new jsPDF();

  scope.docs.forEach(function(img, idx){
    if(img.src){
      console.log('index of page', idx);
        if(idx>0) doc.addPage();
        doc.addImage(img.src,'png',0, 0, 210, 290);
     }
     if(idx===scope.docs.length-1){
       console.log('change saving', scope.savingPdf);
       toggleSavingPdf();          }
  });

  doc.save('Kiosk.pdf');
}

and in my template I have

<div id="document-list" ng-show="savingPdf">

but the document-list never hides, unless I change the ng-show to ng-hide, in which case it never shows.

I've tried running scope.$apply() after each toggleSavingPdf(), but it tells me an apply is already in progress.

This must be possible. It takes about 3+ seconds to create the pdf, so ample time for the user to hit the button multiple times.

Upvotes: 0

Views: 56

Answers (2)

floribon
floribon

Reputation: 19193

As is the code is never waiting for anything. So even if it takes 3s to save your file, your code will be executed in an instant.

I don't know the code of your function doc.save, but I assume it is asynchronous and it is the one that takes some time. So it should either return a promise, or take a callback in parameter that will be executed when the save is done (about 3s later).

Your code would then become:

function saveToPdf(){
  toggleSavingPdf();

  var doc = new jsPDF();

  // Save the PDF
  doc.save('Kiosk.pdf', function() {
    // Now it is saved, execute the rest

    scope.docs.forEach(function(img, idx){
      if (img.src) {
        console.log('index of page', idx);
        if(idx>0) doc.addPage();
        doc.addImage(img.src,'png',0, 0, 210, 290);
      }
    });

    // No need to put that into the forEach, forEach is
    // synchroneous so that will be executed only after
    console.log('change saving', scope.savingPdf);
    toggleSavingPdf(); 

  });
}

Note that you may need to call scope.$apply after the last toggleSavingPdf() if the asynchronous doc.save is out of Angular context (i.e. not an $http call but a regular Ajax one)

UPDATE: If the function save is synchronous and executed client side, then it will block the website while it is processing, which means the user probably cannot click on the button anyway. But what you want to do is disable the button, then render the HTML with that disabled button, and only then execute the save method, after which you can enable the button again. For that you need to use $timeout to be sure Angular has updated the DOM before you save the doc. See code:

function saveToPdf(){
  toggleSavingPdf();

  // Let Angular some time to render the DOM with the disabled button
  $timeout(function() {

    // Now we can save the PDF

    var doc = new jsPDF();   
    doc.save('Kiosk.pdf');

    scope.docs.forEach(function(img, idx){
      if (img.src) {
        console.log('index of page', idx);
        if(idx>0) doc.addPage();
        doc.addImage(img.src,'png',0, 0, 210, 290);
      }
    });

    console.log('change saving', scope.savingPdf);
    toggleSavingPdf(); 

    // No need to call scope.$apply here, $timeout will take care of that
  });
}

Upvotes: 1

Diadistis
Diadistis

Reputation: 12174

Since you are not providing enough information to be sure I'll just take a shot in the dark and assume it's the most common problem: scoping issue.

If your function is running inside a child scope then don't expect your parent variable to change. Example

One solution is to update an object property instead of the scope variable.

$scope.progress.savingPdf = true;

Example

Hope this helps.

Upvotes: 1

Related Questions