メンチカツ

ロースカツが好きです

【メモ】chromeアップデートしたら Unchecked runtime.lastError: The message port closed before a response was received.

Vue.js 開発環境で、chromeアップデートしたら

Unchecked runtime.lastError: The message port closed before a response was received.

がコンソールに出るようになった。

f:id:easy-breezy:20190807114217p:plain

ぐぐる拡張機能のどれかが影響して出ているらしい。

でも、環境によって犯人が異なるみたい。

なので、インストール済みの拡張機能を少しずつオンオフしていくと、私の環境では、犯人は

 SkyChromeExtension

だった。

f:id:easy-breezy:20190807114219p:plain

速攻オフって平安を取り戻した。

Vue Test Utilsでコンポーネントのdata()を評価したい

なぜかうまくいかず時間がかかったのでメモ

f:id:easy-breezy:20190801171844p:plain

こういうページネーションのコンポーネントのテストを書くとき、

コンポーネントのpropsには以下のような値(現在のページ,ページあたりの件数,トータル件数)を設定できるとして

<Pagination :config="{ current: 1, limit: 20, total: 100 }" @movePage="load" />

propsに値を設定したことを受け、watch に定義したメソッドで、data() の最終ページ lastPage の値を算出したい。

watch: {
  config: function(payload) {
    // ここではわかりやすく「5ページ >>」とか変数にいれてる
    this.lastPage = Math.ceil(payload.total / payload.limit) + 'ページ >>' // 上記例の設定値だと「5ページ >>」になる
  }
}

この「5ページ >>」を検査したかったのだけど、テストコードで propsData をセットしてもなぜかwatchが発火しない。

<template>
  ...
  <a
    class="pagination-link last-page"
    :disabled="currentPage === lastPage"
    aria-label="Goto last page"
    @click="moveLast"
  >
    {{ lastPage }} <!-- 「5ページ >>」 -->
  </a>
  ...
</template>
describe('components/Pagination.vue', () => {
  let wrapper
  describe('template', () => {
    beforeEach(() => {
      wrapper = mount(Pagination, {
        propsData: { config: { nextPage: 2, limit: 20, total: 100 } } // propsにセットしたのだからwatch発動してほしい!
      })
    })
    test('最終ページは5ページ目であること', () => {
      const page = wrapper.find('.last-page')
      expect(page.text()).toBe('5ページ >>') // 失敗。got:null
    })
  })
})

props を2回セットしたら思った通り動くようになった。

describe('components/Pagination.vue', () => {
  let wrapper
  describe('template', () => {
    beforeEach(() => {
      wrapper = mount(Pagination, {
        propsData: { config: { nextPage: 2, limit: 20, total: 100 } }
      })
    })
    test('最終ページは5ページ目であること', () => {
      wrapper.setProps({ config: { nextPage: 2, limit: 20, total: 100 } }) // これを足した。
      const page = wrapper.find('.last-page')
      expect(page.text()).toBe('5ページ >>') // 成功した!
    })
  })
})

2度setPropsしていてなんかモヤるけど、やりたいことはできた。

【メモ】Nuxt.js/BulmaのNavbarでdropdownが残ってしまう(:focus-withinが効きっぱなし)

よくあるSPAのドロップダウン。

f:id:easy-breezy:20190712172643p:plain
ドロップダウンだ

<n-link class="navbar-link" to="/members">
  <span class="icon is-medium">
    <i class="fas fa-lg fa-user"></i>
  </span>
  <span>メンバー管理</span>
</n-link>
<div class="navbar-dropdown">
  <n-link class="navbar-item" to="/members">メンバー一覧</n-link>
  <n-link class="navbar-item" to="/members/new">メンバー登録</n-link>
</div>

これ、なぜか n-link click後にフォーカス状態が残ってしまい、閉じてくれない。

これは @click.native イベントに一行追加することで、閉じてくれるようになりました(手元のChromeFirefoxで確認)。

