Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Pro] HTML in Select2 fields #5618

Open
pxpm opened this issue Aug 16, 2024 · 1 comment
Open

[Pro] HTML in Select2 fields #5618

pxpm opened this issue Aug 16, 2024 · 1 comment
Assignees

Comments

@pxpm
Copy link
Contributor

pxpm commented Aug 16, 2024

Discussed in Laravel-Backpack/community-forum#1109

Originally posted by giovdi August 7, 2024
Hi everyone!
Today, I was wondering if it's possible to write HTML into a Select2 dropdown with Backpack. Well, the short answer (I think) is no... but...

I tried looking at the code that generates the Select2 Javascript for my specific case, and it is probably pretty easy to do.

In this example, I added the following code blocks to backpack/pro/resources/views/fields/relationship/select.blade.php.

    // make sure the $field['value'] takes the proper value
    $current_value = old_empty_or_null($field['name'], []) ??  $field['value'] ?? $field['default'] ?? [];

+   if(isset($field['html']) && !is_array($field['html'])) {
+       $field['html'] = [true];
+   }
+   $activeHtml = !empty($field['html']) ? true : false;
+
+   if ($activeHtml) {
+       $field['html']['base_element'] = $field['html']['base_element'] ?? 'span';
+   }

    if (!empty($current_value) || is_int($current_value)) {
        switch (gettype($current_value)) {
        @if($field['multiple'])
        multiple
        @endif

+       data-html="{{ var_export($field['allows_null']) }}"
+       @if($activeHtml)
+         data-html-element="{{ $field['html']['base_element'] }}"
+       @endif
        >
    function bpFieldInitRelationshipSelectElement(element) {
        const $placeholder = element.attr('data-placeholder');
        const $multiple = element.attr('data-field-multiple')  == 'false' ? false : true;
+       const $html = element.attr('data-html')  == 'true' ? true : false;
+       const $htmlElement = element.attr('data-html-element');
        const $allows_null = (element.attr('data-column-nullable') == 'true') ? true : false;
        var $select2Settings = {
                theme: 'bootstrap',
                multiple: $multiple,
                placeholder: $placeholder,
                allowClear: $allowClear,
                dropdownParent: $isFieldInline ? $('#inline-create-dialog .modal-content') : $(document.body)
            };
+           if ($html) {
+               $select2Settings.templateSelection = function(elem) {
+                   if (!elem.id) {
+                       return elem.text;
+                   }
+
+                   return $('<'+$htmlElement+'>'+elem.text+'</'+$htmlElement+'>');
+               }
+               $select2Settings.templateResult = function(elem) {
+                   if (!elem.id) {
+                       return elem.text;
+                   }
+
+                   return $('<'+$htmlElement+'>'+elem.text+'</'+$htmlElement+'>');
+               }
+           }
        if (!$(element).hasClass("select2-hidden-accessible"))

At this point, I can add the following option to the CRUD:

CRUD::field([
	'name' => 'variants',
	'type' => "relationship",
	'inline_create' => true,
	'init_rows' => 1,
	'min_rows' => 1,
	'subfields' => [
		[
			'name' => 'price',
			'label' => 'Price',
			'type' => 'number',
			'prefix' => "&euro;",
			'attributes' => ["step" => "any"],
			'wrapper' => [
				'class' => 'form-group col-md-3'
			],
		],
		[
			'name' => 'weight_initial',
			'label' => 'Weight',
			'type' => 'number',
			'suffix' => "grams",
			'wrapper' => [
				'class' => 'form-group col-md-3'
			],
		],
	],
	'pivotSelect' => [
+		'html' => true,
		'wrapper' => [
			'class' => 'form-group col-md-6'
		],
		'options' => (function ($query) {
			return $query->orderBy('variant_name', 'ASC')->get();
		})
	],
]);

...and with the following accessor:

public function identifiableAttribute()
{
	return 'variant_name_full';
}

protected function variantNameFull(): Attribute
{
	return Attribute::make(
		get: function () {
			$ret = '';
			if (!empty($this->product->filament_material)) {
				$ret .= '<span class="badge badge-primary">'.$this->product->filament_material->filament_material.'</span> ';
			}
			if (!empty($this->attributes['color'])) {
				$ret .= '<span class="badge" style="border:1px solid black; background-color:'.$this->attributes['color'].'">&nbsp;</span>';
			}
			$ret .= " ".$this->product->manufacturer->manufacturer_name . ' - ' . $this->product->product_name . ' - ' . $this->attributes['variant_name'];
			if (!empty($this->code)) {
				$ret .= ' ('.$this->code.')';
			}
			return $ret;

		},
	);
}

...here is the result!
Screenshot 2024-08-07 222805

Not so bad, isn't it?

As you can see from the code blocks, a <span> tag surrounds the HTML option, but you can customize it with the base_element property, as follows: 'html' => ['base_element' => 'div'].
You can't avoid this because if you mix plain text options and HTML ones, the $(elem.text); fails with a JS error, so at least one HTML tag is required.

Please let me know your suggestions... or if I just lost 2 hours of my life for some option that I don't know 😄
Unfortunately, I don't have enough time to create a full PR in a reasonable time, so please, if it's a good idea, take care of it :)

Thanks a lot!

@pxpm pxpm transferred this issue from Laravel-Backpack/community-forum Aug 16, 2024
@pxpm pxpm self-assigned this Aug 16, 2024
@tabacitu
Copy link
Member

I was 1 minute into reading it and was ready to say "NO" but, holy shit... can this be useful! Like for example... when you have a selector for people - to show the avatar too. Or show both the name and their email, but in a nice way, not in the standard John Doe - [email protected].

I like this, I think it's a nice COULD-DO. Buuuut.... I would mean having to do this in all our select2s right 🤦‍♂️ So it would take quite some time to actually implement it in Backpack PRO 😔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

No branches or pull requests

2 participants