Node + Nuxt.js + Ant Design VueでTABLEを作るとき、それぞれの行の値を抽出する方法

NuxtのUIコンポーネントはいくつかあって、Reactからの移植版も多いのでカオスになっていますね。

以前はVuetifyをメインで使っていたのですが、機能がまだ不足気味です。

TABLEで検索機能や固定ヘッダを使いたい場合、今のところReactからの移植版Ant Design Vue一択ではないでしょうか。

例えばIDとNAMEというカラムがあるTABLEを作っていたとします。

NAMEのリンク先をIDを使って動的にしたい場合が多々あると思うのですが、どのUIコンポーネントもどうやってリンクをはればいいのかサッパリわからないです。

Vuetifyのときもかなりググってpropsというobjectの中に入っていることを突き止めました。

Ant Designの場合は結局見つからず、マニュアルを見ながら一つ一つ確かめて、keyというobjectに入ることがわかりました。

マニュアルの下の方に一応書いてあるのですが、まったく意味がわかりませんでしたよw

実際のコードの例です。
Ant Design Vueのcustomized-filter-panelを使ったとします。

<template>
  <a-table :dataSource="data" :columns="columns">
    <div
      slot="filterDropdown"
      slot-scope="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }"
      style="padding: 8px"
    >
      <a-input
        v-ant-ref="c => searchInput = c"
        :placeholder="`Search ${column.dataIndex}`"
        :value="selectedKeys[0]"
        @change="e => setSelectedKeys(e.target.value ? [e.target.value] : [])"
        @pressEnter="() => handleSearch(selectedKeys, confirm)"
        style="width: 188px; margin-bottom: 8px; display: block;"
      />
      <a-button
        type="primary"
        @click="() => handleSearch(selectedKeys, confirm)"
        icon="search"
        size="small"
        style="width: 90px; margin-right: 8px"
      >Search</a-button>
      <a-button @click="() => handleReset(clearFilters)" size="small" style="width: 90px">Reset</a-button>
    </div>
    <a-icon
      slot="filterIcon"
      slot-scope="filtered"
      type="search"
      :style="{ color: filtered ? '#108ee9' : undefined }"
    />
    <!-- ここに設定を追加 -->
    <template slot="customRender" slot-scope="text,key">
      <span v-if="searchText">
        <!-- ここにリンク設定を追加 -->
        <nuxt-link :to="`/hoge/`+key.key">
          <template
            v-for="(fragment, i) in text.toString().split(new RegExp(`(?<=${searchText})|(?=${searchText})`, 'i'))"
          >
            <mark
              v-if="fragment.toLowerCase() === searchText.toLowerCase()"
              :key="i"
              class="highlight"
            >{{fragment}}</mark>
            <template v-else>{{fragment}}</template>
          </template>
        </nuxt-link>
      </span>
      <template v-else>
        <!-- ここにリンク設定を追加 -->
        <nuxt-link :to="`/hoge/`+key.key">{{text}}</nuxt-link>
      </template>
    </template>
  </a-table>
</template>

<script>
const data = [
  {
    key: "1",
    name: "John Brown",
    age: 32,
    address: "New York No. 1 Lake Park"
  },
  {
    key: "2",
    name: "Joe Black",
    age: 42,
    address: "London No. 1 Lake Park"
  },
  {
    key: "3",
    name: "Jim Green",
    age: 32,
    address: "Sidney No. 1 Lake Park"
  },
  {
    key: "4",
    name: "Jim Red",
    age: 32,
    address: "London No. 2 Lake Park"
  }
];

