LoginSignup
1
3

More than 3 years have passed since last update.

vte.cxとReactでPDF出力機能の実装

Last updated at Posted at 2019-10-30

概要

以前作成したアプリの一覧画面をPDFで出力する機能を実装しました。
vte.cxについて詳しく知りたい方はvte.cxのドキュメントをご覧ください。

今回実装したPDF出力機能の仕様は以下になります。

  • 一覧を表示した状態で「PDF出力」ボタンを押すとPDFをダウンロードする
  • 一覧画面に表示されているデータだけをダウンロードする

実際にダウンロードしてみるとこんな感じになります。
スクリーンショット 2019-10-30 12.39.03.png

PDF出力機能を実装するには、サーバーサイドで実行されるサーバーサイドJavaScriptについて知る必要があります。
詳しくはこちらの記事を参考にしてください。
https://qiita.com/stakezaki/items/d596afd085988d76983c

実装

今回編集したファイルは一覧画面(ListProf)と新しく作ったPDF出力ファイル(ssr.pdf)になります。

では、PDF出力ファイルから見ていきます。
まず、/src/server/の配下にssr.pdf.tsxファイルを作ってください。
実際のファイルが以下になります。

PDF出力ファイル(ssr.pdf)

ssr.pdf
import * as vtecxapi from 'vtecxapi'
import * as React from 'react'
import * as ReactDOMServer from 'react-dom/server'
import * as pdfstyles from '../pdf/pdfstyles'

const PrintToPdf = () => {

  const feed = vtecxapi.getFeed('/foo')
  const currentPage = Number(vtecxapi.getQueryString('currentPage'))

  let sliceFirstNumber = 0
  let sliceLastNumber = 5

  if(currentPage !== 1) {
    sliceFirstNumber = currentPage * 5 - 5
    sliceLastNumber = currentPage * 5

  }

  const showFeed = feed.slice(sliceFirstNumber, sliceLastNumber)

  return (
     <html>
       <body>
         <div className="_page" style={pdfstyles._page}>
         {feed.length > 0 &&
           <table style={pdfstyles._table}>
             <tr>
               <th style={pdfstyles._th}>名前</th>
               <th style={pdfstyles._th}>メール</th>
               <th style={pdfstyles._th}>職業</th>
               <th style={pdfstyles._th}>住所</th>
               <th style={pdfstyles._th}>身長</th>
               <th style={pdfstyles._th}>誕生日</th>
               <th style={pdfstyles._th}>性別</th>
               <th style={pdfstyles._th}>チェック</th>
               <th style={pdfstyles._th}>セレクト</th>
               <th style={pdfstyles._th}>メモ</th>
             </tr> 

             {showFeed.map((entry) => (
               <tr>
                 <td style={pdfstyles._td}>{entry.user!.name}</td>
                 <td style={pdfstyles._td}>{entry.user!.email}</td>
                 <td style={pdfstyles._td}>{entry.user!.job}</td>
                 <td style={pdfstyles._td}>{entry.user!.address}</td>
                 <td style={pdfstyles._td}>{entry.user!.height}</td>
                 <td style={pdfstyles._td}>{entry.user!.birthday}</td>
                 <td style={pdfstyles._td}>{entry.user!.gender}</td>
                 <td style={pdfstyles._td}>{entry.user!.check}</td>
                 <td style={pdfstyles._td}>{entry.user!.select}</td>
                 <td style={pdfstyles._td}>{entry.user!.memo}</td>
               </tr>
             ))}

           </table>
         }
         </div>
       </body>
     </html>
    )
  }

const html = ReactDOMServer.renderToStaticMarkup(PrintToPdf())

// PDF出力
vtecxapi.toPdf(1, html, 'ListProf.pdf')

まずは、PDFファイルに表示させるデータを取得します。

const feed = vtecxapi.getFeed('/foo')

上記のように、vtecxapi.getFeed()を使うことで、これまで/d/fooに対して行ってきたGETリクエストをサーバサイドJSから行うことができます。この場合、/foo/dはつけなくて大丈夫です。
これを以下のコマンドでデプロイしてください。

npm run watch:server -- --env.entry=/server/ssr.pdf.tsx

