Vue.js and WordPress

I recently had the opportunity to use Vue.js in one of the backend WordPress plugins I was creating. I've used both React and Angular in I ...

Reading Time
3 min (~550 words)
Tags
Javascript
Wordpress
Date
11/6/2016
Vue.js and WordPress

I recently had the opportunity to use Vue.js in one of the backend WordPress plugins I was creating. I've used both React and Angular in past projects but I wanted something lightweight and quick to set up and use, Vue seemed like the perfect tool for the job.

This plugin was being created to make it easier for content and SEO team members to view information about different posts/pages in the back-end of WordPress. They needed to be able to see the ID, Slug and Title of each post on the site and quickly sort through them. The default WordPress admin view for pages is incredibly slow and the search is not very accurate so I set out to improve it.

To start out, I get the plugin initialized by adding our admin page. It's important to namespace our plugin as well to prevent global scope pollution.

1<?php namespace PageInfo;
2
3// We can pass add_action a closure instead of passing it a function name.
4add_action('admin_menu', function() {
5  add_menu_page(
6    'All of the pageinfo',
7    'Pageinfo',
8    'edit_pages',
9    'pageinfo',
10    __NAMESPACE__.'\\createAdminPage',
11    'dashicons-clipboard',
12    6);
13});

read more about what add_menu_page() does.

This will create our page in the backend of WordPress and adds a link to it in the admin sidebar. Anyone with permission to edit pages can access it.

Next, we need to set up the "createAdminPage()" function. We'll use it to build the content of our page. To start with, we need to make a new WP_Query that will grab all of the posts and pages on the site.

(In the future, I may just use the WordPress REST api to do this, since it saves a step.)

1function createAdminPage() {
2  ?><div class="wrap"><?php
3
4    /**
5    * This queries WordPress for every page and post.
6    * We need to use a '\' before WP_Query to tell php to use the global namespace, since that function does not exist in this scope.
7    */
8    $pageInfo = new \WP_Query([
9      'post_type' => [
10        'post',
11        'page'
12      ],
13      'posts_per_page' => -1,
14      'orderby' => 'ID',
15      'order' => 'ASC'
16    ]);

read more about namespaces

Now that we have that, we can create a new javascript object that contains all of those post's data and get our Vue object initialized.

1<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.min.js"></script>
2
3<script>
4new Vue({
5  el: '#pageInfo',
6  computed: {
7    filteredPages: function () {
8      return this.pages.filter(function(item){
9        return item.url.indexOf(this.searchQuery) > -1;
10      }.bind(this));
11    }
12  },
13  data: {
14    searchQuery: '',
15    pages: [
16      <?php if ($pageInfo->have_posts()): while($pageInfo->have_posts()):
17        $pageInfo->the_post();
18        $url = parse_url(get_the_permalink());
19        ?>
20        {
21          url: '<?php echo $url['path']; ?>',
22          id: '<?php the_ID(); ?>',
23          title: '<?php the_title(); ?>',
24        },
25        <?php endwhile; endif; ?>
26      ]
27    }
28  });
29  </script>

With this we can construct our view layout in html with Vue tags.

1<div id="pageInfo" style="margin: 0 auto;width: 80%; max-width: 1024px;">
2
3  <h1>Pages &amp; Posts</h1>
4  <p><small><em>
5    This page shows all of the existing posts and pages in an easy to read format. You can also filter by the page url.
6  </em></small></p>
7
8  <strong><label for="search">Filter</label></strong>
9  <input v-model="searchQuery" name="search">
10  <h2 v-if="!filteredPages.length">No results, sorry</h2>
11  <p>{{ filteredPages.length }} result(s)</p>
12  <table style="margin: 2em 0;" cellspacing="0" cellpadding="0">
13    <thead>
14      <tr>
15        <th><strong>TITLE</strong></th>
16        <th><strong>URL</strong></th>
17        <th><strong>ID</strong></th>
18      </tr>
19    </thead>
20    <tbody>
21      <tr v-for="page in filteredPages">
22        <td v-html="page.title"></td>
23        <td><a v-bind:href="page.url">{{ page.url.slice(1).length > 0 ? page.url.slice(1) : "home" }}</a></td>
24        <td>{{ page.id }}</td>
25      </tr>
26    </tbody>
27  </table>
28</div>

Add it all together, and you get something like this (I've added a few css styles to clean it up)

You can drop this plugin file into the wp-content/mu-plugins/ folder and forget about it.