Skip to content
Advertisement

Blazor autocomplete tagging

Similar to this question, I am looking for a component to add tags to an object like in stackoverflow, meaning autocomplete text field for tags. I am either looking for a native component or a way to wrap a JS solution so that it can be used in blazor.

Another idea is a multi-select component with checkboxes like discussed here, but I do not really like the idea.

Ideally I would provide a list of all tags and bind to a list on an item for which the tags are being set.

Advertisement

Answer

Usually, it’s not right practice to share full code as SO is not a code sharing site and it’s generally asked What you have tried so far?. However since Blazor is a new technology thus beginners struggle with finding a good solution or a plugin on web to meet their requirement thus I’m considering this as an exception.

Now answer to your question Creating a new component for adding tags. You can use below solution that I created in one of the project I’m working on. This doesn’t require any JS and can be handled with C# only to create a Tag. You can also check the Blazor Fiddle solution in action that I have prepared for you on fiddle. Hope this is what you are looking for.

@using System;
@using System.Collections.Generic;
@using System.Linq;
@using System.Text.RegularExpressions;

<style>

.suggestion-container {
    position: relative;
}

.tagsinput, .tagsinput * {
    box-sizing: border-box
}

.tagsinput {
    display: -webkit-box;
    display: -webkit-flex;
    display: -ms-flexbox;
    display: flex;
    -webkit-flex-wrap: wrap;
    -ms-flex-wrap: wrap;
    flex-wrap: wrap;
    background: #fff;
    font-size: 14px;
    line-height: 20px;
    color: #556270;
    padding: 5px 5px 0;
    border: 1px solid #e6e6e6;
    border-radius: 2px
}

    .tagsinput.focus {
        border-color: #ccc
    }

    .tagsinput .tag {
        position: relative;
        background: #556270;
        display: block;
        max-width: 100%;
        word-wrap: break-word;
        color: #fff;
        padding: 5px 30px 5px 5px;
        border-radius: 2px;
        margin: 0 5px 5px 0
    }

        .tagsinput .tag .tag-remove {
            position: absolute;
            background: 0 0;
            display: block;
            width: 30px;
            height: 30px;
            top: 0;
            right: 0;
            cursor: pointer;
            text-decoration: none;
            text-align: center;
            color: #ff6b6b;
            line-height: 30px;
            padding: 0;
            border: 0
        }

            .tagsinput .tag .tag-remove:after, .tagsinput .tag .tag-remove:before {
                background: #ff6b6b;
                position: absolute;
                display: block;
                width: 10px;
                height: 2px;
                top: 14px;
                left: 10px;
                content: ''
            }

            .tagsinput .tag .tag-remove:before {
                -webkit-transform: rotateZ(45deg);
                transform: rotateZ(45deg)
            }

            .tagsinput .tag .tag-remove:after {
                -webkit-transform: rotateZ(-45deg);
                transform: rotateZ(-45deg)
            }

    .tagsinput div {
        -webkit-box-flex: 1;
        -webkit-flex-grow: 1;
        -ms-flex-positive: 1;
        flex-grow: 1
    }

        .tagsinput div input {
            background: 0 0;
            display: block;
            width: 100%;
            font-size: 14px;
            line-height: 20px;
            padding: 5px;
            border: 0;
            margin: 0 5px 5px 0
        }

            .tagsinput div input:focus {
                color: #495057;
                background-color: #fff;
                border-color: #80bdff;
                outline: 0;
                box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25);
            }

            .tagsinput div input.error {
                color: #ff6b6b
            }

            .tagsinput div input::-ms-clear {
                display: none
            }

            .tagsinput div input::-webkit-input-placeholder {
                color: #ccc;
                opacity: 1
            }

            .tagsinput div input:-moz-placeholder {
                color: #ccc;
                opacity: 1
            }

            .tagsinput div input::-moz-placeholder {
                color: #ccc;
                opacity: 1
            }

            .tagsinput div input:-ms-input-placeholder {
                color: #ccc;
                opacity: 1
            }
</style>

<div class="suggestion-container w-75">
    <div id="@($"{Id}_tagsinput")" class="tagsinput">

        @if (Tags != null && Tags.Any())
        {
            @foreach (var tag in Tags)
            {
                <span class="tag">
                    <span class="tag-text">@tag</span>
                    <span class="tag-remove" @onclick="() => DeleteTag(tag)" />
                </span>
            }
        }

        <div id="@($"{Id}_addTag")">
            <div class="@(IsContainSpecialCharacter ? "tag-tooltip" : string.Empty)">
                <input id="@($"{Id}_tag")"
                       class="tag-input"
                       placeholder="Add tags"
                       autocomplete="off"
                       @bind-value="Value"
                       @bind-value:event="oninput"
                       @onkeyup="AddTags" />

                @if (IsContainSpecialCharacter)
                {
                    <div class="error-right d-inline-flex p-2">
                        <i class="oi oi-warning text-warning p-1"></i>
                        <p class="text-left m-0 p-1">Special characters not allowed.</p>
                        <i></i>
                    </div>
                }
            </div>
        </div>
    </div>
</div>

@code{

    private Guid Id => Guid.NewGuid();
    protected string Value { get; set; }
    protected bool MenuVisibility { get; set; }
    protected bool IsContainSpecialCharacter { get; set; }
    protected List<string> Tags { get; set; } = new List<string>();

        protected void AddTags(KeyboardEventArgs eventArgs)
        {
            IsContainSpecialCharacter = false;

            if (!String.IsNullOrEmpty(Value))
            {
                if (eventArgs.Key.Equals("Enter"))
                {
                    var regex = new Regex(@"[^a-zA-Z0-9s]");
                    if (!regex.IsMatch(Value))
                    {
                        if (!Tags.Exists(t => t.Equals(Value, StringComparison.CurrentCultureIgnoreCase)))
                        {
                            Tags.Add(Value);
                        }

                        Value = string.Empty;
                    }
                    else
                    {
                        IsContainSpecialCharacter = true;
                    }
                }
            }       
        }
    
        protected void DeleteTag(string value)
        {
            if (String.IsNullOrEmpty(value)) return;

            var tag = Tags.FirstOrDefault(t => t == value);
            if (tag == null) return;

            Tags.Remove(tag);
        }
}

P.S. What I’ve shared here is only a code snippet of building tags, it doesn’t not contain tags with autocomplete option as it would require time to create a full solution on fiddle with fake data. Thus I’m avoiding that due to time limitation.

Advertisement