How I created WPBingo.com
Using Vue.js, Tailwind CSS, and the WP REST API
#wcmia
@rzen
Making WPBingo
I kind of like to create disruption...
Bingo!
#wcmia
@rzen
Making WPBingo
I really like to create disruption...
Bingo!
Bingo!
Bingo!
Bingo!
Bingo!
Bingo!
#wcmia
@rzen
Making WPBingo
What this talk is not...
#wcmia
@rzen
Making WPBingo
Part 1: The Idea
#wcmia
@rzen
Making WPBingo
Just a bulleted list in evernote...
#wcmia
@rzen
Making WPBingo
...wouldn’t it be more fun to play with friends?
#wcmia
@rzen
Making WPBingo
...besides, I needed an excuse to play with some new libraries anyway...
#wcmia
@rzen
Making WPBingo
What I wanted to accomplish:
#wcmia
@rzen
Making WPBingo
Here’s what I created...
#wcmia
@rzen
Making WPBingo
#wcmia
@rzen
Making WPBingo
Part 2: Exploration
#wcmia
@rzen
Making WPBingo
Why Vue.js?�Why not [framework]?
#wcmia
@rzen
Making WPBingo
Two Good Reasons:
(Plus, this was a learning exercise. I’ve already practiced with others)
#wcmia
@rzen
Making WPBingo
Vue.js is capable of a lot of cool things and works like I expect/predict.
#wcmia
@rzen
Making WPBingo
Pause: A Primer
on Vue.js
#wcmia
@rzen
Making WPBingo
Creating a Vue Instance
#wcmia
@rzen
Making WPBingo
Vue Directives
v-model: bind an input to a dynamic data from JavaScript
<input v-model="message">
#wcmia
@rzen
Making WPBingo
Vue Directives
<div class="edit-product" v-if="product.id">
v-if: only render element if something evaluates truthy
#wcmia
@rzen
Making WPBingo
Vue Directives
v-for: repeat an element in a for-style loop
<tr v-for="product in products">
<td>{{ product.id }}</td>
<td>{{ product.name }}</td>
<td>{{ product.stock_quantity }}</td>
</tr>
#wcmia
@rzen
Making WPBingo
Vue Directives
v-bind: bind an element’s attribute to dynamically controlled data
<a v-bind:href="link">Visit Link</a>
Shorthand: just use a colon, followed by value
<a :href="link">Visit Link</a>
#wcmia
@rzen
Making WPBingo
Vue Directives
v-on: bind a method to an event
<button v-on:click="saveProduct">Save</button>
Shorthand: just use an @, followed by event
<button @click="saveProduct">Save</button>
#wcmia
@rzen
Making WPBingo
Vue Directives
v-on: also accepts stock event modifiers, like preventDefault():
<button v-on:click.prevent="saveProduct">Save</button>
Shorthand works the same way here, too:
<button @click.prevent="saveProduct">Save</button>
#wcmia
@rzen
Making WPBingo
Let’s talk about CSS
#wcmia
@rzen
Making WPBingo
Object Oriented CSS (OOCSS)
#wcmia
@rzen
Making WPBingo
Block - Element - Modifier (BEM)
#wcmia
@rzen
Making WPBingo
Scalable and Modular Architecture for CSS (SMACSS)
#wcmia
@rzen
Making WPBingo
Atomic CSS
#wcmia
@rzen
Making WPBingo
#wcmia
@rzen
Making WPBingo
#wcmia
@rzen
Making WPBingo
mx-auto
#wcmia
@rzen
Making WPBingo
mx-auto
Set margin left and right to “auto”
#wcmia
@rzen
Making WPBingo
my-4
#wcmia
@rzen
Making WPBingo
my-4
Set margin top and bottom to 1rem
#wcmia
@rzen
Making WPBingo
p-4
#wcmia
@rzen
Making WPBingo
p-4
Set padding on all sides to 1rem
#wcmia
@rzen
Making WPBingo
#wcmia
@rzen
Making WPBingo
Part 3: Prototyping
#wcmia
@rzen
Making WPBingo
[Recap] What I wanted to practice:
#wcmia
@rzen
Making WPBingo
I really enjoyed Vue’s syntax
v-if
v-for
v-bind
v-model
@click
@change
v-show
data: {}
methods: {}
created()
#wcmia
@rzen
Making WPBingo
Data prep
#wcmia
@rzen
Making WPBingo
Creating markup
<div v-if="gameBoard">
<div v-for="cell in gameBoard">
<label>
<span v-html="cell.label"></span>
<input class="hidden" type="checkbox"
v-model="cell.found" @change="trackChange()">
</label>
</div>
</div>
#wcmia
@rzen
Making WPBingo
Adding custom methods
[code redacted]
#wcmia
@rzen
Making WPBingo
Tapping into local storage is super easy.
#wcmia
@rzen
Making WPBingo
Storing data to local storage
setLocalStorage() {
localStorage.setItem(
'startTime',
this.startTime
);
localStorage.setItem(
'gameBoard',
JSON.stringify( this.gameBoard )
);
}
#wcmia
@rzen
Making WPBingo
Fetching data from local storage
getLocalStorage() {
this.startTime = localStorage.getItem( 'startTime' );
this.startTime = JSON.parse(
localStorage.getItem( 'gameBoard' )
);
}
#wcmia
@rzen
Making WPBingo
Atomic CSS is real slick, with a pre-built and well-documented framework
#wcmia
@rzen
Making WPBingo
The list of classes gets a bit unwieldy...
<div class="container mx-auto mb-4 px-2 flex flex-wrap">
<div class="cell flex w-1/5 p-2 border-grey-lighter border-2 bg-white">
<label class="text-xs self-center text-center w-full h-full block flex">
<span class="self-center text-center w-full" v-html="cell.label"></span>>
</label>
</div>
</div>
#wcmia
@rzen
Making WPBingo
But this is the entirety of my custom CSS:
<style>
.cell {
height: 20vw;
max-height: 150px;
}
</style>
#wcmia
@rzen
Making WPBingo
Scoring a bingo game was an undocumented, fun new challenge
#wcmia
@rzen
Making WPBingo
#wcmia
@rzen
Making WPBingo
#wcmia
@rzen
Making WPBingo
2. Score a set of squares
#wcmia
@rzen
Making WPBingo
3. Check if any set has a score of 5
#wcmia
@rzen
Making WPBingo
Registering custom endpoints
was the easiest part of all
#wcmia
@rzen
Making WPBingo
Feed data from WP REST API
#wcmia
@rzen
Making WPBingo
$> wp scaffold plugin wpbingo --skip-tests --activate
Success: Created plugin files.
$> wp scaffold post-type wpbingo --title="Bingo Square" --dashicon=grid-view --plugin=wpbingo
Success: Created post type 'Bingo Square'.
$> wp scaffold taxonomy wpbingo-card --post_types=wpbingo --label="Bingo Card" --plugin=wpbingo
Success: Created taxonomy 'Bingo Card'.
$> wp scaffold taxonomy wpbingo-category --post_types=wpbingo --label="Square Category" --plugin=wpbingo
Success: Created taxonomy 'Square Category'.
#wcmia
@rzen
Making WPBingo
Here’s the magic REST bit:
// Allow random ordering in wpbingo REST endpoint
function wpbingo_init( $params ) {
register_post_type( 'wpbingo', array(
// ...
'show_in_rest' => true,
'rest_base' => 'wpbingo',
'rest_controller_class' => 'WP_REST_Posts_Controller',
) );
}
add_filter( 'init', 'wpbingo_init' );
#wcmia
@rzen
Making WPBingo
Next, add random sorting
// Allow random ordering in wpbingo REST endpoint
function wpbingo_rest_allow_rand_orderby( $params ) {
$params['orderby']['enum'][] = 'rand';
return $params;
}
add_filter( 'rest_wpbingo_collection_params', 'wpbingo_rest_allow_rand_orderby' );
#wcmia
@rzen
Making WPBingo
WARNING: Random sort is non-cacheable and non-performant, which is why core excludes support by default.
#wcmia
@rzen
Making WPBingo
Finally, modify the REST response
// Modify wpbingo REST response
function wpbingo_rest_modify_fields( $data ) {
return [
'label' => $data->data['title']['rendered'],
'found' => 0,
];
}
add_filter( 'rest_prepare_wpbingo', 'wpbingo_rest_modify_fields', 12 );
#wcmia
@rzen
Making WPBingo
Et voila, the desired random output:
Postman HTTP Client
#wcmia
@rzen
Making WPBingo
Additional Resources
#wcmia
@rzen
Making WPBingo
Great presentations on WPSessions
#wcmia
@rzen
Making WPBingo
Vue Tutorials
#wcmia
@rzen
Making WPBingo
Learn more about Tailwind and Atomic CSS
#wcmia
@rzen
Making WPBingo
Thanks!
Don’t forget to play WPBingo during the 4:40pm keynote for a chance to win a prize!
Slides & Discount: https://wpsessions.com/wcmia
#wcmia
@rzen
Making WPBingo