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.
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-9\s]");
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.