Default values and validation in ASP RazorPage - asp.net-mvc

My PageModel has a single BindProperty like ?Query=, whose length should not exceed 1000. However, I also want this string to always be defined; on initial page load (or if a blank form is submitted), I want to use the default value "*" instead of null or "":
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel.DataAnnotations;
namespace RazorTestApp.Pages;
public class IndexModel : PageModel
{
[BindProperty(SupportsGet = true)]
[StringLength(1000)]
[Required(AllowEmptyStrings = false)]
public string Query { get; set; } = "*";
public void OnGet()
{
}
}
I have an equally simple View:
#page
#model IndexModel
<div class="text-center">
<form method="get">
#Html.EditorForModel()
<div>
<input type="submit" />
</div>
</form>
</div>
I am having 2 issues with this:
Load page with no query string (initial load): the input box correctly displays "*", but I also get a validation error "The Query field is required".
Load page with ?Query= (submit with empty query): the input box does not show the default "*" (shows empty string), and I get the same validation error "The Query field is required".
I am obviously not doing this correctly. How can I set a default value for Query to be used whenever it is empty/not provided, and have it play nicely with validation?

I tried to test the validations on my side.
Below is my model:
[BindProperty(SupportsGet = true), Required, StringLength(1000)]
public string Rating { get; set; } = "*";
View:
<div class="form-group">
<label asp-for="Movie.Rating" class="control-label"></label>
<input asp-for="Movie.Rating" class="form-control" />
<span asp-validation-for="Movie.Rating" class="text-danger"></span>
</div>
Output:
On my side, I could see that * is added to the input field and if I remove it and submit the form then I get the validation message.
For testing purposes, I would suggest you make a test with the <form method="post">. If the issue persists then creating a new model and view and trying to test it separately may help to narrow down the issue.

Related

Why validation is not working with Html.TextBoxFor but is when using Html.EditorFor?

I am trying MVC's datatype attributes, and just created a simple scenario like the following:
The View:
#model MVC4.Models.Model
#{
ViewBag.Title = "DataTypeAttribute";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<h2>DataTypeAttribute</h2>
#using (Html.BeginForm("SubmitData", "Home"))
{
<div>
#Html.ValidationSummary()
</div>
#Html.EditorFor(m => m.Email)
<br />
<br />
#Html.EditorFor(m => m.PostalCode)
<br />
<br />
#Html.EditorFor(m => m.TextOnly)
<br />
<br />
<button type="submit">Submit</button>
}
The Model:
public class Model
{
[DataType(DataType.EmailAddress)]
[Required]
//[EmailAddress]
public string Email { get; set; }
[DataType(DataType.PostalCode)]
public string PostalCode { get; set; }
public string TextOnly { get; set; }
}
"SubmitData" is just a controller that, returns View(..., model) if ModelState.IsValid is false.
Although posts like this do a good job in tackling the differences between Html.TextBoxFor and Html.EditorFor, I could not find an answer as to why validation for the datatype EmailAddress will not work when using TextBoxFor. I did find people mentioning TextBoxFor does not take metadata into account, while EditorFor does.
But does this make sense ? So TextBoxFor does not offer support for client validations ?!
I wonder what is the reason for the difference between the two ?
TextBoxFor() does work with validations.
[DataType(DataType.EmailAddress)] is not a validation attribute. Its an attribute that determines the type of input to display by setting the type attribute in the rendered html. For example <input type="text" ..>, <input type="date" ..>, <input type="email" ..> in order to render the browsers implementation of a HTML4 datepicker, email input etc.. It works only for EditorFor() because TextBoxFor() as its name suggest generates and input with type="text"
If you want validation for an email address, then you use the [EmailAddress] attribute on your property.
[Required]
[EmailAddress]
public string Email { get; set; }
Edit (further to the comments)
One of the features of HTML5 is the ability to validate user data without relying on scripts. One such form of browser validation is using the type attribute. The use of [DataType(DataType.EmailAddress)] on a property that is rendered with #Html.EditorFor() adds type="email" to the input element. From the MDN documentation
email: The element represents one email address. Line breaks are automatically stripped from the input value. An invalid email address can be set, but the input field will only satisfy its constraints if the email address satisfies the ABNF production 1*( atext / "." ) "#" ldh-str 1*( "." ldh-str ) where atext is defined in RFC 5322 section 3.2.3, and ldh-str is defined in RFC 1034 section 3.5.
If your are currently seeing a validation error message associated with the property, and you have not added the [EmailAddress] attribute, then it means that jquery.validate.js is not loaded and you are seeing the browsers error message associated with type="email".
When jquery.validate.js is loaded (correctly), the novalidate="novalidate" attribute is added to the form element, which specifies that form is not to be validated (using the HTML5 validation) when submitted. The relevant code from jquery.validate.js is (approx line 35)
// Add novalidate tag if HTML5.
this.attr('novalidate', 'novalidate');
This is added to prevent possible confusion between error messages displayed by browser validation and jquery unobtrusive validation.
As to why DataTypeAttribute attribute inherits ValidationAttribute when it does not actually do validation, from Brad Wilson himself in this answer
The reason it derives from ValidationAttribute is so that you could create a new custom data type class, which was both a DataType and a Validation, all wrapped up into one. It's an unfortunate side-effect of .NET not allowing multiple inheritance.

