4

My problem is simple. I have this interface built with Angular. When I click on 1 it opens the popup, and then when I click on 2 it calls a function:

showDownloadLink(ev, name) {
    let config = new MdDialogConfig()
        .textContent('<a href="http://www.google.fr">lol</a>')
        .clickOutsideToClose(false)
        .title('Would you like to delete your debt?')
        .ariaLabel('Lucky day')
        .ok('Please do it!')
        .cancel('Sounds like a scam')
        .targetEvent(ev);
    this.dialog.open(MdDialogBasic, this.element, config)
        .then((ref: MdDialogRef) => {
            ref.whenClosed.then((result) => {
                if (result) {
                    var url = this._Service.getDownloadLink(name);
                    console.log(url);
                    //window.open(url,'Download');
                    //this.downloadURI(url );
                }
                else {
                    this.status = 'You decided to keep your debt.';
                }
            })
        });
}

downloadURI(uri) {
    var link = document.createElement("a");
    link.href = uri;
    link.click();
}

The module used is ng2-material with a "confirm dialog" button.

The var url gives me a good url in the console console.log(url); => when I click on the link shown in the console, it downloads the file well :)

The problem is that the download is not triggered in code.

Edit 1 :

After moving some things-

Dashboard.components.ts :

downloadFile(url,file) {
    this._Service.getDownloadFile(url,file);
}

The downloadFile is called in the show download link like before.

In the _service.ts:

import {Injectable} from "angular2/core";
import {S3Object} from "./s3object";
import {Observable} from "rxjs/Observable";
import {Http, Headers, Response} from "angular2/http";
import "rxjs/Rx";
import {CredService} from "./cred.service";
//mettre les parenthèses !!
@Injectable()
export class Service {

    private serviceUrl = 'url';
    private token;
    private headers = new Headers();
    private credS;

    constructor(private http:Http, private cred:CredService) {
        this.credS = cred;
        this.token = cred.getToken();
        this.headers.append('Authorization', 'bearer ' + this.token);
        this.headers.append('Content-Type', 'application/json');
    }

    getDownloadLink(path:string){
        var opUrl = 'download.json?objectKey='+path;
        var urlRes = "";
        this.http.get(
            this.serviceUrl + opUrl,
            { headers: this.headers }
        )
            .map(res => res.text())
            .subscribe(
                data => urlRes = data,
                err => console.log(err),
                () => console.log(urlRes)
            );

        //console.log(urlRes);
        return urlRes;
    }

    getDownloadFile(url:string, file:string) {
        this.http.get(url).subscribe(
            (response) => {
                var blob = new Blob([response._body], {type: "application/text"});
                var filename = file;
                saveAs(blob, filename);
            });
    } 
}

And a customBrowserXhr.ts file:

import {Injectable} from 'angular2/core';
import {BrowserXhr} from 'angular2/http';

@Injectable()
export class CustomBrowserXhr extends BrowserXhr {
    constructor() {}
    build(): any {
        let xhr = super.build();
        xhr.responseType = "blob";
        console.log("it's cool bro ! ");
        return <any>(xhr);
    }
}

Edit 2:

My console Log of the execution of the function ( I replaced the correct url by URL in the code)

test url ddl before : "URL/Desert.jpg?serieofparams"
test name ddl before : Desert.jpg

The function:

downloadFile(url,file) {
    console.log('test url ddl before : ' + url);
    console.log('test name ddl before : ' + file);
    this.http.get(url).subscribe(
        (response) => {
            var blob = new Blob([response._body], {type: "image/jpeg"});
            var filename = file;
            saveAs(blob, filename);
        });
}

But I got this error:

Failed to load resource: the server responded with a status of 404 (Introuvable) http://localhost:8080/context/%22URL/Desert.jpg?serieofparams%22
angular2.dev.js:23740 EXCEPTION: [object Object]

Does anyone have any idea of what is causing this? Is it possible that the routing module does this? On this Plunkr it's working perfectly.

Slater
  • 817
  • 2
  • 10
  • 16

2 Answers2

2

An approach would be to load the image through an AJAX request and then leverage the FileSaver library to open the download dialog.

Here is a sample. You need to extend the BrowserXhr class to set the response type.

@Injectable()
export class CustomBrowserXhr extends BrowserXhr {
  constructor() {}
  build(): any {
    let xhr = super.build();
    xhr.responseType = "blob";
    return <any>(xhr);
  }
}

Then you can leverage this class to execute the request to get the image content:

