はじめに
この記事はこちら↑の続きです。
冒頭のリンクのとおり、Elasticsearchをターゲットにした便利なSPAプラグインであるReactiveSearchを使ってみたのですが、ここではReactiveSearchにクエリを任せる標準方式ではなく、カスタムクエリ型で実現しました。
カスタムクエリ型の場合、Vue.js版のチュートリアルを流し読みしたのと私の英語読解力の範囲では、コンポーネントのプロパティ設定レベルでコンポーネント間で検索条件を共有するすることが難しいように見えました。 (ReactiveSearch自体のソースは読んでいない上の見解です。)
例えば、検索クエリはオリジナルなものにしたいが、加えて検索BOXを2つにしてそれぞれ別フィールドを検索する、また検索BOX1つめと2つめのAND(Elasticsearch クエリDSLで言うと、must)で検索したいというようなものをやるにはどうしたらというところです。
とは言え、Vue.jsに載っているアプリですので、このようなことも多少味付けすればできるハズなのは確実なのですが、念のため確かめてみました。
で、結論としては、Vue.jsのコンポーネント間の状態共有を使う方式一般で取り扱えそうです。
今回は、Vue.jsのステータス管理のプラグインであるVuexを付け足して使うことにしました。以下、その簡単な経緯とひとまず手元では動作したというサンプルコードです。
目次
まずは動かす
先に動かしてからの方が話がとおりやすいと思いますので、以下のコピペ手順で動かしてからちょいとだけ補足します。
なお、説明を簡単にするため、冒頭に述べた前回のものが動作している&環境の前提条件が整っているとします。
Vuexをインストール
例えばnpmでインストールしてください。
npm install vuex
Vue CLIでVuexが有効なプロジェクトをcreate
例えば、こんな感じでVue CLIを起動します。
vue create rs-sample-search-vuex
「Manually select features」を選択します。
次にこんな感じの画面が表示されるので、Vuexが対象になるように選択してください。
createしたプロジェクトのディレクトリのmain.js、store.js、App.vueを置き換え
それぞれ次の内容で置き換えて保存してください。
なお、時間があれば、置き換え前に、雛形になっているデフォルトのmain.js stone.jsを見ておくと、Vuexのお作法が見て取れます。(キーワードは、store、state、mutations、actionsあたりでしょうか。)
main.js
ReactiveSearchのお試しコードなので、ReactiveSearchをimportしたり「use」しているのはもちろん、「store」というところに注目ください。
import Vue from 'vue' import App from './App.vue' import store from './store' import ReactiveSearch from "@appbaseio/reactivesearch-vue"; Vue.config.productionTip = false Vue.use(ReactiveSearch); new Vue({ store, render: h => h(App) }).$mount('#app')
store.js
検索BOXを2つ設けるとして、それぞれの現在の入力値を保持する(つもり)プロパティ(ここではkwsと名付けました)とそれらを操作するためのメソッドを定義しています。
ちょっとアレなコーディング&実装アーキテクチャかもしれませんが、ここでは、ReactiveSearchとVuexの噛み合わせを示すための例にしてあります*1。
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { kws :{ k1: "", k2: "" } }, mutations: { mut_KW_UPDATE1(state,text){ state.kws.k1 = text }, mut_KW_UPDATE2(state,text){ state.kws.k2 = text } }, actions: { kw_update1(state,kw){ state.commit("mut_KW_UPDATE1",kw); }, kw_update2(state,kw){ state.commit("mut_KW_UPDATE2",kw); } }, getters:{ kws(state){ return state.kws; } } })
App.vue
Vuexを使う&ここまでのおまじないで、「単一コンポーネント(App.vue)」の中で、「store」について次のことができるようになります。
- 「this.$store.dispatch」で、store.jsで定義されている「更新のためのメソッド」に引数を与えて、「store」のデータを更新できる。
- 「this.$store.getters」で、store.jsで定義されている「store」のデータを取得できる。
<template> <div id="app"> <reactive-base app="rs_index" url="http://localhost:9200/" > <h5>検索お試しランチャー(Vuex)</h5> <data-search componentId="myDataSearch" placeholder="検索キーワード1" @valueChange="sb1ValueUpdate" :customQuery="myCustQuery" className="data-search" /> <data-search componentId="myDataSearch2" placeholder="検索キーワード2" @valueChange="sb2ValueUpdate" :customQuery="myCustQuery" className="data-search" /> <result-list componentId="mySearchResult" :react='{ or: ["myDataSearch","myDataSearch2"] }' :renderData="getResultList" :from="0" :pages="5" :pagination="true" /> </reactive-base> </div> </template> <script> export default { name: "App", methods:{ sb1ValueUpdate(value){ this.$store.dispatch('kw_update1',value) }, sb2ValueUpdate(value){ this.$store.dispatch('kw_update2',value) }, myCustQuery() { let kws_words = this.$store.getters.kws.k1 + " " + this.$store.getters.kws.k2 ; return { query:{ multi_match: { query: kws_words, fields:["title_ja","content_ja"] }}}; }, getResultList( data ) { return { title: data.title_ja,description:`<em>${data.title_ja}</em>` }; } } }; </script> <style> .data-search{ width: 800px; } </style>
アプリを起動してブラウザでアクセス
例えば、次のように起動してください。
npm run start
手順ここまで。
今気づきましたが、ブラウザのコンソールにはワーニングが出まくっています。
また、操作によっては時々不思議な挙動*2をするもののひとまず動いています。
解説(もうちょっとだけ)
上記の単一コンポーネントApp.vueについて補足します。
- myCustQueryの返り値のJSONオブジェクトが、Elasticsearchに投げられるクエリDSLになります。ここでは今回の2BOXならではの検索クエリにはなっていませんが、例えば、この例のように、kws_wordsで検索ワードを与えることができていることは見て取れると思います。
- そのkws_wordsという文字列の変数は、storeから取得できています。
- storeは検索BOXの値を取得しているのですが、sb1ValueUpdate、sb2ValueUpdateという関数でアップデートされる仕組みになっています。
- sb1ValueUpdate、sb2ValueUpdateは、ReactiveSearchのdata-searchコンポーネントの「@valueChange」でイベント関連付けされています。@valueChangeはそのコンポーネントのvalueを指定された関数に引数として渡してくれるものです。要はこの検索BOXでいうと、検索BOXのテキストボックスに入力された値が変更されると、@valueChangeがReactiveSearchの仕組みで呼び出され、さらにsb1ValueUpdate、sb2...の対応するものが起動され、storeに保持される。というものになっています。
前回の例だと、customQueryで指定された関数の第一引数で、検索BOXのテキストボックスの値が受け取れるためこれを検索ワードに使っていましたが、今回は複数の検索BOXの値を使うため、storeを介して2つの検索BOXの値を取得するようにしています。
customQuery、@valueChangeその他についてはこちら↓ opensource.appbase.io ※こちらを見てこの記事で間違っているところがあれば是非是非ご指摘ください。
参考リンク
Vuex公式など
Vuexを特に予告なく登場させましたが、これらの公式についてリンクします。
Vue.js Vuex、その他コーディングスタイルやアーキテクチャについて参考
本記事では記事を単純にするための私の構成力の都合で、いつもどおりアドホックな例に止まっていますが、コンポーネント間の通信についてはいろいろ考えどころがあります。 こちらについて、いくつか参考リンクを貼っておきます。