Attaching a hidden text field to a form MVC

This very well may end up being a very silly question in a way but basically I have this "form" in a model that gets attached to my View as the form but I haven't been able to actually pass any data do it from the View. It only has two properties: an Id property and a String property. I've been trying to fill the String property with text from a hidden text box on the page with no luck.
Form code:
public class AllocateListForm
{
public int Id { get; set; }
public virtual string HiddenText { get; set; }
}
Relevant View code:
<% using (Html.BeginForm("SaveExit", "User", new { }, FormMethod.Post, new { id = "selectExitPoints" })) { %>
<fieldset>
<input type="hidden" id="HiddenText" />
</fieldset>
<% } %>
There is JQuery behind the scenes that fills HiddenText with text and I can assure you that it is filling. There is also JQuery behind the scenes that performs an Ajax submission and I can promise you that code works as it is used elsewhere in the application without a problem. When I perform the action that submits the form to the server and I go to my controller code that this points to, I have a breakpoint set so I can go into the console and check if the HiddenText field on the form has any data it is null. Can anybody point me in the right direction?
If you assign the input's name to be "HiddenText" the model binder should pick it up. I'm assuming that your controller action accepts an AllocateListForm as a parameter.
<input type="hidden" name="HiddenText" id="HiddenText" />
You can also use Html Helpers like so:
#Html.HiddenFor(model => model.HiddenText, new { id = "HiddenText" })
EDIT: Add an AllocateListForm as a property of your main model and then change the helper to be #Html.HiddenFor(model => model.MyAllocateListForm.HiddenText)
This should do the trick, if you want to do it the Razor-way.
#Html.HiddenFor(model => model.HiddenText);

Fields marked HiddenFor not binding to Model in MVC4

I have implemented an image upload child action form for an application. I have a strongly typed partial view.
public class ImageViewModel{
public long ImageId{get;set;}
public long OwnerId{get;set;}
public string ImageName{get;set;}
public string ImageDescription{get;set;}
public IEnumerable<HttpPostedFileBase> Files { get; set; }
}
Razor code looks something like this:
<form action="UploadImage" method="post" enctype="multipart/form-data">
#Html.ValidationSummary()
#Html.HiddenFor(m => m.OwnerId)
#Html.HiddenFor(m => m.ImageId)
#HtmlEditorFor(m=>m.ImageName)
<input type="file" name="Files" id="file0" />
<input type="submit" value="Upload" />
</form>
Here is the problem. When form is posted back, the model has uploaded file and ImageName value in it. But values that were bound using HiddenFor are missing.
[HttpPost]
public ActionResult UploadImage(ImageViewModel model)
{ ...}
I have checked HTML source. Hidden fields are rendered corrected with Id and names matching to property named of model. On post back I checked the raw request. Both hidden fields are carried in Form collection. But model binding is not setting the values of these fields in properties.
Is there something that I am missing about these hidden fields?
Thanks

