0

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.

2 Answers 2

1

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

5 Comments

thatnks @floribon but doc.save and doc.addImage or any of the jsPDF functions are not asynchronous. I wish they were, and tried to use them asynchronously originally.
@pedalpete I see, I have updated my answer in the case doc.save is actually synchronous. You need to make sure the button is disabled before you save the PDF (which takes 3s if I understand correctly)
This did work @floribon, the only change I had to make was that I had to add a time time to the timeout. I've used 1000. Strangely, it takes 3 seconds to download save the file, but the $timeout knows to wait until the function is complete. Going with a smaller timeout didn't work for some reason. Weird... But it works.
It doesn't really make sense as the save occurs after the timeout has waited its 1000ms (since it is inside it). You probably have something else running in parallel, which is the one taking 3s. So $timeout wants to wait 1000ms, but the processor is busy doing the other thing (it is javascript so only one thing is done at a time), hence the $timeout "wait" more. However it is unsafe to rely on that as some users computer could be faster or slower, so you need to be deterministic and find what is causing this 3s delay.
Yeah, that didn't make sense to me either, but this works, without specifying a delay to the timeout, the button is never toggled. So I'll go with this for now and keep my eyes open on for issues.
1

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.