<n-link class="navbar-link" to="/members" @click.native="hideMenu">
  <span class="icon is-medium">
    <i class="fas fa-lg fa-user"></i>
  </span>
  <span>メンバー管理</span>
</n-link>
<div class="navbar-dropdown">
  <n-link class="navbar-item" to="/members" @click.native="hideMenu">メンバー一覧</n-link>
  <n-link class="navbar-item" to="/members/new" @click.native="hideMenu">メンバー登録</n-link>
</div>

んで、このメソッドを追加。

<script>
export default {
  methods: {
    hideMenu(e) {
      e.target.blur()
    }
  }
}
</script>

以上です。

この一行を追加するまで、謎にえらい時間を費やしてしまったので、メモとして残します。

【メモ】Vueコンポーネントのwatchがwatchしない

こういうやつ

// NG
  watch: {
    config: (val, oldVal) => {
      console.log(val)
      console.log(oldVal)
    }
  }

functionにする必要があります。

// OK
  watch: {
    config: function(val, oldVal) { // functionにする
      console.log(val)
      console.log(oldVal)
    }
  }

ウォッチャを定義するためにアロー関数を使用すべきではない

と、ドキュメントにちゃんと、しかも太字で書いてありました。。サーセン

jp.vuejs.org

【メモ】Vueコンポーネントで再帰的に$emitしたいときの引数

Vueコンポーネントの中で自身のコンポーネントを呼び出してツリー表示をしたいときがあると思います。

今回やりたかったのは以下のこと。


1) 初期表示で最上位の階層(親のいない階層)を表示

2) 子要素があれば hasChild 属性が true。「+」ボタンを表示する。

f:id:easy-breezy:20190702155637p:plain

3)「+」ボタンを押したら、 loadChildren メソッドを $emit 。自身の children 属性に子要素リストを読み込んで表示する。

f:id:easy-breezy:20190702155641p:plain

4) 2-4を繰り返す。


というもの。再帰のところで $emit の引数に何を渡すんだろう?とひるみましたが、 $event を渡せばよいんだそうです。

<!-- Tree.vue -->
<template>
  <div class="tree">
    <ul v-for="item in items" :key="item.id">
      <li>
        {{ item.name }}
        <Tree
          v-if="item.children !== null" // 子要素があるときだけ機能する
          :items="item.children"
          @loadChildren="$emit('loadChildren', $event)"  // 引数に $event を指定する
        />
        <a v-else-if="item.hasChild" @click="$emit('loadChildren', item)">  // 引数に item を指定する
          +
        </a>
      </li>
    </ul>
  </div>
</template>

ここに例がありました。

vuejs.org

【メモ】Nuxt.js で「Uncaught Error: [nuxt] store/index.js should export a method that returns a Vuex instance.」

storeの中に new Vuex.Store を返す index.js を作成したらエラー

ファイル名を store/index.js から store/store.js に変えたら直った。

store/index.js はNuxtの中では特別な意味があるファイルなのか。

ja.nuxtjs.org

【メモ】openapi-generatorで たくさん出るエラーを無かったことにする

その場しのぎメモ

swagger.json から openapi-generator generate でエラーがたくさん出てしまう、おろかなわたしの swagger.json 。。。

openapi-generator generate -i http://localhost:3000/docs/swagger.json -o static/api -g html

…
Errors: #こういうエラーがモリモリ出てgenerateされない。pathパラメータの定義を$refでしてるからなのかなーわからん
        -attribute paths.'/api/users/{id}'. Declared path parameter id needs to be defined as a path parameter in path or operation level

コンソールのメッセージにあるのですが、 --skip-validate-spec オプションをつけるとエラーを無視してgenerateしてくれます。

openapi-generator generate --skip-validate-spec -i http://localhost:3000/docs/swagger.json -o static/api -g html

DSLから生成した swagger.json の構文エラーは直すのが大変なので、その場しのぎ大変助かります。