using must in fluentValidation - fluentvalidation

I am using FluentValidation for the server side validation. Now I want to call a function using must.
This is the form code snippet :
<form method="post"
asp-controller="Category"
asp-action="SaveSpecification"
role="form"
data-ajax="true"
data-ajax-loading="#Progress"
data-ajax-success="Specification_JsMethod">
<input asp-for="Caption" class="form-control" />
<input type="hidden" asp-for="CategoryId" />
<button class="btn btn-primary" type="submit"></button>
</form>
What changes should I make to the code below to call function SpecificationMustBeUnique ?
public class SpecificationValidator : AbstractValidator<Specification>
{
public SpecificationValidator()
{
RuleFor(x => new { x.CategoryId, x.Caption}).Must(x => SpecificationMustBeUnique(x.CategoryId, x.Caption)).WithMessage("not unique");
}
private bool SpecificationMustBeUnique(int categoryId, string caption)
{
return true / false;
}
}
Tips: 1 - The combination of CategoyId and Caption should be unique
2 - Validation is not done when submitting the form(the validation just not running when submit the form)

The tricky part is deciding which property should be validated when the validation rule applies to a combination of values on different fields. I usually just close my eyes, and point to one of the view model properties and say "this is the property I'll attach the validator to." With very little thought. FluentValidation works best when the validation rules apply to a single property, so it knows which property will display the validation message.
So, just pick CategoryId or Caption and attach the validator to it:
RuleFor(x => x.CategoryId)
.Must(BeUniqueCategoryAndCaption)
.WithMessage("{PropertyName} and Caption must be unique.");
The signature for the BeUniqueCategoryAndCaption method would look like:
private bool BeUniqueCategoryAndCaption(Specification model, int categoryId)
{
return true / false;
}
Note: I guessed that the CategoryId property is an int, but you will need to make sure the categoryId argument to BeUniqueCategoryAndCaption is the same type as the CategoryId property in your view model.

Related

Call action method from view to automatically populate form value

I have a form which is used to create a resource, and it has two fields to be populated. When a user enters a value into the first field, I want to automatically call back to an action method on the server which will determine the value to use in the second field, without the user having to submit the form.
For example:
Full Name - User enters this value
Username - View calls server with the value specified in Full Name, server calculates value to be used, server passes value back to view, view presents the value.
Is it possible to do this in MVC core, and if so, can you please point the right direction?
I've been reading up on remote validation, and feel that I could probably use (or abuse) it in order to achieve the functionality looking for, but I'd imagine there's a property way to do this.
Any pointers appreciated.
Remote Validation can only do the validation without submitting the form, but It can't assign value to another field. In your case. It's actually very simple. You can use the js onchange event listen to the first field, in the event, use ajax to access the background. and then fill the returned value into the second field in the callback function. Below is a simple test
View:
<span>Full Name</span>
<input type="text" id="FullName" name="FullName" />
<span>User Name</span>
<input type="text" id="UserName" name="UserName" />
#section scripts{
<script>
$("#FullName").on("change", function () {
$.ajax({
type: "post",
url: "/User/GetUserName",
data: {
fullname: $("#FullName").val()
},
success: function (result) {
$("#UserName").val(result);
}
})
})
</script>
}
Controller:
[HttpPost]
public string GetUserName(string fullname)
{
var username = fullname.Split(" ");
return username.First();
}
Result:
#mj1313's answer was extremely helpful, but I didnt like the idea of having some loose Javascript kicking around in my views, especially since I may need to use this same functionality in multiple views.
I ended up converting the script into a global function, and calling it from within the onchange event like so.
JS Function
(in site.js)
function fullNameToUserName (fullNameId, usernameId) {
$.ajax({
type: "get", // GET rather than POST
url: "/User/GetUserNameFromFullName",
data: {
fullname: $(fullNameId).val()
},
success: function (result) {
$(usernameId).val(result);
}
})
}
Action method
[HttpGet("GetUserNameFromFullName")] // GET rather than POST
public IActionResult GetUserNameFromFullName(string fullName)
{
var username = fullName.Split(" ");
return Ok(username.First());
}
Model
public class UserModel
{
public string FullName { get; set; }
public string Username { get; set; }
}
View
#model MvcApp.Models.UserModel
#Html.DisplayNameFor(m => m.FullName)
#Html.TextBoxFor(m => m.FullName, new
{
onchange = $"fullNameToUserName({#Html.IdFor(m => m.FullName)}, {#Html.IdFor(m => m.Username)});"
})
#Html.DisplayNameFor(m => m.Username)
#Html.TextBoxFor(m => m.Username)
The only problem I have with this approach is that the call to fullNameToUserName(fullNameId, usernameId) is not strongly-typed, and if this function is called from multiple views, it's likely to be miss-typed at some point.
While #mj1313's answer was great and pointed me in the right direction, I wasn't fully satisfied with the approach. Personally, I prefer this approach as it's slightly more reusable and keeps my view's more lean.

How to remove required attribute in MVC4, although that has been made required in model

