LoginSignup
1
0

More than 5 years have passed since last update.

オリジナルKdB(科目検索)を作ってみよう -5- Express.jsでWEBアプリ整形編

Last updated at Posted at 2019-02-23

これまでの記事

前回の要約

  • Node.jsの定番WebフレームワークのExpress.jsで遊んだ
  • Hello,World と Routing をした

スクリーンショット 2019-02-22 16.11.47.png

この記事を読む前に

この記事はシリーズものです。前回の記事を読んである前提で話を進めるので注意してください。
また、ある特定の人にしかわからない単語が出現する可能性が高いです。あらかじめご了承ください。

この記事での開発環境

  • MacOS 10.14.3
  • Visual Studio Code 1.31.1
  • Node.js LTS 10.15.1

今回やること

  • パースした情報をExpress.jsで処理する
  • Express.jsと相性のいいテンプレートエンジンPugを書く

キーワード モジュール化, テンプレートエンジン, Pug

データを流し込もう

それでは、Express.jsについてある程度理解したので、パースしてきた情報をExpress.jsで処理しましょう!まずはindex.jsを使いやすいようにモジュール化します。index.jsを以下のように丸っと書き換えてください

index.js
const fs = require('fs');
const parse = require('csv').parse;
const iconv = require('iconv-lite');

// モジュールの関数を作ります
const myParser = ( filename, decoding ) => {

    // パースしたデータを入れる配列
    const dataArray = []; 

    // ①
    const parser = parse( (err, data) => {
        data.forEach( (element, index) => {
            dataArray.push(element)
        });
    });

    // ファイルストリームから読み込み → デコード → パース
    fs.createReadStream( __dirname + '/' + filename )
        .pipe( iconv.decodeStream(decoding) )
        .pipe( parser );

    // パースした結果
    return dataArray;
}

// ②
module.exports = myParser;

これでパース処理をindex.js以外でも使えるようになりました! 解説していきます。

① パース情報を配列を作成

const parser = parse( (err, data) => {
    data.forEach( (element, index) => {
        dataArray.push(element)
    });
});

これまではdataArray.push()の部分がconsole.log()でした。これは最終的に配列を返したいのでこのような記述になります。push()は配列の破壊的な変更になるので、気になる方はconcat()を使って副作用のないコードに変更することをオススメします。

② モジュール化!

module.exports = myParser;

ここでは、index.jsモジュール化をしています。モジュール化すると、他のjavascriptファイルから自由に呼び出すことができるので一気に便利になり、コードの見通し、メンテナンス性が良くなります。今回はmyParser()のみをモジュールに含めていますが、もちろん複数選択可能です。その場合は以下のようになります。

module.exports = { 
    hoge: myFunction1,
    fuga: myFunction2,
    nyan: myfunction3
}

使用する側はrequire文を使うことでモジュール化された関数を使うことができます。app.jsを変更して確認しましょう

app.js
const app = require("express")(); // ショートハンドに書き直してます
const getParseData = require("./index"); // 作ったモジュールを読み込みます!

// index.jsの関数を使う!
const dataArray = getParseData( "kdb.csv", "shift_jis" );

// とりあえず全表示...
app.get( "/", (req, res) => {
    res.send(dataArray);
});

app.listen(3000); // ポート番号3000でサーバ起動!
console.log("server started!!");

実行します。

$ node app.js

さて... それではブラウザで http://localhost:3000/ にアクセスしてみましょう。

スクリーンショット 2019-02-23 18.19.47.png

キャァァァッ!!!! ヒョウジサレタァァァッ!!!!

圧倒的な文字列... でもちゃんと表示されているので成功です!ちゃんとパースしたものがブラウザで表示されていますね!整理すると

  • モジュール化に成功
  • パース成功
  • Express.jsでRouting成功

という、なんやかんやでめんどくさい部分は終わりました!それでは、これをHTMLに整形していきましょう。

テンプレートエンジンに任せよう

Express.jsのres.send()に直接HTMLを書いてもいいのですが、絶対にやめましょう。なぜかというと、フロントエンド(View)部分の自由度を下げてしまったり、意図しない挙動が発生してしまったり、表示バグを招いてしまったりといいことが一つもないからです。なので、基本的には必要な情報のみ or JSON などでレスポンスすることを心がけます。