export default {
  data() {
    return {
      data,
      searchText: "",
      searchInput: null,
      columns: [
        {
          title: "Name",
          dataIndex: "name",
          key: "name",
          scopedSlots: {
            filterDropdown: "filterDropdown",
            filterIcon: "filterIcon",
            customRender: "customRender"
          },
          onFilter: (value, record) =>
            record.name
              .toString()
              .toLowerCase()
              .includes(value.toLowerCase()),
          onFilterDropdownVisibleChange: visible => {
            if (visible) {
              setTimeout(() => {
                this.searchInput.focus();
              }, 0);
            }
          }
        },
        {
          title: "Age",
          dataIndex: "age",
          key: "age",
          scopedSlots: {
            filterDropdown: "filterDropdown",
            filterIcon: "filterIcon",
            customRender: "customRender"
          },
          onFilter: (value, record) =>
            record.age
              .toString()
              .toLowerCase()
              .includes(value.toLowerCase()),
          onFilterDropdownVisibleChange: visible => {
            if (visible) {
              setTimeout(() => {
                this.searchInput.focus();
              });
            }
          }
        },
        {
          title: "Address",
          dataIndex: "address",
          key: "address",
          scopedSlots: {
            filterDropdown: "filterDropdown",
            filterIcon: "filterIcon",
            customRender: "customRender"
          },
          onFilter: (value, record) =>
            record.address
              .toString()
              .toLowerCase()
              .includes(value.toLowerCase()),
          onFilterDropdownVisibleChange: visible => {
            if (visible) {
              setTimeout(() => {
                this.searchInput.focus();
              });
            }
          }
        }
      ]
    };
  },
  methods: {
    handleSearch(selectedKeys, confirm) {
      confirm();
      this.searchText = selectedKeys[0];
    },

    handleReset(clearFilters) {
      clearFilters();
      this.searchText = "";
    }
  }
};
</script>
<style scoped>
.highlight {
  background-color: rgb(255, 192, 105);
  padding: 0px;
}
</style>

<!– ここにリンク設定を追加 –>と書いてあるところにリンクを、slot-scopeにkeyを追加しています。

slot=”customRender”とセットになっているslot-scopeにtextとkeyを追加すると、該当のtemplateで値を取り出せるようになるみたいです。

ここらへんはAnt Designの仕様みたいで、イマイチまだよくわかっていません。

Nuxt.js:localhostとproductionでMySQLの設定を変える方法

この記事の続きになります。
Node.js: Nuxt.jsの設定でローカル、本番環境に分ける方法

Nuxt.jsでフロントを構築し、バックエンドとなるAPIをNuxtの中に入れてしまう場合の話になります。

DBへの接続先はローカルとサーバーで違うことがほとんではないかと思います。
ググってもイマイチな方法しかなかったので自分はいつもこのようにしています。

/server/index.js ←これはExpress
/server/api.js ←これがAPI本体
/server/config.js ←DBの設定ファイルを追加

package.jsonはデフォルトのまま変えていないという前提です。

/server/config.js

'use strict';

const mysql = (env) => {
  let conf;
  if (env == 'localhost' || env == 'development') {
    conf = {
      'host': 'localhost',
      'port': 3306,
      'user': 'root',
      'password': '',
      'database': 'hoge'
    };
  } else if (env == 'production') {
    conf = {
      'host': 'foo.ne.jp',
      'port': 3306,
      'user': 'admin',
      'password': 'hohohoho',
      'database': 'hoge'
    };
  }
  return conf;
};

module.exports = {
  mysql: mysql
}

/server/api.js

const mysql = require('mysql2/promise');
const config = require('./config.js'); // ←configファイル読み込み

router.get('/test', async (req, res, next) => {
  try {
    const conn = await mysql.createConnection(config.mysql(process.env.NODE_ENV)); // ←ココ
    const sql = `SELECT poo FROM pootaro`;
    const [result] = await conn.query(sql);
    await conn.end();
    res.send(result[0].poo); return next;
  } catch (err) { console.error(err) }
});

こうすると、NODE_ENVがlocalhostまたはdevelopmentの場合はlocalhostのDBサーバーを使用し、NODE_ENVがproductionの場合はfoo.ne.jpとなります。

つまりprocess.env.NODE_ENVの中に入っているってことですね。

余談ですが、Nuxt初体験の時によくわからなかったのですが、NODE_ENVはpackage.jsonで設定されています。

まず最初にnpm run devしろと言われhttp://localhost:3000/にアクセスさせられると思います。
run devした場合にはpackage.jsonの

  "scripts": {
    "dev": "cross-env NODE_ENV=development nodemon server/index.js --watch server"
  },

この部分で設定しています。
つまりその下の、

    "build": "nuxt build",
    "start": "cross-env NODE_ENV=production node server/index.js",

ココらへんが本番環境の設定になります。

 
MySQLだけではなくてmongoやlog4jsなど、ローカルとサーバーで使い分けたい時に使ってます。

Ant Design VueでデフォルトCSSを変更する方法

すぐ忘れるのでメモ。
vuetifyにちょっと問題があってAnt Design Vueを使うことになったのだけど、Nuxt.js最新版に対応しておらず、またしてもデフォルトCSSを変更できないという問題に遭遇。

その解決策はこちら。
https://github.com/vueComponent/ant-design-vue/issues/234#issuecomment-466308850

デフォルト
https://github.com/vueComponent/ant-design-vue/blob/master/components/style/themes/default.less

