Fieldtypes

Fieldtypes are specialized form inputs designed to help manage content and data in the control panel. They are flexible and reusuable in all Content Types.


Overview

Fieldtypes are Vue.js components that have a corresponding PHP classes to facilitate any pre/post processing of data.

Having a good understanding of Vue.js will allow you to build more elaborate fieldtypes more readily. Their documentation is very helpful - it’s worth a read. We’ve fallen in love with Vue.js and think you should too.

Note: We use Vue.js v1 in the Statamic Control Panel.

Anatomy of a Fieldtype

At its most basic level, a fieldtype requires an AddonNameFieldtype.php class and a corresponding js/fieldtype.js Vue component.

site/addons/MyAddon/
|-- /resources/assets/js/
|   |-- fieldtype.js
|-- MyAddonFieldtype.php

Multiple Fieldtypes

Since 2.6, an addon may have more than one fieldtype. One “primary” fieldtype, and multiple “secondary” fieldtypes.

Directory Structure

You may store your modifier classes either in the root directory, like so:

site/addons/Bacon
|-- BaconFieldtype.php
|-- BitsFieldtype.php
`-- meta.yaml

…or within a Fieldtypes directory/namespace if you wish to stay more organized:

site/addons/Bacon
|-- Fieldtypes
|   |-- BaconFieldtype.php
|   `-- BitsFieldtype.php
`-- meta.yaml

You still only need one single fieldtype.js file. You may add multiple Vue components in it.

Primary vs. Secondary

An addon’s primary fieldtype will use the name of the addon.

field_name:
  type: your_addon

This will correspond to the Statamic\Addons\YourAddon\YourAddonFieldtype or Statamic\Addons\YourAddon\Fieldtypes\YourAddonFieldtype class.

Secondary fieldtypes will use the name of the addon and the secondary name, delimited by a dot.

field_name:
  type: your_addon.my_secondary

This will correspond to the Statamic\Addons\YourAddon\MySecondaryFieldtype or Statamic\Addons\YourAddon\Fieldtypes\MySecondaryFieldtype class.

Note: In your fieldtype.js file, secondary components follow this naming convention: your_addon-my_secondary-fieldtype.

Generating a Fieldtype

The php please make:addon or php please make:fieldtype commands will create these for you - with some boilerplate code to get you going.

The code within the fieldtype.js file will be loaded automatically within the Control Panel.

Note: This will only generate a primary fieldtype in the root directory.

The Javascript side

Vue.js lets us do work some magic with the power of two-way data-binding, like knowing and having access to the current state of all data in a Publish screen at all times.

Statamic calls the defined Vue.js component using the props data, config and name.

Let’s dive into an example to make things clearer. Have you ever seen one of those password fields with a checkbox that toggles whether the text is visible? Let’s build one of those.

Secret Password? Nahhhh...

Let’s start with the Vue component.

Vue.component('password_toggle-fieldtype', {

    mixins: [Fieldtype],

    data: function() {
        return {
            show: false
        };
    },

    computed: {
        inputType: function() {
            return this.show ? 'text' : 'password';
        }
    }, 

    template: '' +
      '<div>' +
        '<input :type="inputType" v-model="data" />' +
        '<input type="checkbox" :id="name" v-model="show" />' +
        '<label :for="name">Show password</label>' +
      '</div>' +
    ''

});

Fieldtype Mixin

This mixin provides some basics for you automatically.

It gives you the three required props that are passed into your fieldtype by a parent fieldtype builder component. The data prop contains the field’s value, the name prop is the name of the field in the fieldset, and the config props is the config from the fieldset.

It also sets things up like data value change watchers. See below for more information.

Data

data should be a function that returns an object containing any variables you want available in your component.

In the example, we’re defining show to keep track of whether the value should be shown or not.

Computed

The computed property is an object containing any values that should be dynamically evaluated.

In the example, we’re letting inputType be the value for the input’s type attribute - making it either a text or password field.

Template

template is the html that’ll be rendered where you’d expect to see your fieldtype.

You should try to make sure that everything is contained within one root element - like the div in the example.

Let’s take a closer look at the template:

<input :type="inputType" v-model="data" />
<input type="checkbox" :id="name" v-model="show" />
<label :for="name">Show password</label>

The colons and v-’s you see is Vue’s binding syntax at work.

The colons will evaluate the value and turn it into the attribute. So, :type="inputType" will either be type="text" or type="password", depending on the value of inputType.

The v-model attributes will bind the values to the form input. If you type into the text box, data will get updated. If you check the checkbox, show will get updated.

Methods, Events, Ready…

Vue has more features you can use in your fieldtypes. Remember, a fieldtype is just a Vue component. You aren’t limited to the items used in the example.

Refer to the Vue component docs for more information.

Data Change Watcher

Have you ever filled out a long form and accidentally navigated away from the page, only to lose all your progress? That’s really annoying! The publish page component will monitor fieldtypes for any changes, and prevent users from navigating away by mistake.

For simple fieldtypes, this is done automatically just by making sure to mix in the Fieldtype mixin, as demonstrated above. This mixin will fire up an event watcher, monitoring your data prop for any changes and telling the publish component that something’s different.

If your fieldtype is more complex, there are a couple of steps for you to add to get this going. By “more complex” we mean you might perform some initial data modification once your component is ready, like AJAX loading or setting a default value.

First, tell the fieldtype to not automatically bind the watcher. Then, bind it manually when you’re ready. Take this example that needs to adjust the initial data after an AJAX request.

{
    mixins: [Fieldtype],

    data: function () {
        return {
            autoBindChangeWatcher: false // Disable the automagic binding
        }
    },

    ready: function () {
        this.$http.get('/example', function (response) {
            this.data = response.data.something; // Manipulate as required.
            this.bindChangeWatcher(); // Bind manually once you're ready.
        });
    }
}

Focus

When Statamic wants to programatically focus your field (for example, when adding a new Replicator set or Grid row), it needs to know how to do so.

By default, Statamic will simply call .focus() on your Vue component’s $el property (a DOM element). This will work fine if you have a simple fieldtype like the text field.

You may customize how focusing works by adding a focus() method to your Vue component. For example:

template: `<div><div><input type="text" v-el:my-custom-element /></div></div>`,
methods: {
    focus() {
        this.$els.myCustomElement.focus();
    }
}

Replicator Preview Text

When Replicator sets are collapsed, Statamic will display a preview of the data within it.

By default, Statamic will do its best to display your fields data. However, if you have a value more complex than a simple string or array, you may want to customize it.

You may customize the preview text by adding a getReplicatorPreviewText() method to your Vue component. For example:

methods: {
    getReplicatorPreviewText() {
        return this.data.join('+');
    }
}

The PHP Side

The PHP side of your fieldtype is responsible for manipulating data before the data is loaded or after it’s submitted. This will let you enforce data types, set relationships, fire events, or any other number of things.

Example Fieldtype Class

<?php

namespace Statamic\Addons\MyAddon;

use Statamic\Extend\Fieldtype;

class MyAddonFieldtype extends Fieldtype
{
    public function blank()
    {
        return null;
    }

    public function preProcess($data)
    {
        return $data;
    }

    public function process($data)
    {
        return $data;
    }
}

These are the default method values for a fieldtype. As you can see, we’re just passing the data along untouched, but you could do anything you’d like here.

Blank value

The value returned by the blank method will be used as the “default” value for fields using your fieldtype. For example, maybe your field should be dealing with arrays by default. In that case, you should return [].

Processing

The preProcess and process methods handle any manipulation of data coming to and from the field.

The preProcess method is called after data is read from file and before it’s sent to the publish page. This could be useful if your Vue component needs the data in a different format than what’s appropriate to be saved in files.

The process is the opposite: it’s called after the publish page is submitted and before the data is written back to file.

In both cases, simply return the value after you’ve manipulated it.

Extras

Auto-slugify

If you have a need to automatically create a slug in one place based off the value of another field, we have a Vue mixin you can use in your component. An example of this is when you type into a title field and the slug field gets generated as you type.

{
    mixins: [Fieldtype, AutoSlug],

    ready() {
        this.autoSlug()
    }
}

The autoSlug method takes 2 arguments:

  • The name of the field to generate a slug from. In the example above, this would be "title".
  • The name of an instance property on your Vue component where the slug should be stored. If left blank, it will be "data" which is like doing this.data = newSlug.

If you’re thinking about using this to make a simple slugging field, the Text fieldtype has an autoslug option that does this.

Last modified on October 9, 2018