Vue.js が予想以上に良かったので、既存WordPressに導入。Vue.js (vue-class-component) + TypeScript + WordPress で作る、記事読み込み component 「実装編」
2017.03.26
この記事は最終更新日から1年以上が経過しています。
こちらの続きとなります。
では、前回の続きから、
components/post-more-btn.html
<div class="moreBtnBox"> <a @click="onClick">他の記事を読み込む</a> </div>
componentsである、「post-more-btn.html」をクリックすると、その対となる 「post-more-btn.ts」の「PostMoreBtn」 のonClickが実行される流れでした。
components/post-more-btn.ts
import Vue = require('vue'); import Component from 'vue-class-component'; import PostMoreService from '../service/post-more.service'; import bus from '../bus' @Component({ props: ['nowPostNum'], template: require('./post-more-btn.html') }) export class PostMoreBtn extends Vue { private nowPostNum: number; private postMoreService: PostMoreService = new PostMoreService(); onClick (): void { this.postMoreService.fetchData(this.nowPostNum) .then((response: any) =>; { bus.$emit('update-post', response.data); this.nowPostNum += 10; }, (error: any) => { console.error(error); }); } }
onClick関数を見てみると、postMoreServiceのfetchDataを行っているのが確認できます。
そのpostMoreServiceを見てみると、このようになっています。
service/post-more.service.ts
const axios = require('axios'), wpAjaxUrl = window['wpAjaxUrl']; export default class PostMoreService { private formData = new FormData(); constructor() { this.formData.append('action', 'get_more_post'); } public fetchData(nowPostNum: number): any { this.formData.append('now_post_num', nowPostNum); return axios({ method: 'post', url: wpAjaxUrl, data: this.formData }); } }
formDataオブジェクトのインスタンスをprivateで保持しているのは、wordpressでAjax通信する(POSTでリクエストする)際、
RequestHeaderのcontent-typeが「application/x-www-form-urlencoded」である必要であるためです。
jQueryなどを使用していて、$.ajax()を用いてAjax通信する際は、jQuery側がよしなにやってくれているかもですが、今回は、特に、jQuery等も使わず「axios」を用いてAjax通信しているので、formDataオブジェクトのインスタンスを作っています。
PostMoreServiceのインスタンスが生成される際に実行されるconstructor関数に書かれている
this.formData.append('action', 'get_more_post');
は、formDataオブジェクトに action名である「get_more_post」をappendし、Ajax通信後のwordpress側の関数を実行するためのものです。
axiosはAjax通信後、Promiseが返却されますので、axios関数自体をreturnしております。
service/post-more.service.ts
return axios({ method: 'post', url: wpAjaxUrl, data: this.formData });
APIのエンドポイントである、「wpAjaxUrl」は、tml側でグローバル変数として受け取っている形となります。
これは、wordpressで用意されているAjaxの経路となります。
該当のファイル(エンドポイント)は、「wp-admin/admin-ajax.php」となり、そちらにリクエストを送る形となります。
この経路が一般的に、「wordpressでのAjax経路」として知られている形となります。
<script>var wpAjaxUrl = '<?php echo admin_url('admin-ajax.php'); ?>';</script>
admin_url関数に「’admin-ajax.php‘」の引数を渡すことで、wordpressのAjaxのpathを取得することができます。
wordpress側
Ajax用のアクションフックに登録
function.php
add_action( 'wp_ajax_get_more_post', 'get_more_post' ); add_action( 'wp_ajax_nopriv_get_more_post', 'get_more_post' );
この関数をアクションフックに登録する必要があります。
アクションフック処理をするadd_actionメソッドが二つありますが、一つ目はログイン状態での登録で、二つ目は非ログイン状態での登録となります。
function.php
function get_more_post() { global $wpdb; $now_post_num = $_POST['now_post_num']; $get_post_num = 10; $sql = "SELECT $wpdb->posts.ID, $wpdb->posts.post_title, $wpdb->posts.post_content FROM $wpdb->posts WHERE $wpdb->posts.post_type = 'post' AND $wpdb->posts.post_status = 'publish' ORDER BY $wpdb->posts.post_date DESC LIMIT $now_post_num, $get_post_num"; echo create_post_json($wpdb->get_results($sql)); die(); } function create_post_json($posts) { $post_list = array(); foreach($posts as $post) { $postId = $post->ID; $post_list[] = array( "title" => $post->post_title, "date" => get_the_date("Y年n月j日 l", $postId), "imageUrl" => get_post_image_src($postId), "postUrl" => get_permalink($postId), "postDetail" => mb_substr(strip_tags($post->post_content), 0, 160).'…', "tagList" => create_post_tag_list(get_the_tags($post->ID)), "categoryList" => create_post_category_list(get_the_category($postId)) ); } return json_encode($post_list); }
get_more_post関数は、アクションフックと結びついており呼び出されます。
何件目から記事データを取得するかを、「now_post_num」をpostデータで受け取ります。
global変数である「$wpdb」を使用し、SQL文を発行します。
コードでreturnを使っていないのは、returnを使用するとレスポンスに0が返ってしまう為、die()で処理を終わらせています。
create_post_json関数は、フロント用にjsonデータとして加工する関数です。
これでVueJS側のリクエストである、「postMoreService」と、レスポンス側のwordpressのfunction.phpの準備が整いました。
最後は、レスポンスが返って来た後の、pub/subの役割を持つbusについてです。
親子間以外の通信を行う 「bus」
親子関係ではない2つのコンポーネントの通信を行う場合は、「bus」を利用します。
これは、空の Vue インスタンスを中心のイベントバスとして使用することができます。
公式ガイドページより
bus.ts
var bus = new Vue();
// コンポーネント A のメソッドの中で bus.$emit('id-selected', 1)
// コンポーネント B の created フックで bus.$on('id-selected', function (id) { // ... })
と、公式ガイドページとあまり説明もなく、実際にもあまり説明することもないのですが。。
とりあえず、ざっくり言うと、「EventEmitter的な振る舞いが、空の Vueインスタンスで実現できますよ」と、いった感じです。
ちょっと、前回の記事に振り返り、「Vue.js が予想以上に良かったので、既存WordPressに導入。Vue.js (vue-class-component) + TypeScript + WordPress で作る、記事読み込み component 「環境構築編」」で、作成した post-list.tsを見てみましょう。
components/post-list.ts
created() { bus.$on('update-post', this.onUpdatePost) } destroyed() { bus.$off('update-post', this.onUpdatePost) }
post-list.tsで定義されている「created関数」は、component生成されたタイミングで実行され、destroyed関数はその逆で、componentsが破棄されたタイミングで実行されます。
なので、component生成されると同時に、「bus.$on」でupdate-postを購読登録し、this.onUpdatePostを実行するようにしております。
また、componentsが破棄されると同時に、「bus.$off」でupdate-postを購読解除を行っております。
なので、post-more.serviceでは、レスポンスが返ると、update-postがemitされ、components/post-list.ts
側のthis.onUpdatePost関数が呼び出され、記事リストである componentsの、「components/post-list.html」が更新される流れとなります。
service/post-more.service.ts
bus.$emit('update-post', response.data);
と、簡単ではありましたが、「wordpress」と「Vue Class component」を使った、「もっと記事を読み込む」の流れでした。
なんとなく、「Angular」に近い感じではありますが、「Angular」を使うとなると、ガッツリ「SPA」になってしまうので、今回の様な、ちょっとした機能には、手軽にすぐに実装できる「Vue Class component」が良い気がしました。
実は、「カテゴリ」とかに全く考慮していないので、工夫して作ってみると良いかもしれませんね。
それでは、またぁまたぁ。
公式ガイドページ