Custom validating a Model object in MVC - c#

I I need to validate some thing in mvc in my controller I have to
#Html.ValidationMessageFor(model => model.somestring)// in the view
if (model.string = some condition)
ModelState.AddModelError("somestring", "String cannot be empty");// in the controller
but if in my view I have a custom object like
#Html.ValidationMessageFor(model => model.someobject.somestring)// in the view
how do I validate it? Is the following syntax correct?
if (model.someobject.somestring = some condition)
ModelState.AddModelError("somestring", "String cannot be empty");// in the controller

You need to make sure the full path to your property is used when specifying your key:
ModelState.AddModelError("someobject.somestring", "String cannot be empty);

Related

ASP.NET MVC GetFullHtmlFieldId not returing valid id

I have taken a look at but it did not help me out
GetFullHtmlFieldId returning incorrect id attribute value
ASP.NET GetFullHtmlFieldId not returning valid id
Problem
Basically I have the following problem:
I have a custom validation attribute which requires to get the fieldId of the control
public class MyValidationAttribute : ValidationAttribute, IClientValidatable
{
//...... Collapsed code
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ViewContext vwContext = context as ViewContext;
var fieldId = vwContext.ViewData.TemplateInfo.GetFullHtmlFieldId(metadata.PropertyName);
//...... Collapsed code
yield return clientValidationRule;
}
//...... Collapsed code
}
The result of GetFullHtmlFieldId depends on how I build my asp.net mvc page:
// Edit.cshtml or Create.cshtml
#Html.EditorFor(model => model.MyBoolProperty)
// Shared/EditorTemplates/Boolean.cshtml
#Html.CheckBoxFor(model => model)
result of GetFullHtmlFieldId incorrect: MyBoolProperty_MyBoolProperty
// Edit.cshtml or Create.cshtml
#Html.CheckBoxFor(model => model.MyBoolProperty)
result of GetFullHtmlFieldId correct: MyBoolProperty
Even with more complex editors I see this incorrect behavior
// Edit.cshtml or Create.cshtml
#Html.EditorFor(model => model.JustAnArray[1].ComplexProperty.MyBooleanProperty)
// Shared/EditorTemplates/Boolean.cshtml
#Html.CheckBoxFor(model => model)
result of GetFullHtmlFieldId incorrect: JustAnArray_1__ComplexProperty_MyBoolProperty_MyBoolProperty
// Edit.cshtml or Create.cshtml
#Html.CheckBoxFor(model => model.JustAnArray[1].ComplexProperty.MyBooleanProperty)
result of GetFullHtmlFieldId correct: JustAnArray_1__ComplexProperty_MyBoolProperty
Also this is returning correct value
// Edit.cshtml or Create.cshtml
#Html.EditorFor(model => model.JustAnArray[1])
// Shared/EditorTemplates/ComplexProperty.cshtml
#Html.CheckBoxFor(model => model.MyBooleanProperty)
result of GetFullHtmlFieldId correct: JustAnArray_1__ComplexProperty_MyBoolProperty
It looks like that using #Html.CheckBoxFor(model => model), it gives incorrect results but when using #Html.CheckBoxFor(model => model.MyBoolProperty) it is working as expected
I have the same issue with other controls (like TextBoxFor)
Question
How can I get the proper fieldId of my control in my validation attribute, independent of how you build the page.
I'd rather use already existing methods (maybe the same methods as which are used by TextBoxFor and CheckBoxFor and other controls) than mimic this already existing functionality. If I mimic the generation of the fieldId, I have a change I don't take care of all situations where the ASP.NET controls take care of.
It seems that the valid prefix depends on the expressions used to build the page, and that isn't available to the GetClientValidationRules method. It would be great if someone has a solution for it, but as the method GetClientValidationRules is trying to initialize the data needed for the Javascript validation, you could try to resolve this on the client side.
On some of the default validation attributes like the [Compare], which depends on a second field, they set a parameter *.propertyName and in the unobtrusive adapter they replace the *. part with the valid prefix retrieved from the input name attribute. You could try a similar approach for the id.
However this would be needed if you were interested in another field. In this case it seems you are interested in the id of the very same input field that you are validating. You could then retrieve it from the input element itself. It will be available in both the unobtrusive adapter or the method itself:
//You could get it in the adapter and pass it to the validator in the "param"
adapters.add("dummy", function (options) {
var fullFieldId = options.element.id;
if (console) console.log("Full field id: " + fullFieldId);
setValidationValues(options, "dummy", fullFieldId);
});
$jQval.addMethod("dummy", function (value, element, param) {
var fullFieldId = param;
//do whatever validation logic
return true;
});
//You could also retrieve it directly in the validator method
$jQval.addMethod("dummy", function (value, element, param) {
var fullFieldId = element.id;
//do whatever validation logic
return true;
});
adapters.add("dummy", function (options) {
setValidationValues(options, "dummy", {});
});
Hope it helps!

