Create a field that references a library to include on the page.
This is for a component library concept I'm working on. I would like to write html and attach a library to demo how a component can be used.
First, generate a field in a custom module, with a library_id string property. That will create FieldFormatter, FieldType, and FieldWidget plugins.
We need a way to get a list of libraries. I copied this code from Manage Libraries and stripped it down to 2 functions. It extends the core LibraryDiscovery service with use of library_handler and module_handler to get a list of libraries for enabled themes and modules. See the full class file for the overriden LibraryDiscovery.
src/LibraryDiscovery
/**
* {@inheritdoc}
*/
public function getLibraries() {
$libraries = [];
foreach ($this->getEnabledExtensions() as $extension) {
foreach ($this->getLibrariesByExtension($extension) as $library_name => $library_info) {
$library_info['extension'] = $extension;
$library_info['name'] = $library_name;
$libraries[$library_info['extension'] . '/' . $library_info['name']] = $library_info;
}
}
return $libraries;
}
/**
* Returns enabled extensions.
*
* @return array
* List of enabled extension including core.
*/
protected function getEnabledExtensions() {
$enabled_extensions = ['core'];
$listing = new ExtensionDiscovery(DRUPAL_ROOT);
$listing->setProfileDirectories([]);
$extensions = $listing->scan('module', TRUE);
$extensions += $listing->scan('profile', TRUE);
$extensions += $listing->scan('theme', TRUE);
foreach ($extensions as $extension) {
$extension_name = $extension->getName();
// Do not check libraries for disabled extensions.
if ($extension->getType() == 'theme') {
if (!$this->themeHandler->themeExists($extension_name)) {
continue;
}
}
elseif (!$this->moduleHandler->moduleExists($extension_name)) {
continue;
}
$enabled_extensions[] = $extension_name;
}
return $enabled_extensions;
}
The getLibraries() function can be used to populate the field's select list.
src/Plugin/Field/FieldWidget/LibraryWidget
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state): array {
$libraries = $this->libraryManagerDiscovery->getLibraries();
$element['library_id'] = [
'#type' => 'select',
'#options' => array_merge(['' => '-none-'], array_combine(array_keys($libraries), array_keys($libraries))),
'#default_value' => $items[$delta]->library_id ?? NULL
];
...
Now we can use the value to attach the library.
src/Plugin/Field/FieldFormatter/LibraryDefaultFormatter
public function viewElements(FieldItemListInterface $items, $langcode): array {
$element = [];
foreach ($items as $delta => $item) {
if ($item->library_id) {
$element['#attached']['library'][] = $item->library_id;
}
}
}
This is working! I can put rendered markup for an element into a text field, and load the referenced library to display it correctly. This worked for a dropbutton component.
One thing to note is that some libraries extend others and can't be called directly or else the dependencies won't load. Instead load the root library.