Pages

Wednesday, February 15, 2017

Download a PDF in Angular 2

If you need to download a PDF file (stream) from your web service, there are a couple of key things to do.

Make sure you use the responseType property of the RequestOptions object (I'm using TypeScript here). You need to set this property to ResponseContentType.Blob (an enum from @angular/http)

Code snippet


// Depending on what you are sending to the server
// and what the server is sending back
let headers = new Headers({ 
   'Content-Type': 'application/json', 
   'Accept', 'application/pdf'
});


let options = new RequestOptions({ headers: headers });
// Ensure you set the responseType to Blob.
options.responseType = ResponseContentType.Blob;


Then, we will use the new Blob object in HTML5 to handle the response. To do this, create a new Blob using the response.blob() function in your map() function.

this.http
   .post(url, body, options)
   .map(response) => {
      // Removed checking of valid response
      ...
      let fileBlob = response.blob();
      let blob = new Blob([fileBlob], { 
         type: 'application/pdf' // must match the Accept type
      });
      ...
   })

Finally, use the saveAs() function of HTML5 to create the file on the client. Due to browser support, it is best to use the FileSaver JavaScript library (npm install file-saver, npm install @types/file-saver)

Don't forget to import it

...
import * as FileSaver from 'file-saver'; 
...

Then just use it after you have created the Blob.

...
let filename = 'mypdf.pdf';
FileSaver.saveAs(blob, filename);
...

Done.

Wednesday, January 18, 2017

Using RxJS Observable to return a simple value

So, I have a DataService that is using Observables (kinda the new Promises) to get a bunch of things, then when they are all ready it will get all the values and deal with them.
So functions that use the RxJS .post().map() etc will return Observable fine but I just wanted to return 10. As in the number 10. I don't really care if its Observable per se but I do want to be able to consume the function in the DataService as a promise. So how to you return a number 10? Here goes:

getNumber(): Observable {
   var observable = Observable.create((observer: Observer) => {
      observer.next(10);
      observer.complete();
   });
   return observable;
}

It can then be consumed, along with other promise-style functions and dealt with like promise.all()

Observable.forkJoin([
   this.dataService.getNumber(),
   this.dataService.getSomethingElse()
]).subscribe(results => {
   this.number = results[0];
   this.somethingElse = results[1];
});

Using the subscribe is like using promise.all()

Feels a bit weird at first, but basically if you want to return a promise of a simple type, just create a function like the getNumber() function above.

Wednesday, May 27, 2015

Filtering SPD 2013 Workflow REST calls by Date

I recently spent way too much time trying to figure out why my REST call was failing in a SPD 2013 Workflow when I was filtering on a DateTime field.

All the articles I found relating to filtering by a DateTime seemed to hard-code a date. I.e. ...$filter=SomeDateField eq DateTime'2015-05-27T00:00:00Z'... which is fine and works but if you are filtering using DateTime variable (because you are applying some sort of date transformation) then it is difficult to find anything about how to do this.

Turns out when you add your filter date variable to the above example, you need to change the 'Return field as:' setting from "String" to "ISO Formatted".

So the above example would look something like:
...$filter=SomeDateField eq DateTime'[%Variable:FilterDate%]'... and when you add that variable you select to return the contents as ISO Formatted.

NOTE: Just incase, you cannot just type in the bit of code in the [%..%] section, you need to use the "Add or Change Lookup" button in the String Builder dialog.

Hope this saves someone else some time.

Sunday, March 2, 2014

SharePoint 2013 Tasks LVWP bug with connections framework

OK, previously I posted about having issues with connecting web parts in 2013 without using the 'Server Render' option. I now know its a little more specific.

I've finally had a bug registered for this by Microsoft. It took nearly 4 months mind you.

The issue is in fact only Task-based List Templates that cannot connect with other web parts when using client side rendering. It seems that with the new features built into the CSR for the Tasks lists, they forgot to add the code to make the connections framework work. I'm hoping its a simple case of adding some JavaScript to get it to work. I haven't had the time to fish through the csr code to see if this is the case.

Tuesday, September 24, 2013

ddwrt:FormatDate bug - still in SP2013

OK, firstly, if you didn't know, SP2010 had a bug in the ddwrt:FormatDate function in XSLT where it assumes that you always use Locale: English (US). So if you live in any other part of the world you can't use this function.

Well, thanks to a bunch of other people and their posts, here, here, and kinda here, there is a way around this using even more XSLT.

So for me, I was calculating differences between dates using strings (yeah I know, probably not the best way but...) and because my Australian Dates were all over the place but I was always calculating them using yyyyMMdd I modified Elio's template for my own.

Hope this helps others, at least as a starting point. Oh yeah, and I hope one of these days MS get around to fixing the root cause!

<!-- ***************************************************************************** -->
<!-- This template fixes an issue where the US think they are the only ones on earth -->
<!-- NOTE: Returns date in yyyyMMdd -->
<!-- If you went to town implementing string manipulation templates to parse a date format out of a parameter you would -->
<!-- be able to return any formatted date, but I don't have the time at the moment -->
<xsl:template name="CustomFormatDate">
<xsl:param name="dateValue" />
<xsl:param name="monthFormat" />
<xsl:param name="locale" select="3081" />

<!-- Split Date -->
<xsl:variable name="day" select="substring-before($dateValue, '/')" />
<xsl:variable name="month" select="substring(substring-after($dateValue, '/'), 1, 2)" />
<xsl:variable name="year" select="substring(substring-after(substring-after($dateValue, '/'), '/'), 1, 4)" />

<!-- Create US Date Format -->
<xsl:variable name="USDate">
<xsl:value-of select="$month" />/<xsl:value-of select="$day" />/<xsl:value-of select="$year" />
</xsl:variable>

<!-- Month Notation -->
<xsl:variable name="monthString">
<xsl:choose>
<xsl:when test="$monthFormat='MM'">
<xsl:value-of select="$month" />
</xsl:when>
<xsl:when test="$monthFormat='MMM'">
<xsl:value-of select="ddwrt:FormatDateTime($USDate, $locale, 'MMM')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="ddwrt:FormatDateTime($USDate, $locale, 'MMMM')" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>

<xsl:variable name="twoDigitDay">
<xsl:choose>
<xsl:when test="string-length($day) = 1">0</xsl:when>
</xsl:choose>
<xsl:value-of select="$day" />
</xsl:variable>

<!-- Create Date -->
<xsl:value-of select="$year" /><xsl:value-of select="$monthString" /><xsl:value-of select="$twoDigitDay" />
</xsl:template>