Angular ASP.NET MVC Binding

In our MVC 5 project we use Angular. The following Razor works nicely:
#Html.EditorFor(x => x.FirstName,
new { required = "required", ng_model = "FirstName" })
However, if the MVC Model.FirstName is set to "Bob" when the page is rendered, the Input field is still blank.
If I set this in the Angular controller:
$scope.FirstName = "#(Model.FirstName)";
Then "Bob" appears.
My question is: Do I have to set the $scope.VARIABLE=MODEL.VARIABLE for every field in the UI, or can I tell Angular to respect what came over from ASP.NET MVC.
Angular is appearing to over write the [input value="Bob"] that MVC writes.
There is no need to separate the model into individual fields when you bind to scope. Instead you should bind the entire model:
$scope.model = #Html.Raw(Json.Encode(Model));
This would render to the client as:
$scope.model = { FirstName: 'John', LastName:'Doe', etc };
Then you can bind your input fields as:
#Html.EditorFor(x => x.FirstName,
new { required = "required", ng_model = "model.FirstName" })
Personally, I think its cleaner not to use #Html, in favor of simple HTML:
<input ng-model="model.FirstName" required />
In Angular, you don't really need an id anymore.

Using .NET MVC HTMLHelper .For methods doesn't generate correct ID/Name when casting viewmodel field