app.get( "/", (req, res) => {
    res.send(dataArray);
});

今回は配列をレスポンスで返したので、この情報をHTMLで処理していきます。「...ってどうやって?」となりますが、Express.jsにはデータをいい感じにHTMLに流し込んでくれる「テンプレートエンジン」という機能があります!これまたいくつかありますが、今回はその中のPugを使っていきます

書き方は以下の記事に詳しく書いてあります

それでは、Pugをインストールしましょう。

$ npm install pug

app.jsを編集します。

app.js
const app = require("express")();
const getParseData = require("./index");

const dataArray = getParseData( "kdb.csv", "shift_jis" );

// テンプレートエンジンをセット!
app.set("view engine", "pug");

// 後で解説します
app.get( "/", (req, res) => {
    res.render("html.pug", { 
            state: dataArray,
            word: "全検索"
    });
});

app.listen(3000); // ポート番号3000でサーバ起動!
console.log("server started!!");

ちょいとわかりづらいですが、とりあえずpugのファイルを作りましょう(テンプレートエンジンでは/views/ディレクトリにテンプレートファイルを置く必要があります。)

views/html.pug
- const values = state

doctype html
html
    head
        title 検索 : #{word}
        meta(charset="UTF-8")
    body
        h1 開設科目一覧
        ul
            each value in values
                li #{value[0]} : #{value[1]}

ぱっと見全然わかりませんね笑。でもPugの記述ルールは難しくないので、こちらの記事を軽く読めばわかります。超意訳すると、PythonみたいにインデントでHTMLのDOM構造を書き表したものです。記述の中にプログラムちっくな処理もかける(- const values = stateeach value in valuesなど)優れモノです。

では実行してみましょう。

$ node app.js

スクリーンショット 2019-02-23 18.49.39.png

解説

綺麗に表示できましたね!流石に全件となると同期処理では重いですが、無事表示できました!それでは解説です。

app.set("view engine", "pug");

最初にテンプレートエンジンとしてPugを使うことを明記します。Pugの他にもejsなどが使えますが、個人的にはPugの方がコードの見通しが良いため好きです。テンプレートエンジンの使用を宣言したら次です。

app.get( "/", (req, res) => {
    res.render("html.pug", { 
            state: dataArray,
            word: "全検索"
    });
});

これまで使ってきたres.send()ではなく、res.render()を使っています。res.render()では、第1引数にテンプレートファイルの名前、第2引数にテンプレートファイルに渡したいデータを、それぞれ記述します。今回の場合はhtml.pugに以下のデータ(オブジェクト)を渡しました。

{
    state: dataArray,
    word: "全探索"
}

一方このデータをもらったhtml.pugではどのようにデータが処理されるかをみてみます。

views/html.pug
- const values = state

doctype html
html
    head
        title 検索 : #{word}
        meta(charset="UTF-8")
    body
        h1 開設科目一覧
        ul

            each value in values
                li #{value[0]} : #{value[1]}

一行目- const values = stateでExpress.jsからもらったstate( ... すなわちdataArray)をvaluesに格納しています。変数(Express.jsからもらったデータでも、Pug内で定義した変数でも)をHTMLに流し込むときは#{変数名}と記述します。

each value in valuesvaluesの中身一つ一つに処理をするのでstate(dataArray)の各要素の0番目と1番目をひたすら列挙してくれます!だから、科目番号と科目番号名だけが表示されているということですね

スクリーンショット 2019-02-23 18.49.39.png

なんとなくわかりましたか?
(例えたくても、例えが思いつかないのでそのまま説明するしかないのです...)
とりあえずわかったね!先に進もう!

簡易的な検索機能を作ろう

前回のRoutingの手法を活かして、検索機能を作っていきます。それではapp.jsを編集しましょう。

app.js
const app = require("express")();
const getParseData = require("./index");

const dataArray = getParseData( "kdb.csv", "shift_jis" );

app.set("view engine", "pug");

app.get( "/", (req, res) => {
    res.render("html.pug", {state: dataArray ,word: "全検索"});
});

