I have the following ViewModel on an ASP.NET MVC:
public CategoryNewViewModel {
public Int32 Type { get ; set; }
public IDictionary<String, String> Descriptions { get; set; }
}
The Description Key is the language and Description Value is the text.
On the view I will need to render N inputs, one for each language ...
When the form is submitted the Descriptions property would become:
"en", "The description in english"
"pt", "A descrição em português"
"fr", "La description en français"
One problem is I am not sure how many inputs I will have on the view.
Does anyone knows if this binding is possible?
I think i find nice way of binding dictionary and it's actually what you need in your situation.
In ASP.NET MVC 4, the default model binder will bind dictionaries using the typical dictionary indexer syntax property[key].
If you have a Dictionary<string, string> in your model, you can now bind back to it directly with the following markup:
<input type="hidden" name="MyDictionary[MyKey]" value="MyValue" />
For example, if you want to use a set of checkboxes to select a subset of a dictionary's elements and bind back to the same property, you can do the following:
#foreach(var kvp in Model.MyDictionary)
{
<input type="checkbox" name="#("MyDictionary[" + kvp.Key + "]")"
value="#kvp.Value" />
}
Stolen from this question =)
Related
I am trying to force my asp.net core 3.1 application to use the locale "en-US", no matter the default locale of the Windows server it runs on.
My main goal is to have ModelBinding correctly parse doubles entered by users. So far, I did not have any luck. The comma is interpreted as the decimal separator, whereas the point is interpreted as the thousands separator, which is consistent with the Windows default locale.
So my code looks (simplified) like this:
public class Model
{
public double Percentage {get; set;}
}
[HttpPost]
public ActionResult Index(Model model)
{
Debug.WriteLine(model.Percentage);
}
This results in data entered and posted as "12.34" to be cast to double 1234, etc.
Previously, in ASP.Net MVC 5, adding this to the web.config file solved this issue:
<configuration>
<system.web>
<globalization culture="en-US" uiCulture="en-US" />
I have tried various methods described, which I will list below, but to no avail. These seem to be revolving around localization of the mark-up, but do not affect model binding.
Apply the same settings as listed above.
Adding this to the Startup's ConfigureServices() method:
System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
Adding this to the same method:
services.Configure<RequestLocalizationOptions>(
options =>
{
options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US");
options.SetDefaultCulture("en-US");
});
The latter attempts being merely trial-and-error.
I tried running on IIS and on IIS Express.
I do realize I could write a custom model binder, but I think this adds unneeded complexity. Also, I could skip model binding and parse the post data manually, but the actual models are extensive and contain a lot of data. I do not want to go that way.
Addendum
I could reproduce this behavior with a small demo application. Steps to reproduce:
Create a new web application in Visual Studio .Net 2019 with:
ASP.Net Core Web Application
Web Application (Model-View-Controller)
Add a view model:
public class InputModel
{
[Range(0, 1)]
public double Fraction { get; set; }
}
Replace the index.cshtml with:
#model InputModel
#using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
#Html.LabelFor(m=>m.Fraction)
#Html.TextBoxFor(m=>m.Fraction)
#Html.ValidationMessageFor(m => m.Fraction)
<input type="submit" value="Submit" />
}
Add an action to the conntroller:
[HttpPost]
public IActionResult Index(InputModel input)
{
if (ModelState.IsValid)
{
return Content($"Received fraction: {input.Fraction:E3}");
}
return View(input);
}
On my development Pc:
input 0.5 is invalid and
input 0,5 is valid.
input 0,1 is valid, but the action will output:
Received fraction: 1,000E+000
Adding this to ConfigureServices in Startup.cs:
services.Configure<RequestLocalizationOptions>(
options =>
{
options.SupportedCultures = new List<CultureInfo> { new CultureInfo("en-US") };
options.SupportedUICultures = new List<CultureInfo> { new CultureInfo("en-US") };
options.SetDefaultCulture("en-US");
});
does not make a difference.
Only when I set change settings through Control Panel - Clock and Region - Change date time or number formats, can I make my web application accept the dot as the decimal separator. I do not like having to rely on that.
I am wanting to further my MVC learning. I do not understand why my form won't validate; well, actually I can read the message and it is straightforward enough but I can't find where I'm slipping up.
I'm only including what is relevant to the error message.
My model is this:
[Validator(typeof(ExtForumValidator))]
public partial class ExtrnlSubsModel : BaseEntityModel
{
.....snip......
public int ExtForumBoardId { get; set; }
}
My Validator:
public ExtForumValidator(ILocalizationService localizationService)
{
RuleFor(x => x.ExtForumBoardId)
.NotEqual(0).WithMessage(localizationService.GetResource("ExternalForumBoards.Fields.AvailBoard.Required"));
}
Razor:
<td>
#Html.DropDownListFor(model => model.ExtForumBoardId, Model.AvailableForumBoards)
#Html.RequiredHint()
#Html.ValidationMessageFor(model => model.AvailableForumBoards)
</td>
On submit all my other elements check out except this one has the ModelState error. For attempted value it shows as a string array???? 0,11 or 0,12 or 0,4 etc. It always has a zero and a comma prepended. The second number is correct as that is the id of the item selected in the drop down.
For exception it says null and for error message it says...well it says what I have for the validation message.
So it seems to me that it is getting hung up on this string array 0, stuff and I am at a loss as for where it comes from to go looking for the fix.
I am using Struts 2.0. I have a Java Bean Person having fields perName, perAge, perMail & many more. I want to display a Map of Java Bean {[1, person1], [2, person2]) on JSP and allow the user to update it from the same JSP using text fields. There are lot of variables in the bean and some of them are editable while some of them are not.
For example, perName is not editable while perAge is editable. I don't want to display non editable fields. If I use ediatable fileds only, after updating, I get non editable fields as null (perName is null after update). So I can't specify name attribute as it is changing at run time. So I am putting them in HTTP Session and displaying them. I am specifying name attribute of text field using #session.person[iterator index].perAge. Till this point everything works fine. But if I change the value in any text field & try to update, I get the old session attribute instead of changed one. I want the old session attribute with the new changed values and values of non editable fields should persist within user request. I don't want to use JavaScript. OGNL or expressions are most welcome.
public class PersonDTO implements Serializable {
private String perName;
private int perAge;
public String getPerName() {
return perName;
}
public void setPerName(String perName) {
this.perName = perName;
}
public int getPerAge() {
return perAge;
}
public void setPerAge(int perAge) {
this.perAge = perAge;
}
}
<s:form action="updatePerson" id="updatePerson">
<table>
<tr>
<td>AGE:<s:textfield name="#session.person.perAge" />
</td>
</tr>
<tr>
<td><s:submit id="update" value="Update" />
</td>
</tr>
</table>
</s:form>
Thanks for your support. After a day long GOOGLE, I found that it is not possible to update session directly using Struts tags. You can use session information for display and update session in scriplets but you can't update session directly. You may refer Struts2 form to update object in Session map? for more info. Once again thanks for help!!!
Good morning all. I've been pondering over the best way to use plupload with my mvc web app to marry up uploaded files with their respective record when created on the back-end.
I've finally boiled it down to the following process - this will work for both new and existing records (think CMS):
Create new record, use plupload to upload multiple files.
Multiple files (series of json strings representing each file) are stored on the client then posted back via hidden field.
A property on the model for the form holds the result of these uploaded files so I can handle the back-end business.
The model (without the fluff of unrelated properties) will look like this:
public class Record{
public IList<ClientFile> Files {get;set;}
}
Client file looks like this:
public class ClientFile{
public string UniqueId{get;set;}
}
My problem lies with what I have on the client after each successful upload (a json string of ClientFile) and how I can correctly get this into hidden field and bound correctly so I can have the list of client files ready in my model.
I'm thinking along the lines of either structuring the hidden field(s) right so the binder knows how to wire them up to the list, or having do some some custom deserializing myself to turn the collection of json strings into a list of objects.
Does anyone have any wisdom before I lose my bank holiday weekend to mvc.
This is how I achieve it now:
public class ProductModel
{
// This is the field bound to the view (hidden field)
public string FilesValue
{
get
{
return Files.ToJson<IEnumerable<Plupload>>();
}
set
{
Files = value.FromJson<IEnumerable<Plupload>>();
}
}
public IEnumerable<Plupload> Files { get; private set; }
}
(ToJson & FromJson are just an extension methods I use to serialise and deserialise objects quickly.)
In my view:
<input type="hidden" name="FilesValue" id="FilesValue" />
I have an inline script that converts the value of the hidden field when the form is loaded:
Bundles.AddInlineScript("fileManager.dserialize('" + Html.Raw(Model.FilesValue) + "')");
And the client script which runs before the form is posted to the server:
function preSubmit(e) {
// need to force upload
var $form = $(e.currentTarget),
hidden = $form.find('#FilesValue')[0];
if (uploader.files.length > 0) {
uploader.bind('UploadComplete', function () {
serialize(hidden);
$form.trigger('submit');
});
uploader.start();
return false;
} else {
serialize(hidden);
return true;
}
}
n.b uploader is a reference to plupload and serialize() is a local method which turns the file array into json which gets posted back.
Your client html before post should be like this:
<input type="hidden" name="Files[0].UniqueId" value"XX" />
<input type="hidden" name="Files[1].UniqueId" value"XX" />
<input type="hidden" name="Files[2].UniqueId" value"XX" />
Assuming you have a callback function for each file uploaded with a parameter containg the JSON string you can create dinamically the hiddens with jQuery after uploading each file like this:
....
var i = 0;
....
function(data){
$('form').append('<input type="hidden" name="Files[' +i+'].UniqueId" value"'
+ data.UniqueId + '" />');
i++;
}
In my ASP.NET MVC application, I have a form and I'm using a ViewModel, so the ModelBinder can bind to my Strongly Typed Class. I'm using DataAnnotations for validation
public class FormViewModel
{
[Required]
public string SomeValue {get;set;}
[Range(0, 10, ErrorMessage="Enter a number between 0 and 10.")]
public byte? SomeOtherValue {get;set;}
}
This works great. The problem however is when the user doesn't enter a valid value for the SomeOtherValue (like abc), a standard MVC-error pops up: 'The value 'abc' is not valid for 'SomeOtherValue'. This is really annoying, as I can't customize this message. I know there are ways to Localize this message, but that just doesn't make sense (I don't want a general message, I want a value-specific value).
I tried applying a RegularExpression-attribute to the 'SomeOtherValue', which only allows byte-values, but probably the standard-validation 'overrides' this validation. Is there some way to apply a custom 'the value is not valid' message for a property, or otherwise disable the standard-message?
Here is a different (non-ideal way, IMHO) to fix it if the custom validation attribute is not working for you. In the controller:
if (!ModelState.IsValid)
{
string fieldName = "ThatFieldName";
var m = ViewData.ModelState[fieldName];
if (m != null && m.Errors.Count > 0)
{
ViewData.ModelState.Remove(fieldName);
ViewData.ModelState.AddModelError(fieldName, "You mucked that field up.");
}
}