Aspnetcore: Blazor form component InputSelect should support binding of type Guid

Created on 3 May 2019  路  13Comments  路  Source: dotnet/aspnetcore

Is your feature request related to a problem? Yes.

Following code does not work if binding Type is Guid. The runtime error in browser console is `InputSelect does not support the type Guid"

// ItemCreate.razor
...
<InputSelect bind-Value="Item.CategoryId">
  @foreach(var cat in Categories) {
    <option value="cat.Id">@cat.Name</option>
  }
</InputSelect>
...
// Entities.cs
...
public class Item
{
  public Guid Id { get; set; }
  public string Name { get; set; }
  public virtual Category Category { get; set; }
  [ForeignKey("Category")]
  public Guid? CategoryId { get; set; }
}

public class Category
{
  public Guid Id { get; set; }
  public string Name { get; set; }
}
...

Describe the solution you'd like

InputSelect should support binding for type Guid.

Describe alternatives you've considered

I have to create custom MyInputSelect on top of InputSelect which supports Guid.

Additional context

Asp.Net Core 3 Preview 4
Browser: Chrome @version:73

Done area-blazor enhancement

Most helpful comment

Guid (or UUID) is used by every modern web app on the face of the planet. It should have first class support everywhere it's possible to use it.

Currently this has to be worked around by adding redundant properties to your model and explicitly invoking Convert on the getter and setter every time you want to use InputSelect based on an object's Guid.

`

class Model : MyObject 
{
    public string MyUidAsString { get; set; }

    override public Guid MyUid
    {
        get { return Guid.TryParse(MyUidAsString, out Guid g) ? g : default; }
        set { MyUidAsString = Convert.ToString(value); }
    }
}

`

This is frustrating and tedious. Not Blazing!

All 13 comments

I have a similar problem using Blazor server-side. I am using integers ID and binding InputSelect to that.
Loading the component works fine, but changing the value in InputSelect causes an exception.

[2019-05-29T02:10:46.461Z] Error: System.InvalidOperationException: Microsoft.AspNetCore.Components.Forms.InputSelect`1[System.Int32] does not support the type 'System.Int32'.
   at Microsoft.AspNetCore.Components.Forms.InputSelect`1.TryParseValueFromString(String value, T& result, String& validationErrorMessage)
   at Microsoft.AspNetCore.Components.Forms.InputBase`1.set_CurrentValueAsString(String value)
   at Microsoft.AspNetCore.Components.BindMethods.<>c__DisplayClass9_0.<SetValueHandler>b__0(UIEventArgs eventArgs)
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Components.Rendering.Renderer.GetErrorHandledTask(Task taskToHandle)

@rynowak was this one of the issues your TypeConverter work fixed?

Guid (or UUID) is used by every modern web app on the face of the planet. It should have first class support everywhere it's possible to use it.

Currently this has to be worked around by adding redundant properties to your model and explicitly invoking Convert on the getter and setter every time you want to use InputSelect based on an object's Guid.

`

class Model : MyObject 
{
    public string MyUidAsString { get; set; }

    override public Guid MyUid
    {
        get { return Guid.TryParse(MyUidAsString, out Guid g) ? g : default; }
        set { MyUidAsString = Convert.ToString(value); }
    }
}

`

This is frustrating and tedious. Not Blazing!

It is a shame that the fix (#17126) was not finished.

I am really curious about the thought process behind only supporting string and enum. It is obvious to me that the integer types, guid and their nullable versions should also be supported

any update on this?

Our company managed to solve this by remaking the entire form system. (Just like those folks over at Syncfu* / Teler / DevExp* with their closed source and proprietary custom components)

I'll just drop this source code, hopefully can be an inspiration for whoever tackling this issue.

using System;
using System.Collections.Generic;
using System.Text;

namespace Accelist.BlazorRAD
{
    /// <summary>
    /// Contains methods to convert string to most CLR basic types.
    /// </summary>
    public static class PowerConvert
    {
        /// <summary>
        /// Converts a string into an typed object.
        /// </summary>
        /// <param name="t"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static object FromString(Type t, string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                if (t.IsValueType)
                {
                    return Activator.CreateInstance(t);
                }
                else
                {
                    return null;
                }
            }

            if (t == typeof(string))
            {
                return value;
            }

            var t2 = Nullable.GetUnderlyingType(t);
            if (t2 != null)
            {
                t = t2;
            }

            if (t == typeof(Guid))
            {
                return Guid.Parse(value);
            }

            // DateTime is IConvertible so no manual conversion is required

            if (t == typeof(DateTimeOffset))
            {
                return DateTimeOffset.Parse(value);
            }

            if (t == typeof(TimeSpan))
            {
                // DateTimeOffset can parse HH:MM:SS but TimeSpan cannot parse ISO 8601!
                var dt = DateTimeOffset.Parse(value);
                return new TimeSpan(dt.Hour, dt.Minute, dt.Second);
            }

            // https://docs.microsoft.com/en-us/dotnet/api/system.iconvertible?view=netcore-3.1
            // https://docs.microsoft.com/en-us/dotnet/api/system.convert.changetype?view=netcore-3.1
            return Convert.ChangeType(value, t);
        }

        /// <summary>
        /// Converts a string into an typed object.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static T FromString<T>(string value)
        {
            return (T)FromString(typeof(T), value);
        }
    }
}

/// <summary>
        /// Converts the property value to string.
        /// If the property type is <see cref="DateTime"/> or <see cref="DateTimeOffset"/>, convert it into ISO 8601 format.
        /// If the property type is <see cref="DateTime"/> with unknown time zone, treat it as UTC Time Zone.
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public string GetValueAsString(object model)
        {
            var value = Property.GetValue(model);
            if (value is DateTime dt)
            {
                if (dt.Kind == DateTimeKind.Unspecified)
                {
                    dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc);
                }
                return dt.ToString("o");
            }
            if (value is DateTimeOffset dto)
            {
                return dto.ToString("o");
            }
            return value?.ToString();
        }

Basically what happens is that because most basic HTML form inputs for Blazor works just fine when used with string instead of other data types, the implementer should perform a chained bind like this:

Native HTML input <----> C# string <----> Developer Data Type

That way, whenever the input is mutated from the HTML, it can be converted into the desired data type via string conversion first.

On the other way, the value of the actual data type chosen by the developer (For example: GUID, integer, etc.) should first be converted into string before being bound to the native HTML input.

This way, Blazor input controls can be used with any basic data types.

Is there any update on this?

Ran into this today when starting (and subsequently abandoning) our plan to replace our MVC forms with Blazor Components.

It seems someone lied to us and said that Blazor was production-ready.

Ran into this today when starting (and subsequently abandoning) our plan to replace our MVC forms with Blazor Components.

It seems someone lied to us and said that Blazor was production-ready.

@valuator18, for info Blazor is definitely production ready, we've have a large server-side Blazor app running in production for a few months now.

There are several ways around this particular issue (some discussed above) and third party controls that have this functionality. Alternatively you can just declare a derived property that converts your Guid property to a string and bind to that instead.

@danroth27 It seems that Guids and integers should have been supported from the onset. Now I have to tell anyone I recommend Blazor to about this issue which should have been baked in from the start. I feel like Blazor is not production-ready with such issues.

@ryanelian I also had to implement a workaround to make it work with GUID. I would really appreciate it being possible to link Guid-type data. I hope a solution will be part of 5.0

Was this page helpful?
0 / 5 - 0 ratings