//  http://localhost:3000/〇〇 で検索できるように!
app.get( "/:word", (req, res) => {
    const state = [];
    const matchReg = new RegExp(req.params.word); // 正規表現の準備

    dataArray.filter( (data) => {
        return data[1].match(matchReg) // 科目名で検索
    }).forEach( (element, index) => {
        state.push(element);  // マッチしたものを格納
    });

    res.render("html.pug", {
        state: state,
        word: req.params.word
    });
});

app.listen(3000);
console.log("server started!!");

しれーっとfilterRegExp()、正規表現を使ってますが、javascriptで標準搭載の便利なものなのでどんどん使っていきます。詳しくは以下の記事を読みましょう。

実行します

$ node app.js

これで簡易的な検索機能を実装しました。それでは http://localhost:3000/コンテンツ などで検索してみましょう。

スクリーンショット 2019-02-23 19.30.53.png

検索できたよ!やったね!

科目名に「コンテンツ」が含まれる科目だけが出力されました!(簡易的ではありますが)検索機能の完成です!やったね!

もちろん、すべてのユーザにURLを直打ちしてもらうわけにはいかないので、フォームなどを作って検索させると利便性が向上すると思います。今回はパパッと作るために省略しましたが、余裕があったら是非実装してみてください!

もう少し見栄えを良くしたい場合はCSSを書いて、俺専用ッ!なものを作ってみると面白いです。

次回はいよいよこのWEBアプリを公開していきます。簡単にWEBアプリを公開できるherokuを使っていきましょう!乞うご期待!

今回のあとがき

Express.jsは軽量WEBフレームワークで、大きな人気を博しています。というのも、機能を最小限に絞り、軽量で動作が早く、すぐに導入できるからだと思います。特に、今回はhtml.pug(View)にindex.js(Model)がパースした情報app.js(Controller)で処理しています。今回のような設計モデルをMVCモデルと言います。MVCとは Model View Controller の頭文字です。

  • Model ... Controllerの命令に基づいて実際にデータを処理する部分
  • View ... 実際にユーザが見ている部分。ユーザのアクションに基づいてControllerに処理をお願いする。
  • Controller ... Viewからの命令をもとにModelに処理を命令したり、Modelの情報をViewに渡したりする。

例えば、ユーザ認証ページで考えてみるとわかりやすいです。

View ユーザがID,Passを入力。
View Viewはその情報をControllerに通知。
Controller ID,Passが正しいかをModelに確認するよう命令
Model 受け取ったIDをもとに該当ユーザを検索。該当ユーザをControllerへ通知
Controller Modelからもらったユーザ情報と入力されたID,Passを検証。結果をViewへ通知。
view Controllerからの情報を表示

といった感じです。もちろんこれは100%は正解ではありません。「ModelはControllerからの命令をもとに直接Viewに情報を渡す」とか「Controllerがユーザの行動を監視。アクションに応じてModelに情報を用意させてViewに取りにいかせる」とかとか。「なんやわけわからん難しい」ですがこれらも立派なMVCモデルです。

分業させることでそれぞれの処理に集中させることが目的であり、プロジェクトが大きくなっても複雑になりすぎることがなく理解しやすいのが利点です。フルスタックWebフレームワークの Ruby on Rails ではMVCの設計思想で進むことが基本です。ですが、Viewに関する自由度が幾らか下がってしまうのが難点だったりします。

Express.jsの最小構成ではMVCで言うところのController機能のみを提供します(もちろん最小構成で使うことはないのでModelもつけます)。その高い汎用性・カスタマイズ性が大変魅力的でした。ささっと何かを作りたい場合にExpress.jsは便利です。きっとこれからも人気を博していくことでしょう。

一方、フロントエンドのjavascriptでは有名なフレームワークが3つあります。Angular.js, Vue.js, React.js です。どれも非常に高機能でありますが、それぞれの得意とする分野はかなり違います。直感的でわかりやすく、レファレンスが充実しているVue.jsは人気です。フルスタックで強固なAngular.jsは大きなプロジェクトでは頼もしいですし、React.jsのコンポーネントモデルに特化した姿勢、再利用性の高さは魅力的です。

バックエンドとフロントエンドどちらをメインにしようか迷っている人間がここに一人。
私は一体どこへ向かっていくんですかね〜

ではでは

次の記事 → オリジナルKdB(科目検索)を作ってみよう -6- 本番環境Herokuにデプロイ編

参考サイト

1
0
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
0