1

I have a form with an input that collects a dollar amount (ex: $5,000)

How can I allow an input of $5,000 to not trigger (!ModelState.IsValid) remove comma (,) and $ for POSTing?

ViewModel

public class FormViewModel
{
    [Required, RegularExpression("[0-9,]")]
    public int? Amount { get; set; }
}

View

<form>
  <input asp-for="Amount" />
  <span asp-validation-for="Amount"></span>
  <button type="submit">Submit</button>
</form>

Controller

[HttpPost]
public IActionResult PostForm(FormViewModel viewModel)
{
  if (!ModelState.IsValid)
  {
    return View(viewModel)
  }
  else
  {
  //Post Form
  }
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
jordanpowell88
  • 857
  • 3
  • 11
  • 25

4 Answers4

0

I'd change the View Model to accept a string instead of int? for the Amount property:

public class FormViewModel {
    public string Amount { get; set; }

    public int? Value {
        get {
            int result = 0;
            if (Int32.TryParse(Amount, NumberStyles.Currency, CultureInfo.CurrentCulture, result) {
                return result;
            }
            return null;
        }
    }
}

Now user input is stored in the Amount property, while parsed integer value is stored in Value property.

Sam Axe
  • 33,313
  • 9
  • 55
  • 89
  • This works but how would I reverse this? So I am able to POST but how would I then GET that value and put back into the form? – jordanpowell88 Apr 17 '17 at 14:34
0

Have you tried the IModelBinder?

public class DecimalModelBinder: System.Web.Mvc.IModelBinder
{
    public object BindModel(ControllerContext controllerContext, 
        ModelBindingContext bindingContext)
    {
        ValueProviderResult valueResult = bindingContext.ValueProvider
            .GetValue(bindingContext.ModelName);
        ModelState modelState = new ModelState { Value = valueResult };
        object actualValue = null;
        if (valueResult.AttemptedValue != string.Empty)
        {
            try
            {
                string attemptedVal = valueResult.AttemptedValue?.Replace(",", ".").Replace("$", "");
                actualValue = Convert.ToDecimal(attemptedVal, new CultureInfo("en-US"));
            }
            catch (FormatException e)
            {
                modelState.Errors.Add(e);
            }
        }
        bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
        return actualValue;
    }
}

Itsnot the same but a similar answer to: error with decimal in mvc3 - the value is not valid for field

Community
  • 1
  • 1
Rolando Retana
  • 412
  • 5
  • 16
0

Update the model to use data type annotations.

public class FormViewModel {
    [DataType(DataType.Currency)]
    [Required]
    public int? Amount { get; set; }
}

If you want to include cents then change property to decimal

public class FormViewModel {
    [DataType(DataType.Currency)]
    [Required]
    public decimal? Amount { get; set; }
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
0

I ended up using AutoMapper to map the the properties back and forth.

I changed my ViewModel to a String and then used automapper like below:

//Map From Int To String
CreateMap<Models.ServiceModel, FormViewModel>().ForMember(dest => dest.Amount, opt => opt.MapFrom(src => src.Amount.ToString()));


//Map From String Back To Int
CreateMap<FormViewModel, Models.ServiceModel>().ForMember(dest => dest.Amount, opt => opt.MapFrom(src => int.Parse(src.Amount.Replace(",", ""))));
jordanpowell88
  • 857
  • 3
  • 11
  • 25