I am not able to do the same thing with string properties. In the below code, I want to remove the validation of the 'Last Name' and make it optional.
[Required(ErrorMessage = "Required")]
[Display(Name="Last Name")]
public string LastName { get; set; }
You can add following java-script to your view and it will to remove the required rule for LastName although it has been made required in model.
$("#LastName").rules("remove", "required");
Thanks!
If you want to ModelState to be valid then try this:
ModelState["LastName"].Errors.Clear();
if (ModelState.IsValid)
{
// your logic
}
if you want to disable clientside validations then :
#Html.EnableClientValidation(false);
Note: this will disable all the client side validations.
You can disable client side validation for specific field with this
<form method="post">
// to disable client-side validaiton for model.SomeValue
#{ Html.EnableClientValidation(false); }
#Html.TextBoxFor(model => model.SomeValue)<br />
#{ Html.EnableClientValidation(true); }
<input type="submit" />
</form>
or you can omit ModelState Error for specific field in controller side with this
ModelState.Remove("SomeValue"); // to omit SomeValue Validation Error
if(ModelState.IsValid)
{
//Do some stuff, eventually redirect elsewhere
}

Form submission for mvc model enumerable using editor template

I have an editor template for my model view ViewSetup. My view to use template is simplified as
#model IEnumerable<ViewSetup>
#Html.EditorFor(s => s)
My ViewSetup editor template has form submission like below:
using (Ajax.BeginForm("Edit", new AjaxOptions() { HttpMethod = "Post" }))
{
#Html.HiddenFor(p => p.TradingPartner.ID)
<input type="submit" value="Save" />
}
So basically i need a form to be submitted for each element of the Enumerable. But I'm facing a problem on form submission. My controller to process post is:
public ActionResult Edit(ViewSetup formDataSent)
{
formDataSent.Save();
}
As per default model binding I'm getting null for TradingPartner property since the name in the html is :
<input name="[0].TradingPartner.ID" type="hidden" value="1"/>
What I need is to submit only the ViewSetup object on each element instead of an array. If I can get the index part in the name removed that could work for me. But I'm not sure how to get just the ViewSetup object on form submission.
I bet that if you base your editor on one item instead of a list of items then you would gain more flexibility.
#model IEnumerable<ViewSetup>
#foreach (var item in Model)
{
#Html.EditorFor(modelItem => item.TradingPartner)
}
I had the same issue, what resolved it for me was this:
#foreach (var item in Model)
{
#Html.EditorFor(modelItem => item.TradingPartner, null, "")
}
The third property of the EditorFor being blank will get rid of the "[0]" from your objects.

Working with Multiple forms in MVC4

I am creating a small application in MVC 4. I have 2 forms form1 and form2 in single view. When one form is posted I proceed to the next form where I redirect from view to a action method Employee in which I perform some task and then again from action method I return back to view, in the view I want to go for form2 without touching form1 how do I do that.
Below I will show sample of how my code looks like
Controller
public ActionResult Index()
{
//some task
resturn view();
}
public ActionResult Employee()
{
//some task
}
View
#html.BeginForm("Index","Show",FormMethod.Post,new {#name=form1})
{
//Shown something
}
#Html.Action("Employee","Show")
#html.BeginForm("Employee","Show",FormMethod.Post,new {#name=form2})
{
//Shown something
}
Create a Javascript that checks if any of the required values in form 1 is set. If so, set focus on whatever element you want in form 2. Here's an example:
The html forms:
<form id="one">
<input type="text" id="requiredFormField" value="some value previously entered" />
</form>
<form id="two">
<input type="text" id="form2text" />
</form>
And the javascript:
$(document).ready(setFocusIfForm1Completed());
function setFocusIfForm1Completed()
{
var expectedValue = $("#requiredFormField").val();
if(expectedValue.length > 0)
{
$("#form2text").focus();
}
}
I should add that I assume that you fill in form 1, then you submit (do a POST request to the server) which does some work, and then returns the same view but with at least some required value from form 1 populated (otherwise, this solution breaks).

Orchard Module - How to return strongly typed Model rathen than dynamic from Driver

I created a ContactUs module that sends email when user click on Submit button.
Everything works perfectly. However, I am curious if it is possible to return a strongly typed Model rather than dynamic class.
For example, following is my Drivers\ContactUsDriver.cs Display function:
protected override DriverResult Display(ContactUsPart part, string displayType, dynamic shapeHelper)
{
return ContentShape("Parts_ContactUs",
() => shapeHelper.Parts_ContactUs(
Name: part.Name));
}
As you can see, above is returning a dynamic Parts_ContactUs.
Now, here's snapshot of my Views\Parts\ContactUs.cshtml:
#model dynamic
#using (Html.BeginForm("Send", "ContactUs", new { area = "ContactUs" }, FormMethod.Post))
{
<fieldset>
<legend>Contact Us</legend>
<div id="contact-us" class="area">
#Html.TextBox("Name", "")
</div>
<div id="submitArea" class="button">
<input type="submit" value="Submit Message">
</div>
</fieldset>
}
As you can see above the View is bound to #model dynamic. As a result, I have to do following
#Html.TextBox("Name", "")
Is there a way I can bind to Model say ContactUsModel and thus do following instead?
#Html.TextBoxFor(m => m.Name)
Particularly, I am interested so I can write a jquery validation with DataAnnotation attribute.
It's perfectly possible. Just provide a desired model type as your first argument when creating a shape:
protected override DriverResult Display(
ContactUsPart part,
string displayType,
dynamic shapeHelper)
{
return ContentShape("Parts_ContactUs",
() => shapeHelper.Parts_ContactUs(typeof(MyClass), Name: part.Name));
}