Ошибка проверки клиента с полями ввода, содержащими значения валют - PullRequest
0 голосов
/ 24 февраля 2019

Я занимаюсь разработкой веб-приложения с использованием Asp.Net Core 2.1 и Code First. У меня есть набор свойств типа decimal и они украшены следующим атрибутом:

        [DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = true)]

Проблемакогда форма переходит в режим редактирования, проверка клиента выдает следующую ошибку, поскольку поле ввода содержит символ валюты:

Поле должно быть числом.

Как я могу сказать ядру asp.net обрабатывать поле ввода с символом валюты как десятичное значение?

Ответы [ 2 ]

0 голосов
/ 26 февраля 2019

Попробуйте создать пользовательское связующее для модели , которое преобразует (скажем) "15 481" в десятичное число.

Результат, который вы ищете, заключается в проверке ввода как валюты, а не какдесятичное число, но у вас могут возникнуть другие потребности в очистке ввода перед привязкой модели, поэтому вы будете использовать интерфейс, описывающий действие Scrub.

 public interface IScrubberAttribute
{
    object Scrub(string modelValue, out bool success);
}

Далее, добавьте CurrencyScrubberAttribute, который будет выполнять работупарсинг пользовательского ввода, чтобы увидеть, является ли он допустимым форматом валюты.В C # decimal.TryParse есть перегрузка, которая принимает NumberStyle и CultureInfo, то есть как сделать проверку валюты.Вы заметите, что в настоящее время это работает только с американской валютой ($), но просто потребует установки CultureInfo для обработки других валют.

[AttributeUsage(AttributeTargets.Property)]
public class CurrencyScrubberAttribute : Attribute, IScrubberAttribute
{
    private static NumberStyles _currencyStyle = NumberStyles.Currency;
    private CultureInfo _culture = new CultureInfo("en-US");

    public object Scrub(string modelValue, out bool success)
    {
        var modelDecimal = 0M;
        success = decimal.TryParse(
            modelValue,
            _currencyStyle,
            _culture,
            out modelDecimal
        );
        return modelDecimal;
    }
}

Использование нового CurrencyScrubberAttribute, как показано ниже:

 public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }

    [DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = true)]
    [CurrencyScrubber]
    public decimal Price { get; set; }
}

Добавить модель переплета.В случае строго типизированной модели, такой как Product, ComplexTypeModelBinderProvider принимает вызов, а затем создает связыватель для каждого свойства.

 public class ScrubbingModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
            throw new ArgumentNullException(nameof(context));

        if (!context.Metadata.IsComplexType&&context.Metadata.PropertyName!=null)
        {
            // Look for scrubber attributes
            var propName = context.Metadata.PropertyName;
            var propInfo = context.Metadata.ContainerType.GetProperty(propName);

            // Only one scrubber attribute can be applied to each property
            var attribute = propInfo.GetCustomAttributes(typeof(IScrubberAttribute), false).FirstOrDefault();
            if (attribute != null)
                return new ScrubbingModelBinder(context.Metadata.ModelType, attribute as IScrubberAttribute);
        }
        return null;
    }
}

Связыватель модели будет обрабатывать простые типы, которые имеютIScrubberAttribute, но если по какой-либо причине мы не будем иметь дело с привязкой, мы передадим ее SimpleTypeModelBinder для ее обработки.

public class ScrubbingModelBinder : IModelBinder
{
    IScrubberAttribute _attribute;
    SimpleTypeModelBinder _baseBinder;

    public ScrubbingModelBinder(Type type, IScrubberAttribute attribute)
    {
        if (type == null) throw new ArgumentNullException(nameof(type));

        _attribute = attribute as IScrubberAttribute;
        _baseBinder = new SimpleTypeModelBinder(type);
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));

        // Check the value sent in
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (valueProviderResult != ValueProviderResult.None)
        {
            bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);

            // Attempt to scrub the input value
            var valueAsString = valueProviderResult.FirstValue;
            var success = true;
            var result = _attribute.Scrub(valueAsString, out success);
            if (success)
            {
                bindingContext.Result = ModelBindingResult.Success(result);
                return Task.CompletedTask;
            }
        }
        // If we haven't handled it, then we'll let the base SimpleTypeModelBinder handle it
        return _baseBinder.BindModelAsync(bindingContext);
    }
}

Добавьте ее в ConfigureServices

services.AddMvc(options =>
        {
            options.ModelBinderProviders.Insert(0, new ScrubbingModelBinderProvider());
        });
0 голосов
/ 25 февраля 2019

Текст валюты может быть показан в отдельном месте, а данные могут быть показаны в текстовом поле.

Вот код для того же самого.

В модели

 public class Test
{
        [Key]
        [MaxLength(30)]
        public string Id { get; set; }

        public string Name { get; set; }

        [DataType(DataType.Currency)]
        [DisplayFormat(DataFormatString = "{0:C0}", ApplyFormatInEditMode = true)]
        public float? Cost { get; set; }


}

В представлении

@model CoreCodeFist.Models.Dataobj.Test
@{
    ViewData["Title"] = "Home Page";
}
<div class="row">    
    <div class="col-md-3">
        <form asp-action="Index">
            <h2>Test</h2>
            <div class="input-group">
                <span class="input-group-addon">@string.Format("{0:C}", Model.Cost!=null?Model.Cost:0).FirstOrDefault()</span>
                <input asp-for="Cost" asp-format="{0}" class="form-control" />
            </div>
            <div class="input-group">
                <span asp-validation-for="Cost" class="text-danger"></span>
            </div>
            <br />
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
            </form>
</div>

</div>

В контроллере

//First Page Load Method In Initialize Model Because Cost Is Initialize
        public IActionResult Index()
        {

            return View(new Test());
        }
        //This Is Edit Method 
        public IActionResult Edit(float id)
        {
            Test t = new Test()
            {
                Cost = id
            };
            return View("Index",t);
        }
        //First Page Post Method
        [HttpPost]
        public IActionResult Index(Test model)
        {
            return View(model);
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...