How to create custom view fields programmatically: Unfiltered header area field and content field plugins

How to create custom view fields programmatically: Unfiltered header area field and content field plugins

If you ever tried to add custom styles or custom javascript to Unfiltered text field in Drupal 8 view, you had probably noticed that the style/script tags are always stripped out. Here is the example of how to programmatically create the unfiltered view header area custom field and the unfiltered view content field plugins where you can use <style> or <script> tags. 

Let's create the custom text_css_field module where we will use field twig template for the area field and hook_preprocess_views_view_field for the second field.

The final result is shown on the image below, where these view fields got its styling defined in the same view.

View results
View results.


Custom Javascript code also creates its console entries:

Console log
Checking console log.

Create a module

First of all, create custom/text_css_field folder inside your Drupal 8 installation modules folder and put inside the following files: text_css_field.info.yml, text_css_field.module and text_css_field.views.inc.

In the info file we just define the module name, package, description and core.

text_css_field.info.yml

name: Text CSS Field
type: module
description: Custom Text/CSS views field
core: 8.x
package: Custom

In our module file we define the theme for the header area field which will accept one parameter, then header area template_preprocess function (I left it empty so you can add your own logic there) and the hook_views_view_preprocess_field which we will use for rewriting the output of our custom view content field.

text_css_field.module

<?php

/**
 * Implements hook_theme().
 */
function text_css_field_theme($existing, $type, $theme, $path) {
  return [
    'text_css_header' => [
      'variables' => [
        'items' => NULL,
      ],
    ],
  ];
}

/**
 * Implements template_preprocess_HOOK().
 */
function template_preprocess_text_css_header(&$variables) {
  // Do your custom preprocess logic here for the field.
  // Add custom variables if you want them to be available in twig template:
  // $variables['custom_variable'] = "Some data";
}

/**
 * Implements hook_preprocess_views_view_field().
 */
function text_css_field_preprocess_views_view_field(&$variables) {
  $field = $variables['field'];
  if ($field->field == 'text_css_content') {
    $result = $field->options['text_css_content'];
    $variables['output'] = Drupal\Core\Render\Markup::create($result); // saving value
  }
}

The third file text_css_field.views.inc will contain the definition settings of our view fields.

text_css_field.views.inc

<?php

/**
 * Implements hook_views_data().
 */
function text_css_field_views_data() {
  $data['views']['text_css_header'] = array(
    'title' => t('Unfiltered Text/CSS area field'),
    'help' => t('Add unrestricted, custom text or CSS markup.'),
    'area' => array(
      'id' => 'text_css_header',
    ),
  );
  $data['views']['text_css_content'] = [   // global group
    'field' => [
      'title' => t('Unfiltered Text/CSS content field'),
      'help' => t('Unfiltered text or CSS markup.'),
      'id' => 'text_css_content',
      'click sortable' => FALSE,
    ],
  ];
  return $data;
}

We will also need to create a few more files and folders according to the structure shown on the screenshot below.

Structure of the module
Folder structure of the text_css_field module.


Create TextCssHeader.php file under the following path: module's root folder/src/Plugin/views/area.
In this file we will set up our view area field handler which will have the module's custom namespace and fied custom class that extends the standard AreaPluginBase class.
Inside that class we set up the options form settings for our area field and its render function. The output of the render function (#items value) we pass to the field's defined template, which will be set in templates/text-css-header.html.twig

Make sure that you put the proper header field ID into  * @ViewsArea("text_css_header") otherwise it will not be picked up by the Drupal core.

TextCssHeader.php

<?php

namespace Drupal\text_css_field\Plugin\views\area;

use Drupal\views\Plugin\views\area\AreaPluginBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * Defines a views area plugin.
 *
 * @ingroup views_area_handlers
 *
 * @ViewsArea("text_css_header")
 */
class TextCssHeader extends AreaPluginBase {

  /**
   * {@inheritdoc}
   */
  public function query() {

  }

  /**
   * {@inheritdoc}
   */
  protected function defineOptions() {
    $options = parent::defineOptions();
    $options['text_css_header'] = ['default' => ''];
    return $options;
  }

  /**
   * {@inheritdoc}
   */
  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    $form['text_css_header'] = [
      '#type' => 'textarea',
      '#required' => TRUE,
      '#title' => $this->t('Text CSS field'),
      '#description' => $this->t('Use &#60;style&#62;&#60;/style&#62; tags to include your custom css styles.'),
      '#default_value' => $this->options['text_css_header'],
    ];
    parent::buildOptionsForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function render($empty = FALSE) {
    if (!$empty || !empty($this->options['empty'])) {
      $result = $this->options['text_css_header'];
      return [
        '#theme' => 'text_css_header',
        '#items' => $result,
      ];
    }
  }
}

The similar logic is applied to the second field handler class, but under module's root folder/src/Plugin/views/field.

We extend the standard FiledPluginBase class and override field options form. We do not assign the output of render function to the theme template, we will do our processing in the hook_views_view_preprocess_field instead. Also, notice @ViewsField("text_css_content") part, it should contain proper ID of the field, which is text_css_content in our case, defined in text_css_field.views.inc file.

TextCssContent.php

<?php

namespace Drupal\text_css_field\Plugin\views\field;

use Drupal\Core\Form\FormStateInterface;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\ResultRow;

/**
 * @ingroup views_field_handlers
 *
 * @ViewsField("text_css_content")
 */
class TextCSSContent extends FieldPluginBase {

  /**
   * {@inheritdoc}
   */
  public function query() {

  }

  /**
   * {@inheritdoc}
   */
  protected function defineOptions() {
    $options = parent::defineOptions();
    $options['text_css_content'] = ['default' => ''];;
    return $options;
  }

  /**
   * {@inheritdoc}
   */
  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    $form['text_css_content'] = [
      '#type' => 'textarea',
      '#required' => TRUE,
      '#title' => $this->t('Text CSS field'),
      '#description' => $this->t('Use &#60;style&#62;&#60;/style&#62; tags to include your custom css styles.'),
      '#default_value' => $this->options['text_css_content'],
    ];
    parent::buildOptionsForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function render(ResultRow $values) {
    $result = "";
    if (!empty($this->view->field['text_css_content'])) {
      $result = $this->view->field['text_css_content']->options['text_css_content'];
    }
    return [
      '#markup' => $result,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function clickSort($order) {
    $this->query->addOrderBy('text_css_content', 'created', $order);
  }

}

After all, create templates folder under module root and put header field template file there. It will contain the raw field output in order to avoid stripping out of our styles and script tags by Drupal 8 twig template engine.

text-css-header.html.twig

{{ items|raw }}

That's all. Enable the module, create a view and test your fields. You can navigate them under Global field group.

Header area field
Header area custom field.

 

Content field
Content custom field.

 

Add new comment

CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.