CMS - Custom Module Fields

Creating your own module fields

## Introduction --- When creating ABC Manager modules, we offer you a complete set of form fields so your editorial users will be able to edit module content. If however, the native fields offered by ABC are not sufficient, you have the option to create custom fields that expose your own, tailor made, logic for your editorial users. [Custom Fields](https://docs.angrybytes.com/p/abc-manager/6.0/docs/cms/module_definitions/custom_fields/index.html) are composed of two PHP components that parse the field from the module definition and create a form field that will be rendered in the module's edit view. Additionally, each field is able to expose multiple CSS and JavaScript assets that will give you the flexibility to add a powerful user experience to the field for the users.
This demonstration assumes you have already read the documentation about creating a Custom Field and will not explain the details in depth.
### The Youtube Preview Field The Youtube preview field is an example of how a custom field can assist a default input field and provide the editorial user with a Youtube embedded preview when creating and/or modifying content. Giving visual feedback instead of just showing a Youtube URL (or ID) helps the editorial users understand which video has been attached to the module content. ![Grid Filter](/bundles/demopublicationbundle/img/demo/custom-field.png) #### The field parser First thing required to support the custom youtube field is to write a XML definition `` parser that knows how to handle (and parsers) the field settings from a module XML definition. The Youtube field has its own type; `youtube_embed`, and the field definition looks like: ```xml Enter the youtube video id (e.g. "https://www.youtube.com/watch?v=[video-id]") ``` For this field, we have choosen to name it `youtube_embed` and to store its value in the name/column `youtube_id`. Now for ABC Manager to know how we should parse this field definition, we will have to create a field parser and register it as a **tagged service** in the service container. The field parser tells ABC Manager which field types it handles and finally returns an ABC Manager field instance; a subclass of `Abc\BaseBundle\Field`. This is either an existing Field component, but can also be a new field. In our experience, most fields will return a default text field (`Abc\BaseBundle\Field\Text`), since they simply add a layer of custom CSS/JavaScript base UX logic on top of the field and will write a value into the text field when the user interacts with the fields. The Youtube field parser looks as follows: ```php // File: src/Demo/ContentBundle/BackendInterface/Field/YoutubeEmbed/Parser.php namespace Demo\ContentBundle\BackendInterface\Field\YoutubeEmbed; use Abc\BaseBundle\Field as BaseField; use Abc\BaseBundle\Field\Text as TextField; use DOMElement; use DOMXPath; /** * Youtube embed module def XML parser */ final class Parser extends \Abc\BaseBundle\Field\Parser { /** * {@inheritdoc} */ public function parse(DOMElement $domNode, DOMXPath $xpath): BaseField { return $this->parseDefault( new TextField, $domNode, $xpath ); } } ``` Note that `parseDefault()` is an ABC core method that parses a `DOMElement` and `DOMXpath` node into a field, knowing how to parse all native ABC Manager field attributes (e.g. `in_grid`, `in_form`, etc.) and set these values into the Field. Of course you can also write your own parser. In this example, we pass our own `YoutubeField` to the field parser. This is a direct subclass of `Abc\BaseBundle\Field\DirectValue`, and will hold the field's meta data, to be used by ABC when handling the field (e.g. when showing its value in a module overview). #### The field service Each custom field type must define a field service, which handles the various API tasks for the field type. A field service is a subclass of `\Abc\BackendInterfaceBundle\Form\AbstractField`. This class is abstract by definition, but all of it's methods are optional to implement. In this example we don't need to format any values or anything else. We only need to define the view & overview field. ```php // File: src/Demo/ContentBundle/BackendInterface/Field/YoutubeEmbed/Field.php namespace Demo\ContentBundle\BackendInterface\Field\YoutubeEmbed; /** * Youtube embed field service */ class Field extends \Abc\BackendInterfaceBundle\Form\AbstractField { protected $view = 'youtube_embed'; protected $overviewView = 'youtube_embed'; } ``` #### The form field In this example we create the field itself in Vue JS. For more information how to setup the frontend server with Webpack see the documentation. First we take a look at our `main.js`. As can be seen in the code, we import `Abc` (window.Abc). The `Abc` variable exports libraries & functions which you can reuse in your custom field, but also contains all registered fields, smart modules & widgets. As you can see we merge our imported fields (`overviewFields/index.js`) with the `Abc.fields` ```javascript // File: src/Demo/ContentBundle/Resources/front/src/main.js import Abc from 'abc-manager' import fields from './fields' import overviewFields from './overviewFields' import widgets from './widgets' import smartModules from './smartModules' // Add our fields to the ABC collections. Object.assign(Abc.fields, fields) Object.assign(Abc.overviewFields, overviewFields) Object.assign(Abc.widgets, widgets) Object.assign(Abc.smartModules, smartModules) ``` In the `index.js` we register all custom fields. This is imported by `main.js` and merged by `Abc.fields` as can be seen above in the code. ```javascript // File: src/Demo/ContentBundle/Resources/front/src/overviewFields/index.js import YoutubeEmbed from './YoutubeEmbed' // The keys in this collection match the `overviewView` property // of the field service in PHP. export default { youtube_embed: YoutubeEmbed } ``` In the `YoutubeField.vue` we create the field. You have to add the Abc field mixin in order to use all the props, computed properties & methods that come with a custom field. You can see the full code of the mixin in: `/vendor/angrybytes/abc-manager/src/Abc/BackendInterfaceBundle/Resources/frontend/abc/src/util/mixins/field.js`. If you look at code of the field we debounce the input. This delays the youtube video, otherwhise the youtube video will change on every input. Also it's possible to add your own styling to the field. We advise to set your style on scoped, this will prevent changing any ABC styling. ```javascript // File: src/Demo/ContentBundle/Resources/front/src/overviewFields/YoutubeField.vue ``` The effect of this can be seen in the Youtube module's form field. #### The overview field You can also create your own overview fields (when in_grid="1") as can be seen in `src/Demo/ContentBundle/Resources/front/src/overviewFields/YoutubeEmbed.vue`. You also need to register the overview field on two places. You need to merge the field in the `src/Demo/ContentBundle/Resources/front/src/main.js` and you need to define `$overviewView` in your field. You can put anything you want in an overview, although in this example we just show the youtube image of the video. It's possible to show the youtube video, but you don't want to show all video's in an overview list of 200 records. ```javascript ``` ### Registering the Components Once created, we will have to tell ABC Manager that there is a new parser and field for the `youtube_embed` type. This is done by registering them as **tagged services** into the service container. * A custom field parser should use the tag `cms.base.parser.xml.field` * A custom field should use the tag `$application.cms.interface.field` Take a look at the service definition: ```yaml # File: src/Demo/ContentBundle/Resources/config/services/cms-fields.yml services: # Youtube Embed preview Field demo.cms.module.youtube_embed.field: class: Demo\ContentBundle\BackendInterface\Field\YoutubeEmbed\Field tags: - name: demo.cms.interface.field type: youtube_embed demo.cms.module.youtube_embed.parser: class: Demo\ContentBundle\BackendInterface\Field\YoutubeEmbed\Parser parent: cms.base.parser.xml.field.base tags: - name: cms.base.parser.xml.field type: youtube_embed ``` Once defined, and the container cache has been cleared, ABC Manage will be able to render the custom field.

You will now be able to write your own custom form fields for ABC Manager modules!