color
https://ant.design/docs/spec/colors

Nuxt.js: ExpressとaxiosでPOSTした時にreq.bodyがundefinedになる件

別件でpostした時普通にreq.bodyで取得できたと思ったのですが、今いじっていたらreqの中にbodyが見つからず、あれやこれやと調べていたら、body-parserというモジュールを別途インストールしなければいけないとのことでした。

工エエェェ(´д`)ェェエエ工! すっごいハマりポイントなので記事にしておきます。

Nuxt.jsの中にAPIも合体させて作るパターンの続きになります。
Nuxt.js使用開始にあたっての設定もろもろ−APIを追加

まずnpm install body-parserするのですが、コロコロ変わるので必ずオフィシャルのマニュアルを一読することを強くおすすめします。

ファイルは /server/api.js で以下を追加するだけです。

const bodyParser = require('body-parser');
const jsonParser = bodyParser.json();

router.post('/post', jsonParser, (req, res, next) => {
    console.log(req.body);
});

たったこれだけでreq.bodyに値が入ってきました。

あと、よくやるのが、index.vueなどでリクエストを投げるときに、投げるデータをJSONにしていないこと。
デフォルトがJSONなので、JSONで送る場合はJSON.parseしておく必要があります。

Nuxt.js + Vuetify デフォルトフォント変更の仕方

以下の記事はVuetify バージョン1の話なのでご注意を。
バージョン2ではstylusではなくてSASSを使っているのでまったく方法が異なります。続きは一番下で。

 
オフィシャルの説明ではさっぱりわからなかったのでメモです。

純粋なNuxtではassetsの中にcssファイルを作って、nuxt.config.jsで読み込めばスタイルシートが使えるはずなのですが、困ったことにVuetifyはデフォルトでstylusを使うのだそうで反応しません。

stylusって何だよと調べてみるとSassと同じようなスタイルシートの記法だそうで、$ほにゃららで上書き設定を入れなければいけないのだそうです。

面倒ですね〜。
ちなみにVuetify2.0でSASSに移行するそうです。

デフォルト値はこちら。
https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/stylus/settings/_variables.styl
※Vuetifyのバージョンが2に上がったのでリンク切れしてます。

でもどこにどのファイルを置いて、何を書けばいいのかオフィシャルの解説ではさっぱりでした。

まずassetsディレクトリに「stylus」というフォルダを作ります。
その中に「main.styl」というファイルを作り以下のようにコードを書きます。

/assets/stylus/main.styl

// main.styl
$body-font-family = 'ヒラギノ丸ゴ Pro W4', 'ヒラギノ丸ゴ Pro', 'Hiragino Maru Gothic Pro', 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', 'HG丸ゴシックM-PRO', 'HGMaruGothicMPRO' !important;

@require '~vuetify/src/stylus/main.styl'

nuxt.config.jsのcssのところに「~/assets/stylus/main.styl」を追加します。

  css: [
    '~/assets/style/app.styl', '~/assets/stylus/main.styl'
  ],

こんな感じ。
とりあえずこれでデフォルトフォントは変更できました。

 
【追記 2019/08/30】
Vuetifyの最新のアップデートが9時間前になってました。
これを書いている間にもいじってるみたいです。

バージョンが2に上がってNuxtのほうも対応したようです。
一番上にも書きましたが、stylusからSASSに変わってデフォルトフォントの変更の仕方も変わりました。

ここにあるとおりにすれば簡単に変更できます。
ただしデフォルトではrun buildしてNODE_ENVがproductionの時しか効かないです。

run devのときにも反映させたかったのですが、現時点ではちょっと無理と判断し、デフォルトフォントを変えるのではなくてdefault.vueのdivタグでstyleを指定することにしました。

こんな感じです。
/layouts/default.vue

<template>
  <v-app>
    <div id="fontSetting">
      <nuxt />
      <div>テスト中</div>
      <v-footer>
        <v-icon size="16">mdi-copyright</v-icon>
        {{ new Date().getFullYear() }} hogehage Inc.
      </v-footer>
    </div>
  </v-app>
</template>

<script>
export default {};
</script>

<style>
#fontSetting {
  font-family: "ヒラギノ丸ゴ Pro W4", "ヒラギノ丸ゴ Pro",
    "Hiragino Maru Gothic Pro", "ヒラギノ角ゴ Pro W3",
    "Hiragino Kaku Gothic Pro", "HG丸ゴシックM-PRO", "HGMaruGothicMPRO";
  font-size: 50px;
}
</style>

これで一応フォントは変えられます。