Merge pull request #10033 from creative-commoners/pulls/4/attributeshtml-trait

API Add an AttributesHTML trait
This commit is contained in:
Maxime Rainville 2021-11-18 17:02:19 +13:00 committed by GitHub
commit 2d4b93883b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 144 additions and 194 deletions

View File

@ -20,6 +20,7 @@ use SilverStripe\ORM\FieldType\DBHTMLText;
use SilverStripe\ORM\ValidationResult; use SilverStripe\ORM\ValidationResult;
use SilverStripe\Security\NullSecurityToken; use SilverStripe\Security\NullSecurityToken;
use SilverStripe\Security\SecurityToken; use SilverStripe\Security\SecurityToken;
use SilverStripe\View\AttributesHTML;
use SilverStripe\View\SSViewer; use SilverStripe\View\SSViewer;
use SilverStripe\View\ViewableData; use SilverStripe\View\ViewableData;
@ -66,10 +67,10 @@ use SilverStripe\View\ViewableData;
*/ */
class Form extends ViewableData implements HasRequestHandler class Form extends ViewableData implements HasRequestHandler
{ {
use AttributesHTML;
use FormMessage; use FormMessage;
/** /**
* Default form Name property
*/ */
const DEFAULT_NAME = 'Form'; const DEFAULT_NAME = 'Form';
@ -816,33 +817,7 @@ class Form extends ViewableData implements HasRequestHandler
return $this; return $this;
} }
/** protected function getDefaultAttributes(): array
* @param string $name
* @param string $value
* @return $this
*/
public function setAttribute($name, $value)
{
$this->attributes[$name] = $value;
return $this;
}
/**
* @param string $name
* @return string
*/
public function getAttribute($name)
{
if (isset($this->attributes[$name])) {
return $this->attributes[$name];
}
return null;
}
/**
* @return array
*/
public function getAttributes()
{ {
$attrs = [ $attrs = [
'id' => $this->FormName(), 'id' => $this->FormName(),
@ -860,53 +835,9 @@ class Form extends ViewableData implements HasRequestHandler
$attrs['class'] .= ' validationerror'; $attrs['class'] .= ' validationerror';
} }
$attrs = array_merge($attrs, $this->attributes);
return $attrs; return $attrs;
} }
/**
* Return the attributes of the form tag - used by the templates.
*
* @param array $attrs Custom attributes to process. Falls back to {@link getAttributes()}.
* If at least one argument is passed as a string, all arguments act as excludes by name.
*
* @return string HTML attributes, ready for insertion into an HTML tag
*/
public function getAttributesHTML($attrs = null)
{
$exclude = (is_string($attrs)) ? func_get_args() : null;
$attrs = $this->getAttributes();
// Remove empty
$attrs = array_filter((array)$attrs, function ($value) {
return ($value || $value === 0);
});
// Remove excluded
if ($exclude) {
$attrs = array_diff_key($attrs, array_flip($exclude));
}
// Prepare HTML-friendly 'method' attribute (lower-case)
if (isset($attrs['method'])) {
$attrs['method'] = strtolower($attrs['method']);
}
// Create markup
$parts = [];
foreach ($attrs as $name => $value) {
if ($value === true) {
$value = $name;
}
$parts[] = sprintf('%s="%s"', Convert::raw2att($name), Convert::raw2att($value));
}
return implode(' ', $parts);
}
public function FormAttributes() public function FormAttributes()
{ {
return $this->getAttributesHTML(); return $this->getAttributesHTML();

View File

@ -13,6 +13,7 @@ use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\ORM\FieldType\DBField; use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\ORM\FieldType\DBHTMLText; use SilverStripe\ORM\FieldType\DBHTMLText;
use SilverStripe\ORM\ValidationResult; use SilverStripe\ORM\ValidationResult;
use SilverStripe\View\AttributesHTML;
use SilverStripe\View\SSViewer; use SilverStripe\View\SSViewer;
/** /**
@ -41,6 +42,7 @@ use SilverStripe\View\SSViewer;
*/ */
class FormField extends RequestHandler class FormField extends RequestHandler
{ {
use AttributesHTML;
use FormMessage; use FormMessage;
/** @see $schemaDataType */ /** @see $schemaDataType */
@ -214,17 +216,6 @@ class FormField extends RequestHandler
*/ */
protected $smallFieldHolderTemplate; protected $smallFieldHolderTemplate;
/**
* All attributes on the form field (not the field holder).
*
* Partially determined based on other instance properties.
*
* @see getAttributes()
*
* @var array
*/
protected $attributes = [];
/** /**
* The data type backing the field. Represents the type of value the * The data type backing the field. Represents the type of value the
* form expects to receive via a postback. Should be set in subclasses. * form expects to receive via a postback. Should be set in subclasses.
@ -659,59 +650,7 @@ class FormField extends RequestHandler
return $this; return $this;
} }
/** protected function getDefaultAttributes(): array
* Set an HTML attribute on the field element, mostly an input tag.
*
* Some attributes are best set through more specialized methods, to avoid interfering with
* built-in behaviour:
*
* - 'class': {@link addExtraClass()}
* - 'title': {@link setDescription()}
* - 'value': {@link setValue}
* - 'name': {@link setName}
*
* Caution: this doesn't work on most fields which are composed of more than one HTML form
* field.
*
* @param string $name
* @param string $value
*
* @return $this
*/
public function setAttribute($name, $value)
{
$this->attributes[$name] = $value;
return $this;
}
/**
* Get an HTML attribute defined by the field, or added through {@link setAttribute()}.
*
* Caution: this doesn't work on all fields, see {@link setAttribute()}.
*
* @param string $name
* @return string
*/
public function getAttribute($name)
{
$attributes = $this->getAttributes();
if (isset($attributes[$name])) {
return $attributes[$name];
}
return null;
}
/**
* Allows customization through an 'updateAttributes' hook on the base class.
* Existing attributes are passed in as the first argument and can be manipulated,
* but any attributes added through a subclass implementation won't be included.
*
* @return array
*/
public function getAttributes()
{ {
$attributes = [ $attributes = [
'type' => $this->getInputType(), 'type' => $this->getInputType(),
@ -729,67 +668,9 @@ class FormField extends RequestHandler
$attributes['aria-required'] = 'true'; $attributes['aria-required'] = 'true';
} }
$attributes = array_merge($attributes, $this->attributes);
$this->extend('updateAttributes', $attributes);
return $attributes; return $attributes;
} }
/**
* Custom attributes to process. Falls back to {@link getAttributes()}.
*
* If at least one argument is passed as a string, all arguments act as excludes, by name.
*
* @param array $attributes
*
* @return string
*/
public function getAttributesHTML($attributes = null)
{
$exclude = null;
if (is_string($attributes)) {
$exclude = func_get_args();
}
if (!$attributes || is_string($attributes)) {
$attributes = $this->getAttributes();
}
$attributes = (array) $attributes;
$attributes = array_filter($attributes, function ($v) {
return ($v || $v === 0 || $v === '0');
});
if ($exclude) {
$attributes = array_diff_key(
$attributes,
array_flip($exclude)
);
}
// Create markup
$parts = [];
foreach ($attributes as $name => $value) {
if ($value === true) {
$value = $name;
} else {
if (is_scalar($value)) {
$value = (string) $value;
} else {
$value = json_encode($value);
}
}
$parts[] = sprintf('%s="%s"', Convert::raw2att($name), Convert::raw2att($value));
}
return implode(' ', $parts);
}
/** /**
* Returns a version of a title suitable for insertion into an HTML attribute. * Returns a version of a title suitable for insertion into an HTML attribute.
* *

138
src/View/AttributesHTML.php Normal file
View File

@ -0,0 +1,138 @@
<?php
namespace SilverStripe\View;
use SilverStripe\Core\Convert;
/**
* This trait can be applied to a ViewableData class to add the logic to render attributes in an SS template.
*
* When applying this trait to a class, you also need to add the following casting configuration.
* ```
* private static $casting = [
* 'AttributesHTML' => 'HTMLFragment',
* 'getAttributesHTML' => 'HTMLFragment',
* ];
* ```
*/
trait AttributesHTML
{
/**
* List of attributes to render on the frontend
* @var array
*/
protected $attributes = [];
/**
* Set an HTML attribute
* @param $name
* @param $value
* @return $this
*/
public function setAttribute($name, $value)
{
$this->attributes[$name] = $value;
return $this;
}
/**
* Retrieve the value of an HTML attribute
* @param string $name
* @return mixed|null
*/
public function getAttribute($name)
{
$attributes = $this->getAttributes();
if (isset($attributes[$name])) {
return $attributes[$name];
}
return null;
}
/**
* Get the default attributes when rendering this object.
*
* Called by `getAttributes()`
*
* @return array
*/
abstract protected function getDefaultAttributes(): array;
/**
* Allows customization through an 'updateAttributes' hook on the base class.
* Existing attributes are passed in as the first argument and can be manipulated,
* but any attributes added through a subclass implementation won't be included.
*
* @return array
*/
public function getAttributes()
{
$defaultAttributes = $this->getDefaultAttributes();
$attributes = array_merge($defaultAttributes, $this->attributes);
if (method_exists($this, 'extend')) {
$this->extend('updateAttributes', $attributes);
}
return $attributes;
}
/**
* Custom attributes to process. Falls back to {@link getAttributes()}.
*
* If at least one argument is passed as a string, all arguments act as excludes, by name.
*
* @param array $attributes
*
* @return string
*/
public function getAttributesHTML($attributes = null)
{
$exclude = null;
if (is_string($attributes)) {
$exclude = func_get_args();
}
if (!$attributes || is_string($attributes)) {
$attributes = $this->getAttributes();
}
$attributes = (array) $attributes;
$attributes = array_filter($attributes, function ($v) {
return ($v || $v === 0 || $v === '0');
});
if ($exclude) {
$attributes = array_diff_key(
$attributes,
array_flip($exclude)
);
}
// Create markup
$parts = [];
foreach ($attributes as $name => $value) {
if ($value === true) {
$value = $name;
} else {
if (is_scalar($value)) {
$value = (string) $value;
} else {
$value = json_encode($value);
}
}
$parts[] = sprintf('%s="%s"', Convert::raw2att($name), Convert::raw2att($value));
}
return implode(' ', $parts);
}
}