I am iterating over an array in typescript to take a snapshot of a div having id equal to the index of the array and download an image file.
If the array was having 2 rows, 2 images should be downloaded. Here is the script:
public toCanvas() {
let i = Object.keys(this.array).length;
Object.keys(this.array).forEach((key, index)=>{
var elem = document.getElementById(index.toString());
console.log(index)
html2canvas(elem).then(function(canvas) {
var generatedImage = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
window.location.href=generatedImage;
});
})
}
Using Object.keys(this.array).forEach((key, index)
, I am iterating to get the index, and then find the document getElementId(index)
.
The problem is that it always download the last image.
And at the console, the indexes are consoled, at the beginning and then the image is downloaded:
The html script:
<mat-card [id]="i" *ngFor="let arrayOfData of array; let i=index; " class="example-card" #matCard>
I tried using while
inside forEach()
:
public toCanvas() {
let i = Object.keys(this.array).length;
Object.keys(this.array).forEach((key, index) => {
while (i != -1) {
i--;
console.log(i)
var elem = document.getElementById(index.toString());
html2canvas(elem).then(function (canvas) {
var generatedImage = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
window.location.href = generatedImage;
});
}
})
}
And it's doing the same behavior. It consoles the index until it reaches to 0
and then downloads the image of the last detected <mat-card>
EDIT
Here is a stackblitz of the recursive method provided by @xyz.
Your implementation may be different, change the code accordingly. Considering the elements in the array is like: array = [0, 1, 2];
. And elements in the array are the ids of divs that you want to download. So your divs are like:
<div [id]="id" *ngFor="let id of array">
I am div id: {{id}}
</div>
Implementation 1
You can leverage rxjs
to download the divs in a sequence, lets make the array as an Observable and work on each value in the array before starting the next download(Will use concatMap
for this).
public downloadDivs() {
from(this.array).pipe(
concatMap((arrayElem) => {
let docElem = document.getElementById(arrayElem.toString());
return from(html2canvas(docElem).then(function (canvas) {
let generatedImage = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
let a = document.createElement('a');
a.href = generatedImage;
a.download = `${arrayElem}.png`;
a.click();
return `${arrayElem}.png`;
}));
})
).subscribe((imageName) => {
console.log("Image downloaded", imageName);
})
}
Your HTML has this button to trigger the call:
<button (click)="downloadDivs()">Download divs using rxjs</button>
Implementation 2
call the download of the next div
only after the download of one div is complete. I have used recursion
for this.
public trivialDownload() {
console.log("Downloading image one by one, without a loop");
this._download(0, this.array);
}
// this method will keep calling itself until all the elements of the array are scanned
private _download(index, array) {
if (index >= array.length) {
console.log("Done!")
} else {
let docElem = document.getElementById(array[index].toString());
html2canvas(docElem).then((canvas) => {
let generatedImage = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
let a = document.createElement('a');
a.href = generatedImage;
a.download = `${array[index]}.png`;
a.click();
// at this point, image has been downloaded, then call the next download.
this._download(index + 1, array)
});
}
}
See this link for the implementation: https://stackblitz.com/edit/download-div-using-canvas?file=src%2Fapp%2Fapp.component.ts