How add custom validation to my razor page?

I have razor page in my mvc 4 application .Now imagine i have a simple textbox in my page as follows :
#Html.TextBox("Email")
or
<input id="Email" name="Email" type="text" />
how can I do some client-side validation for this field you know something like asp.net web forms validation controls . I want to make it required and write a Regex for it .
I use to do this:
In the model Person.cs :
[StringLength(50, ErrorMessage = "El campo {0} no puede tener mas de 50 caracteres")]
[Required(ErrorMessage = "El nombre es obligatorio")]
public string Nombre { get; set; }
In the page Create.cshtml:
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Person.Nombre" class="control-label">Nombre / Name:</label>
<input asp-for="Person.Nombre" class="form-control" />
<span asp-validation-for="Person.Nombre" class="text-danger"></span>
</div>
You generally decorate your model properties with the RequiredAttribute and the RegexAttribute.
You may want to look at Data Annotations. They provide very simple and straightforward way for you to define your (not only) validation for every field in every view model.
Please take a look at the following link:
How to: Validate Model Data Using DataAnnotations Attributes
In your particular case, it will be
[Required]
[RegularExpression(#"<your RegEx>", ErrorMessage = "<your message, leave out for default>")]
public string fieldYouValidating { get; set; }
Other way for validation preferrable by many is Fluent Validation
RuleFor(x => x.fieldYouValidating)
.NotEmpty()
.WithMessage("This is the message.");
Hope this helps

ASP .NET MVC 2 - Browse and add link to document

I am developing an internal project managment dashboard using MVC 2. One of the requirements is to link to already existing documents on one of our local servers, in other words, browse to server, select file, click add and the view for that project will contain the link. Here is what I have (I've left some details out for brevity):
Model:
public class AddDocumentModel
{
public HttpPostedFileBase DocumentLink { get; set; }
}
View:
<%
using (Html.BeginForm(MVC.ProjectDetails.Actions.AddDoc(this.Model.ProjectID),
FormMethod.Post, new { enctype = "multipart/form-data" }))
{%>
<%=Html.TextBoxFor(a => a.DocumentLink,
new { type = "file", style = "width:100%;"})%>
<input type="submit" value="Add Document Link" />
<%} %>
Controller:
[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult AddDoc(AddDocumentModel docModel)
{
var model = _projectManagementService.AddDocumentLink(
docModel.DocumentLink.FileName);
}
So, as you can see I am using an html textbox for file upload but not actually uploading, just attempting to grab path and filename and use that as link. However due to security constraints this will only work in IE, as no other browser will let you get at the path. Also, if the user uses a mapped drive it won't work as the full path will not be used, so they have to navaigate directly to the server.
Can anyone think of another way to do this? I would like to be able to use the browse functionality offered by upload functionality, but not be tied by constraints.
At the moment the only (low tech) solution I can think of is for the user to explicitly paste the link into a text box. But would prefer something a lot more friendly.
Thanks in advance. First posted question too, so be kind :-)
If I were you I would allow them to either upload a new file or paste in the location of an existing file. There's no reason to try to reuse a file upload element to do what you're doing.
Form example (didn't feel like writing out the <%=Html %>
<form>
<div>
<input type="radio" name="AddDocumentType" value="New" />
<label for="NewDocument">Upload New Document</label>
<input type="file" id="NewDocument" name="NewDocument" />
</div>
<div>
<input type="radio" name="AddDocumentType" value="Link" />
<label for="LinkDocument">Link To Existing Document</label>
<input type="text" id="LinkDocument" name="LinkDocument" />
</div>
<input type="submit" value="Add Document Link" />
</form>
Model
public enum AddDocumentType
{
New,
Link
}
public class AddDocumentModel
{
public AddDocumentType AddDocumentType { get; set; }
public HttpPostedFileBase NewDocument { get; set; }
public string LinkDocument { get; set; }
}

Resources