Contribute to Docs Contribute to Docs

Custom Fields

This article covers how to implement custom fields for Piranha. If you want to read more about the standard field included, please refer to Fields.

Fields are the lowest level of content in Piranha CMS and are used to build up Regions and Blocks. In many cases the fields included in Piranha are sufficient, but you might also want to create custom fields for you application. One use case is for example if you would like to reference a custom entity in your application from a Page or Post, much like the included PageField does.

Reserved Names

When naming your components, whether it's fields, blocks or block groups you should stay clear of the names already used by the internal components of the Piranha manager.

  • block-group-horizontal
  • block-group-vertical
  • block-group
  • folder-item
  • generic-block
  • page-item
  • pagecopy-item
  • post-archive
  • region
  • sitemap-item

The IField Interface

In order to create a custom field your class has to implement the IField interface from the Piranha.Extend namespace. The interface contains the following methods that needs to be implemented:

public interface IField
{
    /// <summary>
    /// Gets the list item title if this field is used in
    /// a collection regions.
    /// </summary>
    string GetTitle();
}
GetTitle
string GetTitle();

Gets the list item title if this field is used in a collection region. Please note that it's only supported to reference Field Properties as title.

Init
// Synchronous Init method
void Init(...);

// Asyncronous Init method
Task Init(...);

Initializes the field for client use. This is called when the field is loaded and is used for loading additional resources. As an example, the ImageField loads the referenced image from the database when Init() is called.

The method supports Dependency Injection which means that any service registered in the DI Container can be added as an parameter. This is useful when loading custom data from your application.

Manager Init
// Synchronous Init method
void InitManager(...);

// Asyncronous Init method
Task InitManager(...);

Initializes the field when it's loaded from within the manager. This can for example be useful if you want to load meta-data for the field that you only need when editing it. Just like the regular Init method it supports Dependency Injection so that you can use any service registered in the DI Container.

Creating a Field

Let's take a look at how a simple string field could be implemented.

using Piranha;
using Piranha.Extend;

[FieldType(Name = "Simple String", Component = "simple-field")]
public class SimpleStringField : IField
{
    public string Value { get; set; }

    public string GetTitle() {
        return Value;
    }

    public void Init() {
        // Nothing special for this field
    }
}

Field Configuration

When registering a field with the FieldTypeAttribute you can set the following properties.

Name
public string Name { get; set; }

The display name of the field type in the manager interface.

Component
public string Component { get; set; }

The name of the global Vue component that should be used for rendering the field in the manager interface.

Register The Field

All available fields has to be registered in the singleton Piranha.App after the app has been initialized.

Piranha.App.Init(api);

// Register custom fields
Piranha.App.Fields.Register<SimpleStringField>();

Create The Manager Component

The manager interface is based on Vue.js and assumes that all fields are registered as global components. As these components are written in Javascript you need to register your custom resources in the manager. For more information about this, please refer to Resources in the section Manager Extensions.

Let's take a look at how the component for our SimpleStringField could look like.

Vue.component("simple-field", {
    props: ["uid", "toolbar", "model", "meta"],
    template:
        "<textarea class='form-control' " +
        "    :placeholder='meta.placeholder' " +
        "    v-model='model.value'>" +
        "</textarea>"
});

Component Properties

All field components get the following properties from the main edit views in the manager interface.

uid

A globally unique id that can be used in the component when rendering more complex views. This field is generated at runtime and will be different each time for every field.

toolbar

The id of the container that toolbars should be positioned inside, for example when using the Tiny MCE editor.

model

This is your custom field, i.e the SimpleStringField serialized as JSON.

meta

Meta information about the field collected from both the FieldTypeAttribute and the FieldAttribute on the model. The meta object has the following properties.

{
    uid: "Runtime generated unique string",
    name: "The field name",
    title: "The title if this component is used in a list",
    description: "Optional description",
    placeholder: "Optional placeholder",
    component: "The name of the vue component",
    id: "The field id",
    isHalfWidth: "How the field is rendered",
    notifyChange: "If this field should notify changes to title"
}

Value Serialization

As default, all fields are serialized as JSON in the database, however you can choose to serialize your data in a different way, or maybe even store it in a separate table in the database. The easiest way to do this is the register a custom serializer for your field. You can read more about this in Serializers.