menu
announcement

Spectrum is now read-only. Learn more about the decision in our official announcement.

Twill

Rapidly create a custom admin console that content publishers will love. Twill is an open source CMS toolkit for Laravel, crafted by AREA 17.

Channels
Team

Adding a custom block to Twill Admin View with VueJS

September 25, 2020 at 1:09pm

Adding a custom block to Twill Admin View with VueJS

September 25, 2020 at 1:09pm
Twill has an admin view that is based on VueJS, which means that it can also be extended by VueJS. However, the process is a bit cumbersome if you don’t know how it works…

1. Adding a custom block to the Twill Admin Interface

You can add any custom block to an admin view by creating a new .blade.php file and moving it to views/admin/blocks.
This can be called with the standard blade terms of ('admin.blocks.{name-of-your-block}, [//array of variables]from another blade file.

2. Adding custom Vue to the block

Any new Vue component needs to be compiled with and by the main Twill code, which lives in /vendor/area17/twill. In order to add our new Vue code, we do these steps:

2.1. Add Vue wrapper to new block

Inside the block created in step 1, you can add this code to integrate the Vue component. The “a17-block” part is fixed, apparently, so just keep it.
<a17-block-vue-example
name="name"
>
</a17-block-vue-example>

2.2. Add Vue block to Twill Config

Twill needs to know that there is a new Vue component in town. Add this info to the twill.php from /config:
'blocks' => [
'vue-example' => [
'title' => 'Example',
'trigger' => 'Add Example Block',
'components' => 'a17-block-vue-example',
'compiled' => true
],
]

2.3. Add the actual Vue file

To have Twill build your new Vue file, create a new file in resources/assets/js/blocks and name it the same as you have in your blade.php, so for instance BlockVueExample.vue. It’s now a valid Single File Component for Vue!

2.4. Make Twill build it and use it

Twill still needs to compile the file and make it available in the twill-specific main JS files. This can be done by running php artisan twill:build from your root container. Apparently, it’s also possible to use twill:dev to get Hot Module Reload functionality. If this doesn’t work right away, go to twill.php in configs and add dev-mode: true.
If you are on Docker, twill:dev won’t work, because the ports aren’t mapped correctly. If you know how to work around that, please do so, otherwise you’re stuck with running twill:build after every change.
Also, your container needs to have NodeJS and npm installed when it’s composed.
As a result of twill:build, Twill will copy a new JS file to the public folder that should contain your Vue file and run it inside the mounted blade.

3. Interfacing with data

If all went well, we can now start to interface with the data from the backend/ database inside the Vue component. Twill uses the VueX store to hold data until it’s transfered into the database, so we need to hook into this.

3.1. Setup the Model and database migration

3.1.1 Migration

In order to send data to and from the database, you need to add a column to your database first. Run php artisan make:migration ExampleNewFieldto have Laravel make a new migration file inside of /database/migrations.
Inside this newly created file, add this code to the public function up()
Schema::table('// table_name//', function (Blueprint $table) {
$table->json('example_block')->nullable();
});
Make sure to rename table_name and example_fieldaccordingly. Also, json() can be replaced by any other field type, see here: https://laravel.com/docs/8.x/migrations#creating-columns
Run php artisan migrate.
Use your database inspection to check if the field exists.

3.1.2. Model

Your form.blade.php should have a corresponding Model page under App/Models. Add your new field into the $fillable array:
protected $fillable = [
'example_block',
//other fields
];

3.2. Getting data from the database into the Vue component

The general flow of data can be split into “display data” and “form data”.

3.2.1. Display Data

To display any data inside your Vue component, pass it from the blade component via props.
Inside form.blade.php
@include('admin.blocks.example', [
'name' => 'example-block',
'subtitle' => 'A subtitle string'
])
Inside example.blade.php
@php
$name = $name ?? 'name-undefined';
$subtitle = $subtitle ?? 'subtitle-undefined';
@endphp
<a17-block-vue-example
name="{{ $name }}"
subtitle="{{ $subtitle }}
>
</a17-block-vue-example>
Inside BlockVueExample.vue
<template>
{{ subtitle }}
{{ name }}
</template>
<script>
export default {
props: {
name, subtitle
}
}
</script>

3.2.2. Form Data

As mentioned above, Twill makes use of the VueX state management library. In order to get data into a form field, you need to pass into the store as well (from PHP) and save it into your Vue component with a function. Let’s take a look how this works.
Inside form.blade.php
@include('admin.blocks.example', [
'name' => 'example_block',
// if you don't use complex data, you can skip the json_decode here
'formData' => json_decode($form_fields['example_block'])
])
The name inside $form_fields['example_block'] should be the same as you put into your Model-file in 3.1.2.
Inside example.blade.php
@php
$name = $name ?? 'name-undefined';
$formData = $formData ?? null;
@endphp
<a17-block-vue-example
name="{{ $name }}"
in-store="value"
>
</a17-block-vue-example>
@if(isset($formData))
@push('vuexStore')
window['{{ config('twill.js_namespace') }}'].STORE.form.fields.push({
name: '{{ $name }}',
value: {!! json_encode($formData) !!}
})
@endpush
@endif
The code from line 13 to line 20 will push the $formData into your VueX store and we’ll fetch it there in the next step. Name and value are the important variables here. Also add the “in-store” prop to your vue tag!
Inside BlockVueExample.vue
<template>
{{ name }}
<input :v-model="value">
</template>
<script>
import FormStoreMixin from '@/mixins/formStore'
export default {
mixins: [FormStoreMixin],
props: {
name
},
data () {
return {
value: {}
}
},
mounted() {
if (this.storedValue.length !== 0) {
this.value = this.storedValue
}
}
}
</script>
Ok, more is going on here:
  • the FormStoreMixin makes the VueX store available inside of the component
  • In the mounted function, you access the store with this.storedValue , which is only available if you set in-store inside the vue tag in your blade file.
  • The actual value is tracked in value
Hooray, we have database data inside of Vue!

3.3. Getting data from Vue into the database

This part is actually a bit more straight forward. Twill will save all info from the current VueX state into the database, if it’s defined in the model (see 3.1.2). So we only need to push the value into the store and we can re-use the vue mixin from 3.2.2.
<template>
{{ name }}
<input :v-model="value">
</template>
<script>
import FormStoreMixin from '@/mixins/formStore'
export default {
mixins: [FormStoreMixin],
props: {
name
},
data () {
return {
value: {}
}
},
mounted() {
if (this.storedValue.length !== 0) {
this.value = this.storedValue
}
},
methods: {
updateValue: function (newValue) {
if (this.value !== newValue) {
this.value = newValue
// see formStore mixin
this.saveIntoStore(this.value)
}
}
}
}
</script>
Calling updateValue(newValue) will save the value into the store, provided you have a valid name and a valid value (name passed as prop!). The function this.saveIntoStore(this.value) is from the FormStoreMixin.
Now, you can finally click Update on the Admin Interface and your data should be transfered to the database via the rest of the backend logic.
No messages yet