データが取得できたので、実際に表示させてみましょう。

 return (
     <html>
       <body>
         <div className="_page" style={pdfstyles._page}>
         {feed.length > 0 &&
           <table style={pdfstyles._table}>
             <tr>
               <th style={pdfstyles._th}>名前</th>
               <th style={pdfstyles._th}>メール</th>
               <th style={pdfstyles._th}>職業</th>
               <th style={pdfstyles._th}>住所</th>
               <th style={pdfstyles._th}>身長</th>
               <th style={pdfstyles._th}>誕生日</th>
               <th style={pdfstyles._th}>性別</th>
               <th style={pdfstyles._th}>チェック</th>
               <th style={pdfstyles._th}>セレクト</th>
               <th style={pdfstyles._th}>メモ</th>
             </tr> 

             {showFeed.map((entry) => (
               <tr>
                 <td style={pdfstyles._td}>{entry.user!.name}</td>
                 <td style={pdfstyles._td}>{entry.user!.email}</td>
                 <td style={pdfstyles._td}>{entry.user!.job}</td>
                 <td style={pdfstyles._td}>{entry.user!.address}</td>
                 <td style={pdfstyles._td}>{entry.user!.height}</td>
                 <td style={pdfstyles._td}>{entry.user!.birthday}</td>
                 <td style={pdfstyles._td}>{entry.user!.gender}</td>
                 <td style={pdfstyles._td}>{entry.user!.check}</td>
                 <td style={pdfstyles._td}>{entry.user!.select}</td>
                 <td style={pdfstyles._td}>{entry.user!.memo}</td>
               </tr>
             ))}

           </table>
         }
         </div>
       </body>
     </html>
    )

PDF出力ファイルはJSXで書けますが、ページ構造が決まっています。詳しくはvte.cxのドキュメントのPDFスタイルシートにページ構造が記載されているので、そちらをご覧ください。
データを表示させるテーブルタグの中身は一覧画面とほとんど一緒です。

const html = ReactDOMServer.renderToStaticMarkup(PrintToPdf())

// PDF出力
vtecxapi.toPdf(1, html, 'ListProf.pdf')

ReactDOMServer.renderToStaticMarkup({表示させたいコンポーネント名})とし、
vtecxapi.toPdf(ページ数,生成元のhtml,PDFのファイル名)の生成元のhtmlに指定することで、指定したパラメータを元にPDFを生成します。

これで、ブラウザからhttp://{サービス名}/s/ssr.pdfを開くとPDFファイルがダウンロードされます。
スクリーンショット 2019-10-30 13.38.17.png
しかし、これだと一覧画面に表示されているデータだけではなく、画像のように全てのデータが表示されてしまうので、表示するデータを指定する必要があります。

const currentPage = Number(vtecxapi.getQueryString('currentPage'))

変数currentPageに一覧画面のページ数を格納します。
getQueryString(取得したいパラメータ)とすることで、URLパラメータを取得することができます。
後ほど説明しますが、一覧画面でPDF出力ボタンを押下した際に、ページ遷移と同時に変数 currentPageの値を同時に送ることで、ページ数を取得します。

  let sliceFirstNumber = 0
  let sliceLastNumber = 5

  if(currentPage !== 1) {
    sliceFirstNumber = currentPage * 5 - 5
    sliceLastNumber = currentPage * 5

  }
 const showFeed = feed.slice(sliceFirstNumber, sliceLastNumber)

表示するデータを指定するためには、sliceを使います。そのために、currentPageを使い、最初と最後の値を指定して抜き出します。

最後に、一覧画面にPDF出力ボタンを配置します。
今回はボタンタグを追加しただけなので、JSXのボタンを配置したところだけ載せておきます。

一覧画面(ListProf)

ListProf

       return ( 
          <button type="button" onClick={() => location.href="http://vtecx_sample_app.vte.cx/s/ssr.pdf?currentPage="+currentPage}>PDF出力</button>

onClickイベントにlocation.frefでURLを指定することで、ボタン押下時にPDF出力ファイルに遷移し、PDFをダウンロードできます。
また、最後にURLの最後に'?currentPage='+currentPageを追加することで、現在の一覧画面のページ数を一緒に受け渡すことができます。

注意!
一覧画面にボタンを配置した際に、npm run watch:server -- --env.entry=/server/ssr.pdf.tsxのままになっていると一覧画面を保存しても変更が反映されないので、必ずnpm run watch:indexで反映させてください。

これで、実際にボタンを押下してみてください。
スクリーンショット 2019-10-30 12.39.03.png
こんな感じで、一覧画面と同じデータが表示されたら成功です。

お疲れ様です。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3