Laravel-Backpack / CRUD

Build custom admin panels. Fast!
https://backpackforlaravel.com
MIT License
3.18k stars 897 forks source link

[Pro] HTML in Select2 fields #5618

Open pxpm opened 3 months ago

pxpm commented 3 months ago

Discussed in https://github.com/Laravel-Backpack/community-forum/discussions/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`. ```diff // 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)) { ``` ```diff @if($field['multiple']) multiple @endif + data-html="{{ var_export($field['allows_null']) }}" + @if($activeHtml) + data-html-element="{{ $field['html']['base_element'] }}" + @endif > ``` ```diff 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; ``` ```diff 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+''); + } + $select2Settings.templateResult = function(elem) { + if (!elem.id) { + return elem.text; + } + + return $('<'+$htmlElement+'>'+elem.text+''); + } + } if (!$(element).hasClass("select2-hidden-accessible")) ``` At this point, I can add the following option to the CRUD: ```diff CRUD::field([ 'name' => 'variants', 'type' => "relationship", 'inline_create' => true, 'init_rows' => 1, 'min_rows' => 1, 'subfields' => [ [ 'name' => 'price', 'label' => 'Price', 'type' => 'number', 'prefix' => "€", '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: ```php public function identifiableAttribute() { return 'variant_name_full'; } protected function variantNameFull(): Attribute { return Attribute::make( get: function () { $ret = ''; if (!empty($this->product->filament_material)) { $ret .= ''.$this->product->filament_material->filament_material.' '; } if (!empty($this->attributes['color'])) { $ret .= ' '; } $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 `` 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!
tabacitu commented 2 months ago

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 - john.doe@example.com.

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 😔