I got a ViewModel graph with some sub-objects.
This code:
#Html.DropDownListFor(model => model.JobTypeParams.ContactCatalogId, new SelectList(paramz.ContactCatalogues, "Id", "Description"), new { #class = "select2 input-default " })
Generates a Request.Form key/value item with the key of: JobTypeParams.ContactCatalogId.
That's great because that means the MVC Model Binder can correctly map the key/value item to the corresponding field in the view model.
However! This code (using casts)
#Html.DropDownListFor(model => ((AdSyncJobSpecificParameters)model.JobTypeParams).ContactCatalogId, new SelectList(paramz.ContactCatalogues, "Id", "Description"), new { #class = "select2 input-default " })
Generates a Request.Form key/value item with key ContactCatalogId. It loses the JobTypeParams prefix somewhere.
This leads to the modelbinder being unable to bind correct key to correct viewmodel field, and hence the problem.
I do not want to hard-code the ID because of many reasons. So how do you get the HTML Helper to correctly generate they name of the Name/Id attribute ?
It doesn't get them wrong, it's not supposed to do that. You have an incorrect expectation of how lambda's work. You are effectively replacing the old expression, model.JobTypeParams with a single expression of type AdSyncJobSpecificParameters.
What you should be doing is using an editor template.
#Html.EditorFor(model => model.JobTypeParams)
Then, create a AdSyncJobSpecificParameters.cshtml in your EditorTemplates folder and do this:
#model AdSyncJobSpecificParameters
#Html.DropDownListFor(model => model.ContactCatalogId,
new SelectList(paramz.ContactCatalogues, "Id", "Description"),
new { #class = "select2 input-default " })
You'll need to figure out a solution for the SelectList, since it doesn't seem to be part of your model. Maybe as a ViewBag item.
You can also create one for your superclass as well in YourSuperclass.cshtml
#model YourSuperclass
// etc...

how to update the name attribute of a HTML helper

I am using MVC4, just wondering, is it possible to update the name attribute of a html helper. I am updating this as the action method is expecting a particular name. I know I can just write raw html, but just want to know if there is an overide in the html helper
I tried this
#Html.TextAreaFor(m => m.noteDetail.NotesDetails, new { #class = "k-textbox", #cols = 100, #rows = 5, id="NotesDetails", name= "NotesDetails" })
but when I look at the generated html
<textarea class="k-textbox" cols="100" data-val="true" data-val-required="The details are required" id="NotesDetails" name="noteDetail.NotesDetails" rows="5"></textarea>
Thanks
You just need a # symbol in front of your name property.
One technique I've found when a submodel is the model that your POST action accepts is to put the HTML that renders the submodel in a partial view that's included in the main view. Have this partial by strongly typed by the submodel and pass the value of the submodel into it. This way, the prefixes won't be generated on the submodel.
#Html.Partial("_NoteDetails", Model.noteDetail)
Then in _NoteDetails.cshtml
#model NoteDetail
#Html.TextAreaFor(m => m.NoteDetails,
new { #class = "k-textbox", cols = 100, rows = 5 });
Hi I think as you pass a viewModel as it contains maybe 2 models that is why you have this name. It does like this for the Binder to construct objects from your Post or Get.
If you change the name then you will maybe perform some custom codes for your binder if you want to pass it again to the controller.

Provide value to htmlAttributes where keys contain dash in their name in MVC view (e.g. "data-bind")

Most of the Html helpers available in ASP.Net MVC have overloads with object htmlAttributes. This is used to provide additional attribute values for the outputted tags. While using the anonymous object notation for specifying htmlAttributes value, their property names must be valid c# identifier.
Now the problem arises when you are trying to output a property with a dash - character (for e.g. knockout js's "data-bind" attribute)
So for example lets take the following example:
#Html.TextBox("Title", string.Empty, new { data-bind="text: title" })
Try the above code in your view and at run-time it would show error screen with below message:
Compilation Error
Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: CS0746: Invalid anonymous type member declarator. Anonymous type members must be declared with a member assignment, simple name or member access.
So the question is, how to provide htmlAttributes with their property keys having dash characters; like "data-bind"?
In your property names, replace all your dash - characters with an underscore _ (as shown in example below):
#Html.TextBox("Title", string.Empty, new { data_bind="text: title" })
This would work because all HTML helpers convert an underscore _ in a property name to a dash - when rendering the HTML; i.e. for your example, data_bind when outputted in html gets converted to data-bind.
This is not always correct. Say you're using parameters in a URL.
#Html.ActionLink("Add Job", "Index", "Home", new { foo_bar = "foobar" }, new { #class = "btn btn-default", data_foo = "bar" })
The data_foo does get rendered as "data-foo", but the parameters stays as a under bar. Your result will be: http://your.domain/yourapp/?foo_bar=foobar
Of course you can't actually use the dash or you get the error specified in the OP.
I have worked around this as follows, but I'd be interested to see if anyone that comes along in the future will have a better way:
#{
var link = Html.ActionLink("Add Job", "Index", "Home", new { foo_bar = "foobar" }, new { #class = "btn btn-default", data_foo = "bar" });
}
#Html.Raw(link.ToString().Replace('_', '-'))
Use the HtmlHelper.AnonymousObjectToHtmlAttributes method. The following code will add a tooltip to the text input box, with the tooltip displaying the data from the DisplayAttribute description for MyIntVal on my model.
#{
var htmlAttributesWithDashes = HtmlHelper.AnonymousObjectToHtmlAttributes(
new
{
id = "myTextBoxId",
data_toggle = "tooltip",
data_position = "left top",
title = ModelMetadata.FromLambdaExpression( m => m.MyIntVal, ViewData ).Description
}
);
}
<div class="col-sm-6">
#Html.TextBoxFor( m => m.MyIntVal, htmlAttributesWithDashes )
</div>

Categories

Resources