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

"Tags" as a formfield #5870

Open
Omais-Rana opened this issue Jun 26, 2024 · 0 comments
Open

"Tags" as a formfield #5870

Omais-Rana opened this issue Jun 26, 2024 · 0 comments
Labels

Comments

@Omais-Rana
Copy link

Laravel version

10.0

PHP version

8.1

Voyager version

1.6

Description of problem

Desperately needed a formfield type that would be helpful in creating tags and I know there could be the approach of storing a string and exploding via separation through commas but that's not neat at all and a bit complex as well. So I made a custom formfield that accepts tags at the time of input and stores it as an array. It works similary to the Multiple Dropdown formfield but instead of having to declare options beforehand and only being able to choose from those, this approach lets you create them dynamically. Its not the cleanest looking but it works. I hope voyager makes a default tags formfield in near future.

Proposed solution

Steps to reproduce:

  1. Make a folder named "FormFields" inside Laravel's "App" folder so the directory looks like this "App/FormFields"
  2. Inside FormFields folder create a php file named "TagsFormField.php" and use this code
<?php
namespace App\FormFields;
use TCG\Voyager\FormFields\AbstractHandler;

class TagsFormField extends AbstractHandler
{
    protected $codename = 'tags';

    public function createContent($row, $dataType, $dataTypeContent, $options)
    {
        return view('voyager::formfields.tags', [
            'row'             => $row,
            'dataType'        => $dataType,
            'dataTypeContent' => $dataTypeContent,
            'options'         => $options,
        ]);
    }
}
  1. Now go to "vendor\tcg\voyager\resources\views\formfields" and create a blade file named "tags.blade.php". Paste this code there
@php
    $uniqueId = uniqid();
    $currentTags = !empty($dataTypeContent->{$row->field}) ? json_decode($dataTypeContent->{$row->field}) : [];
    $currentTags = is_array($currentTags) ? $currentTags : [];
@endphp

<div id="{{ $row->field }}-tag-container-{{ $uniqueId }}">
    @foreach ($currentTags as $tag)
        <span class="badge badge-primary mr-1">
            {{ $tag }}
            <button type="button" class="btn btn-sm btn-danger ml-1"
                onclick="removeTag('{{ $uniqueId }}', '{{ $tag }}')">
                X
            </button>
        </span>
    @endforeach
</div>

<div class="form-group">
    <input type="text" class="form-control" id="{{ $row->field }}-tag-input-{{ $uniqueId }}">
    <input type="hidden" name="{{ $row->field }}" id="{{ $row->field }}-hidden-{{ $uniqueId }}"
        value='{{ json_encode($currentTags) }}'>
    <small class="form-text text-muted">Press Enter to add tags</small>
</div>

<script>
    document.addEventListener('DOMContentLoaded', function() {
        function initializeTagInput(uniqueId) {
            const tagContainer = document.getElementById('{{ $row->field }}-tag-container-' + uniqueId);
            const tagInput = document.getElementById('{{ $row->field }}-tag-input-' + uniqueId);
            const tagsHidden = document.getElementById('{{ $row->field }}-hidden-' + uniqueId);
            let tags = {!! json_encode($currentTags) !!};

            updateTagDisplay();

            tagInput.addEventListener('keydown', function(e) {
                if (e.key === 'Enter' && tagInput.value.trim() !== '') {
                    e.preventDefault();
                    const tag = tagInput.value.trim();
                    if (!tags.includes(tag)) {
                        tags.push(tag);
                        updateTagDisplay();
                    }
                    tagInput.value = '';
                }
            });

            function updateTagDisplay() {
                tagContainer.innerHTML = '';
                tags.forEach(tag => {
                    const tagElement = document.createElement('span');
                    tagElement.classList.add('badge', 'badge-primary', 'mr-1');
                    tagElement.textContent = tag;

                    const removeButton = document.createElement('button');
                    removeButton.type = 'button';
                    removeButton.classList.add('btn', 'btn-sm', 'btn-danger', 'ml-1');
                    removeButton.innerHTML = 'X';
                    removeButton.onclick = function() {
                        removeTag(tag);
                    };

                    tagElement.appendChild(removeButton);
                    tagContainer.appendChild(tagElement);
                });

                tagsHidden.value = JSON.stringify(tags);
            }

            function removeTag(tagToRemove) {
                tags = tags.filter(tag => tag !== tagToRemove);
                updateTagDisplay();
            }

            window.removeTag = function(id, tag) {
                if (id === uniqueId) {
                    removeTag(tag);
                }
            };
        }

        initializeTagInput('{{ $uniqueId }}');
    });
</script>
  1. Now the last step is to go to "App/Providers/AppServiceProvider.php" and paste this code
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\FormFields\TagsFormField;
use TCG\Voyager\Facades\Voyager;
use Illuminate\Support\Facades\View;
use App\Http\ViewComposers\CartComposer;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        //
    }

    /**
     * Bootstrap any application services.
     */
    public function boot()
    {

        Voyager::addFormField(TagsFormField::class);
        View::composer('*', CartComposer::class);
    }
}

Alternatives considered

No response

Additional context

Screenshots attached

Screenshot 2024-06-26 101336

Screenshot 2024-06-26 101455

Screenshot 2024-06-26 101636

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant