What I would Like
I would like to trigger client-side validation in my View with an event of my choice. It could be 'onblur' maybe another button but something other than the submit button.
Relevant Links
How to trigger validation without using a submit button
Applying unobtrusive jquery validation to dynamic content in ASP.Net MVC
What I've tried
Given a various event listener, I've fired the following methods with no luck:
$(selector).validate();
$(selector).valid();
$.validator.unobtrusive.parseDynamicContent(selector);
$.validator.unobtrusive.parse($(selector));
Summary
So I need client-side validation to fire on a given event (other than on submit) and show the corresponding validation messages. I dont feel like any of the Markup/Razor syntax is necessary as client-validation fires on submit and all the corresponding validation messages show as expected.
$('form').valid() should work. Let's exemplify.
Model:
public class MyViewModel
{
[Required]
public string Foo { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
}
View:
#model MyViewModel
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.Foo)
#Html.EditorFor(x => x.Foo)
#Html.ValidationMessageFor(x => x.Foo)
}
<div id="validate">Hover here to trigger validation</div>
<script type="text/javascript">
$('#validate').hover(function () {
$('form').valid();
});
</script>
Manual validation of custom messages injected directly to HTML
#{using (Html.BeginForm("addBuyOnlinepostA", "BuyOnline", FormMethod.Post, new { #enctype = "multipart/form-data", #id = "form1" }))
{
#Html.ValidationSummary(true)
<div class="row">
<div class="col-sm-10">
Product Qty
<input class="inputs" required type="number" id="price" name="price" placeholder="Enter Price in AED"
data-val="true" data-val-required="Please enter a value" data-val-regex="Please enter a valid number."
data-val-regex-pattern="\d{1,20}" />
<span class="field-validation-valid" data-valmsg-for="price" data-valmsg-replace="true"
style="color: Red;"></span>
</div>
</div>
<input type="button" onclick="$('form').valid()" />
}
}
Related
I created wizard with the jQuery-Steps lib with simple form for now. There is no Submit button. I submit form on finish step via jQuery.
I am already using jQuery calls all over the place, and I have included all scripts, and I needed form for uploading images and other stuff, it's easier with it.
Nothing happens, the controller action is not called.
I just getting redirected on start page with all this parameters in query string.
Like this:
https://localhost:44380/Dashboard?Name=Aaaa&Surename=Bbbb&City=Cccc
Dashboard is all made with Ajax and partial views. And from person option in menu I'm getting redirected on home view in Dashboard with all this parameters.
This is the form:
<form asp-controller="PersonSettings" asp-action="SaveForm" data-ajax="true" data-ajax-method="POST" id="personForm" class="steps-validation wizard-notification">
<!-- Step 1 -->
<h6>Person</h6>
<fieldset>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label for="Name">
Person Name :
<span class="danger">*</span>
</label>
<input autocomplete="off" type="text" class="form-control required" id="Name" name="Name">
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="Surname">
Person Surname:
<span class="danger">*</span>
</label>
<input autocomplete="off" type="url" class="form-control required" id="Surname" name="Surname">
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="City">
Person City:
<span class="danger">*</span>
</label>
<input autocomplete="off" type="url" class="form-control required" id="City" name="City">
</div>
</div>
</div>
</fieldset>
</form>
This is the Model:
public class PersonDto {
public string Name { get; set; }
public string Surname { get; set; }
public string City { get; set; }
}
Here is Action in PersonSettings controller:
[Route("SaveForm")]
[HttpPost]
public IActionResult SaveForm(PersonDto Person) {
//Do something with person model
return Ok();
}
And on finish button I have jQuery submit called on this form:
$('#personForm').submit();
EDIT:
I tried with this parameters:
<form action="PersonSettings/SaveForm" method="post" id="personFrom" class="steps-validation wizard-notification">
And this works.. it's good, but why does the first method not work? Because I need this in Ajax and finish method.
I didn't find any way to post image with form with classic Ajax call. Because I can't read image path (it's not exposed to the browsers).
I think that you didn't installed Tag Helpers. If it works with action attribute, it should work with Tag Helpers attribute. Because you're using Asp-Net-Controller attribute.
https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.TagHelpers
Please let me know.
I am trying to pass hidden field value from view to controller by doing the following
#Html.HiddenFor(model => model.Articles.ArticleId)
and also tried
<input type="hidden" id="ArticleId" name="ArticleId" value="#Model.Articles.ArticleId" />
On both instances the value of ArticleId is 0 but when i use TextboxFor i can see the correct ArticleId, please help
Here it is
View
#model ArticlesCommentsViewModel
....
#using (Html.BeginForm("Create", "Comments", FormMethod.Post))
{
<div class="row">
<div class="col-xs-10 col-md-10 col-sm-10">
<div class="form-group">
#Html.LabelFor(model => model.Comments.Comment, new { #class = "control-label" })
#Html.TextAreaFor(m => m.Comments.Comment, new { #class = "ckeditor" })
#Html.ValidationMessageFor(m => m.Comments.Comment, null, new { #class = "text-danger"})
</div>
</div>
</div>
<div class="row">
#*#Html.HiddenFor(model => model.Articles.ArticleId)*#
<input type="hidden" id="ArticleId" name="ArticleId" value="#Model.Articles.ArticleId" />
</div>
<div class="row">
<div class="col-xs-4 col-md-4 col-sm-4">
<div class="form-group">
<input type="submit" value="Post Comment" class="btn btn-primary" />
</div>
</div>
</div>
}
Controller
// POST: Comments/Create
[HttpPost]
public ActionResult Create(CommentsViewModel comments)//, int ArticleId)
{
var comment = new Comments
{
Comment = Server.HtmlEncode(comments.Comment),
ArticleId = comments.ArticleId,
CommentByUserId = User.Identity.GetUserId()
};
}
Model
public class CommentsViewModel
{
[Required(ErrorMessage = "Comment is required")]
[DataType(DataType.MultilineText)]
[Display(Name = "Comment")]
[AllowHtml]
public string Comment { get; set; }
public int ArticleId { get; set; }
}
ViewModel
public class ArticlesCommentsViewModel
{
public Articles Articles { get; set; }
public CommentsViewModel Comments { get; set; }
}
The model in the view is ArticlesCommentsViewModel so therefore the parameter in your POST method must match. Your use of
#Html.HiddenFor(model => model.Articles.ArticleId)
is correct, but you need to change the method to
[HttpPost]
public ActionResult Create(ArticlesCommentsViewModel model)
and the model will be correctly bound.
As a side note, your ArticlesCommentsViewModel should not contain data models, and instead should contain only those properties you need in the view. If typeof Articles contains properties with validation attributes, ModelState would be invalid because your not posting all properties of Article.
However, since CommentsViewModel already contains a property for ArticleId, then you could just use
#Html.HiddenFor(model => model.Comments.ArticleId)
and in the POST method
[HttpPost]
public ActionResult Create([Bind(Prefix="Comments")]CommentsViewModel model)
to effectively strip the "Comments" prefix
In your controller, you need to pass the hidden value with the model,
for example, if you have a userId as a hidden value, in your Page you add:
#Html.HiddenFor(x => x.UserId)
In your model of course you would already have UserId as well.
In your controller, you need the model as a parameter.
public async Task<ActionResult> ControllerMethod(YourViewmodel model) { model.UserId //this should be your HiddenValue
I guess your model have another class called Articles inside CommentsViewModel.Change your controller function for accessing the ArticleId accordingly.
[HttpPost]
public ActionResult Create(CommentsViewModel comments)//, int ArticleId)
{
var comment = new Comments
{
Comment = Server.HtmlEncode(comments.Comment),
ArticleId = comments.Articles.ArticleId, // Since you are using model.Articles.ArticleId in view
CommentByUserId = User.Identity.GetUserId()
};
}
In my case, I didn't put the hidden input in the form section, but out of form, so it's not send to backend. Make sure put hidden input inside the form.
Also make sure name attribute is specified on the hidden field. Element's "id" is often used on client side but "name" on server side.
<input type="hidden" value="#ViewBag.selectedTraining" id="selectedTraining"
name="selectedTraining" />
In my case, I was passing a couple of fields back and forth between controllers and views. So I made use of hidden fields in the views.
Here's part of the view. Note a controller had set "selectedTraining" and "selectedTrainingType" in the ViewBag to pass to the view. So I want these values available to pass on to a controller. On the hidden tag, the critical thing is set to the "name" attribute. "id" won't do it for you.
#using (Html.BeginForm("Index", "ComplianceDashboard"))
{
<input type="hidden" value="#ViewBag.selectedTraining" id="selectedTraining" name="selectedTraining" />
<input type="hidden" value="#ViewBag.selectedTrainingType" id="selectedTrainingType" name="selectedTrainingType" />
if (System.Web.HttpContext.Current.Session["Dashboard"] != null)
{
// Show Export to Excel button only if there are search results
<input type="submit" id="toexcel" name="btnExcel" value="Export To Excel" class="fright" />
}
<div id="mainDiv" class="table">
#Html.Grid(Model).Columns(columns =>
Then back on the controller:
// POST: Dashboard (Index)
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(string excel)
{
string selectedTraining, selectedTrainingType;
selectedTraining = Request["selectedTraining"];
selectedTrainingType = Request["selectedTrainingType"];
Or can put the requests as parameters to the method: public ActionResult Index(string excel, string selectedTraining, string selectedTrainingType)
I'm trying to create view that works with two different models. But I keep getting error about wrong type passed in view:
The model item passed into the dictionary is of type 'System.Data.Entity.DynamicProxies.ApplicationUser_D1BDF9065CBD1B8BC24F5E69ACC3CAB19A6C7FB8624B0C4435112D881B7C9CA2',
but this dictionary requires a model item of type
'StudentBookProject.ViewModel.UserPostWrapper'.
I've created a simple wrapper class to so I can access all the properties of two separated classes like this:
public class UserPostWrapper
{
public ApplicationUser UserInfoObject { get; set; }
public List<Post> PostInfoObject { get; set; }
}
And this is the view where the exception is thrown:
#model StudentBookProject.ViewModel.UserPostWrapper
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery-ui-1.11.2.js"></script>
#using (Html.BeginForm("Upload", "Posts", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="jumbotron" style="height:300px">
<div class="col-md-3">
<img id="profilePhoto" src="data:image/png;base64,#Convert.ToBase64String(Model.UserInfoObject.ProfilePicture, 0, Model.UserInfoObject.ProfilePicture.Length)" width="150" />
</div>
<div id="dialog" style="display: none;">
<img src="data:image/png;base64,#Convert.ToBase64String(Model.UserInfoObject.ProfilePicture, 0, Model.UserInfoObject.ProfilePicture.Length)">
</div>
<div class="col-md-push-5">
#Html.Label(Model.UserInfoObject.Name + " " + Model.UserInfoObject.LastName, new { #class = "nameLabel" }) <br />
#Html.Label(Model.UserInfoObject.DateOfBirth) <br />
#Html.Label(Model.UserInfoObject.City)
</div>
</div>
<div class="jumbotron" style="height:170px">
<div class="col-lg-7">
<textarea class="expand" rows="3" cols="20" placeholder="Share something with colleagues..."></textarea>
<div class="icons">
<button class="btn btn-sm btn-primary">Post</button>
</div>
</div>
<input type="file" id="id_default_image" style="visibility: hidden" />
<input type="file" id="id_default_file" style="visibility: hidden" />
<div class="col-lg-5"></div>
</div>
<br />
<br />
<br />
}
This is the action that renders View:
[Authorize]
public ActionResult GetCurrentUser()
{
var user = UserManager.FindById(User.Identity.GetUserId());
return View(user);
}
I'm not sure I understand what is happening. Who is passing wrong model type to an view, if I've explicitly defined which model I'm using in that view?
Does anyone knows what I'm missing here? And is this kind of an approach workable?
Change you action to this:
[Authorize]
public ActionResult GetCurrentUser()
{
var user = UserManager.FindById(User.Identity.GetUserId());
var model = new UserPostWrapper { UserInfoObject = user};
return View(model);
}
As I've said in the comments you're passing a different model into your view. The view expects the UserPostWrapper model but you're passing the ApplicationUser model.
I have MVC application with a model like this :
public class ListOfMyModel
{
public List<MyModel> MyModels { get; set; }
public Guid MyID { get; set; }
}
public class MyModel
{
// Some code like :
public string MyString { get; set; }
}
And my post method in my controller is like this :
[HttpPost]
public ActionResult EditMe(ListOfModel myList)
{
try
{
if (ModelState.IsValid)
{
List<MyModel> myModels = myList.MyModels;
foreach (MyModel model in myModels)
// Some code
return RedirectToAction("Index");
}
catch
{
// Some code
return View(myList)
}
return View(myList);
}
And my view : ( I use Kendo UI ) ( P.S : Some code has been stripped away and replaced by comment code )
#model MyApplication.Web.Models.ListOfMyModel
#{
ViewBag.Title = MyTitle;
Layout = "~/Views/Shared/_MyLayout.cshtml";
}
<div class="span1"></div>
<div class="span8">
<div id="list-wrapper">
<div class="k-content">
<form id="form" class="form-horizontal well span8 offset2" method="post" action="#Url.Action("EditMe")">
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery-1.9.1.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/kendo/2013.1.514/kendo.web.min.js")"></script>
<script src="#Url.Content("~/Scripts/kendo/2013.1.514/kendo.aspnetmvc.min.js")"></script>
<div class="offset2 span2">
<fieldset>
<legend> My title </legend>
<p>Some code :</p>
#Html.HiddenFor(m => m.MyID)
#for (int i = 0; i < Model.MyModels.Count; i++)
{
// Some code
<div class="control-group">
<label class="control-label">MyText : </label>
<div class="controls">
#(Html.Kendo().DropDownListFor(c => Model.MyModels[i].MyString)
.DataTextField("Text")
.DataValueField("Value")
.DataSource(dataSource => dataSource
.Read(read => read.Action("GetSomeThings", "MyController"))
)
.Value(Model.MyModels[i].MyString)
)
</div>
</div>
}
<div class="form-actions">
<button type="submit" class="btn btn-primary">Validate</button>
</div>
</fieldset>
</div>
</form>
</div>
</div>
</div>
But the problem is that when I push the submit button in my view, the method of my controller is called with all the data expected ( saw in Chrome ) but in this method, all of the model is null : The ID and the list... I don't known where the problem is ?
Thank you all for reading and trying to understand this, if you want more informations please tell me.
The MyId should be correctly received with your existing code.
The model binder can only match the value of inputs whose name have a matching property in the form. I.e. if you have an input like this <input name='bikes' ...> then your model should have a property Bikes so that the model binder can transfer the value from the input to the property. In your case you're creating input with dynamic names that doesn't have a matching property name. (NOTE: thei referes to the model you're using as the action parameter).
The farthest you can go is giving a series of input elements the same name, i.e. several elements like <input name='categories' ...> and receive it in an array parameter like string[] categories or a model which has a property like string[] Categories {get;set;}
In short, you have to redesign your model and view. If you used a List<string> instead of a List<MyModel>, and the view had a fixed name for the dropdow lists, like DropDownListFor(c => Model.MyModels, then the model binder would fill the MyModels property with the list of selected strings in each drop down list.
Hint: you can use a model for the View and receive a different model (or series of parameters) in the Action. In this way you can send more information to render the View, and receive a post with the essential data to process the user input.
See my answer to this question for alternatives. It explains something similar to this question.
I'm using C#, MVC3 and VS2010
I noticed that even though the valdiation results to false, the controller method still gets executed. This makes the validation useless on the server-side. Unless there is a way to get the result.
---- EDITS -----
Here is how i'm using it. Is it done properly? At least the validation messages show up, and the textbox turns red.
Model:
public class CategoriaModel
{
[Required(ErrorMessage="Nome é obrigatório!")]
[StringLength(10, ErrorMessage = "First Name max length is 10")]
public string Nome { get; set; }
}
View:
#Html.ValidationSummary(true)
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js") %>" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js") %>" type="text/javascript"></script>
#using (Html.BeginForm("Salvar", "Test", FormMethod.Post))
{
#Html.LabelFor(m => m.Nome)
#Html.EditorFor(m => m.Nome)
#Html.ValidationMessageFor(m=>m.Nome)
<input type="submit" value="Salvar" />
}
Controller:
public ActionResult Salvar(CategoriaModel catModel)
{
ViewBag.StatusMessage = "Ok!";
return View("Index");
}
testing ModelState.IsValid property, however if client side validation fails the Action should not be called. Check if you're actually validating all your Model properties.