@Component({
  selector: 'download',
  template: `
    <div (click)="downloadFile() ">Download</div>
  `,
  providers: [
    provide(CustomBrowserXhr, 
      { useClass: CustomBrowserXhr }
  ]
})
export class DownloadComponent {
  @Input()
  filename:string;

  constructor(private http:Http) {
  }

  downloadFile() {
    this.http.get(
      'https://mapapi.apispark.net/v1/images/'+this.filename).subscribe(
        (response) => {
          var mediaType = 'application/pdf';
          var blob = new Blob([response._body], {type: mediaType});
          var filename = 'test.pdf';
          saveAs(blob, filename);
        });
    }
}

This override (setting the response type) would only applies for this component (don't forget to remove the corresponding provide when bootstrapping your application) because I set the provider in the providers attribute of the component. The download component could be used like that:

@Component({
  selector: 'somecomponent',
  template: `
    <download filename="'Granizo.pdf'"></download>
  `
  , directives: [ DownloadComponent ]
})

See this answer for more details:

Community
  • 1
  • 1
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • I'm looking on it, even if I don't understand this possible solution well. – Slater Apr 12 '16 at 12:25
  • In fact, you load the image content through an AJAX request. The problem with Angular2 is that it only supports text content right now. So you need to tweak a bit to make it possible ;-) I updated my answer to give more details... – Thierry Templier Apr 12 '16 at 12:30
  • in the case that i don't know the file type is it working too ? it's png in the screen but it can be pdf or other kind of files – Slater Apr 12 '16 at 12:38
  • Yes, but you need to extract the type from the `Content-Type` header of the response... – Thierry Templier Apr 12 '16 at 12:41
  • Okey n, I used your solution and the download start BUUUUUT each file is corrupted :/ – Slater Apr 12 '16 at 13:32
  • Great! Are you sure the `responseType` property is set? I mean: is the `CustomBrowserXhr` used? – Thierry Templier Apr 12 '16 at 13:33
  • I added this line in the build(): any console.log("it's cool bro ! "); When the download is launched there is no message displayed in the console .. I suppose it's because the custom class is not really used ... In the main.ts i got this : bootstrap(AppComponent, [ROUTER_PROVIDERS,HTTP_PROVIDERS,provide(BrowserXhr, { useClass: CustomBrowserXhr })]); – Slater Apr 12 '16 at 13:38
  • Did you specify a provider for it within the `providers` attribute of the component where you execute the HTTP request? – Thierry Templier Apr 12 '16 at 13:39
  • The http request is executed in a service @injectable() with only a constructor :/ – Slater Apr 12 '16 at 13:41
  • I edited the question to show you exactly what i have actually :) – Slater Apr 12 '16 at 13:50
  • Great! Try to move the provider for the `CustomBrowserXhr` class into the `providers` attribute of your dahsboard component. – Thierry Templier Apr 12 '16 at 13:55
  • Not working ... @Component({ selector: 'my-dashboard', templateUrl: 'app/partials/dashboard.component.html', directives: [MATERIAL_DIRECTIVES], providers: [ provide(BrowserXhr, { useClass: CustomBrowserXhr }) ] }) And I imported : import {CustomBrowserXhr} from "./customBrowserXhr"; import {BrowserXhr} from "angular2/http"; import {Component, OnInit, ElementRef, provide} from 'angular2/core'; And same thing the custom is not used ... – Slater Apr 12 '16 at 14:03
  • Mum, that's really strange. I can make it work. See this plunkr: http://plnkr.co/edit/tfpS9k2YOO1bMgXBky5Y?p=preview. Your code seems fine and should work. Can you see a difference with my plunkr? – Thierry Templier Apr 12 '16 at 14:10
  • The main difference is i don't call the http.get at the same place than you ... :/ except that i can't see any differences – Slater Apr 12 '16 at 14:25
  • Okey found out where is the problem. In the getDownloadLink function the return is empty BUT i can clearly append the urlRes value in the () => part. But outside this part the value is null – Slater Apr 12 '16 at 15:25
  • the : () => console.log(urlRes) write n the console the right url But the : return urlRes; is equal to null – Slater Apr 12 '16 at 15:26
  • Yes, it's normal because it's outside the callback defined in the `subscribe` method... You should subscribe outside the service. – Thierry Templier Apr 12 '16 at 16:08
  • I edited again to show the new problem i got ... it's becoming boring xDD – Slater Apr 13 '16 at 09:19
0

Simple solution would be "getDownloadLink" to return intermediate link which will redirect user to actual link that you want.

var url = this._Service.getDownloadLink(name);
console.log(url);
window.location = url;

Note that in the actual download link, from server side you need to set proper content disposition headers.

  • That solution is not working. In my case that only make the page to refresh on the same location without launch any download – Slater Apr 12 '16 at 12:24