Modals and form validation - asp.net

I have a page that has about 4 different forms nested inside of their own modal pop up windows.
I have only ever done one page per form until this point.
What happens is when I submit to the controller and my modelState is not valid is that it routs back to my partial view.
How can I make the #validationFor messages show up in my modal, just like on my single form pages? Basically the page doesn't change, but if it does, it returns the page with the modal still up.
Will this require that I use some sort of Ajax call instead? are there any examples on here of that? I am pretty sure this is probably a duplicate thread, however I couldn't find any resources for myself.

Yes this will require some javascript to make this work.
create partial views to hold a form.
#using (Html.BeginForm("CreateHighSchoolType", "HighSchool", FormMethod.Post, new { id = "CreateHighSchoolTypeForm" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend></legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<p>
<input type="submit" value="Create" id="CreateHighSchoolTypeButton" />
</p>
</fieldset>
}
create a controller methods to handle the partial view
public ActionResult CreateHighSchoolTypePartial()
{
return PartialView();
}
[HttpPost]
public ActionResult CreateHighSchoolTypePartial(LookupEditModel viewModel)
{
if (!ModelState.IsValid)
{
return PartialView(viewModel);
}
var hsType = (from t in _highSchoolTypeRepository.FindAll()
where t.Name == viewModel.Name
select t).FirstOrDefault();
if (hsType != null)
{
ModelState.AddModelError("Name", String.Format("This high school type already exists.", viewModel.Name));
return PartialView(viewModel);
}
_highSchoolTypeRepository.Save(new HighSchoolType
{
Name = viewModel.Name
});
return PartialView();
}
wire up everything with some jquery
the dialog opening
$("#AddHighSchoolTypeDialog").dialog({
position: 'center',
autoOpen: false,
modal: true,
resizable: false,
draggable: false,
title: "Add a new high school type",
open: function (event, ui) {
//Load the CreateAlbumPartial action which will return
// the partial view _CreateAlbumPartial
$(this).load("/HighSchoolType/CreateHighSchoolTypePartial", function () {
$("form#CreateHighSchoolTypeForm input:first").focus();
});
},
focus: function (event, ui) {
},
close: function (event, ui) {
$.ajax({
url: "/HighSchoolType/GetAllHighSchoolTypes",
type: "GET",
success: function (data) {
$("#HighSchoolTypeId option").remove();
$.each(data, function (key, value) {
$('#HighSchoolTypeId')
.append($("<option></option>")
.attr("value", key)
.text(value));
});
}
});
//$("#AddHighSchoolTypeDialog").remove();
},
width: 600
});
and the post
$("#CreateHighSchoolTypeButton").live('click', function () {
$.ajax({
url: "/HighSchoolType/CreateHighSchoolTypePartial",
type: "POST",
data: $("#CreateHighSchoolTypeForm").serialize(),
error: function (data) {
var errorMessage = $.parseJSON(data.responseText);
},
success: function (data) {
if (data) {
$('#AddHighSchoolTypeDialog').html(data);
}
else {
$('#AddHighSchoolTypeDialog').html('no data');
}
}
});
return false;
});
Note how on success your need to replace the html of the modal with what was returned from the post call.

As per my understanding, what you are doing is trying to have multiple forms in one page. You can try something like this -
#using (Html.BeginForm("Login", "Member", FormMethod.Post, new {})) {
#Html.LabelFor(m => m.LoginUsername)
#Html.TextBoxFor(m => m.LoginUsername)
#Html.LabelFor(m => m.LoginPassword)
#Html.TextBoxFor(m => m.LoginPassword)
<input type='Submit' value='Login' />
}
#using (Html.BeginForm("Register", "Member", FormMethod.Post, new {})) {
#Html.LabelFor(m => m.RegisterFirstName)
#Html.TextBoxFor(m => m.RegisterFirstName)
#Html.LabelFor(m => m.RegisterLastName)
#Html.TextBoxFor(m => m.RegisterLastName)
#Html.LabelFor(m => m.RegisterUsername)
#Html.TextBoxFor(m => m.RegisterUsername)
#Html.LabelFor(m => m.RegisterPassword)
#Html.TextBoxFor(m => m.RegisterPassword)
<input type='Submit' value='Register' />
}
inside a single page.
You can check answers on this link -
asp.net MVC 4 multiple post via different forms

The unobtrusive validation will only work on forms that exist on page load. if you are loading the pages dynamical in the modals you need to register them after they load
$.validator.unobtrusive.parse("#formid")
or you can just hide the views on load and show when needed

Related

No opportunity to override name attribute for #Html.TextBoxFor in asp.net core 3.1

We are migrating our project from .net Framework 4.72 to .net core 3.1.
I have next html helper code:
#Html.TextBoxFor(Model => Model.Property, "{0:0}", htmlAttributes: new { maxlength = 8, Name = "Model2.Property" })
But when I inspect html code, name attribute is not overridden.
How can I override it?
Thank you.
You can use TextBox:
#Html.TextBox("Model2.Property",Model.Property,new { maxlength = 8})
or use asp-for and name:
<input asp-for="#Model.Property" name="Model2.Property" maxlength="8"/>
Pass data with prefix in form,and validate it in the action(ModelState.IsValid'),then return the error message to your view,but i think it's not a good idea,i think you don't need prefix in you controller action:
Here is a demo:
TestController:
public IActionResult TestPrefix([Bind(Prefix = "Model2")]DataModel model) {
if (ModelState.IsValid) {
return Ok("success");
}
string message=string.Join("; ", ModelState.Values
.SelectMany(x => x.Errors)
.Select(x => !string.IsNullOrWhiteSpace(x.ErrorMessage) ? x.ErrorMessage : x.Exception.Message.ToString()));
return Ok(message);
}
public IActionResult TestDataModel() {
return View();
}
TestDataModel.cshtml:
<form method="post">
#Html.TextBoxFor(Model => Model.Property, "{0:0}", htmlAttributes: new { maxlength = 8 })
<div style="color:red" id="errormessage">#TempData["Error"]</div>
<button onclick="postModel()">post</button>
</form>
#section scripts{
<script type="text/javascript">
function postModel() {
var formdata = new FormData();
formdata.append("Model2.Property", $("#Property").val());
$.ajax({
type: "POST",
url: '#Url.Action("TestPrefix", "Test")',
contentType: false,
processData: false,
data: formdata,
}).done(function (data) {
//$("#errormessage").append(data);
});
}
</script>
}
DataModel:
public class DataModel
{
[Required]
[Range(0,5)]
public int Property { get; set; }
}
result:

Fluent Validation: Changing control style on error

I'm using Fluent Validation in my ASP.NET MVC application. I need to change the CSS class of the control when there's an error. Please see my code below.
using FluentValidation;
using Hrms.Framework.Common;
using Hrms.Web.Models;
namespace Hrms.Web.InputValidators
{
public class AnnouncementValidator : AbstractValidator<AnnouncementModel>
{
public AnnouncementValidator()
{
RuleFor(a => a.Title).NotEmpty().WithMessage("Announcement Title is required");
RuleFor(a => a.Title).MaximumLength(50).WithMessage(string.Format("Announcement Title should be 50 characters or less", 50));
}
}
}
Here is the code I have in the view (HTML)
#model Hrms.Web.Models.AnnouncementModel
#{
Layout = "~/Views/Shared/_Application.cshtml";
}
#using (Ajax.BeginForm("Save", "Announcement", new AjaxOptions
{
HttpMethod = "POST",
OnFailure = "saveFailed",
OnSuccess = "saveSucc",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "result"
}))
{
#Html.AntiForgeryToken()
<div id="result"></div>
<label>#Resources.Organization.Announcement.Title</label>
#Html.TextBoxFor(m => m.Title, new { maxlength = #GlobalConstants.AnnouncementTitleMaxLength })
#Html.ValidationMessageFor(model => model.Title, null, new { #class = "errVal" })
<input type="submit" value="Save">
}
#section scripts {
<script type="text/javascript">
$(document).ready(function () {
$("#SendingDate").datepicker();
});
function saveSucc(m) {
$("#Title").val("");
$("#Body").val("");
$("#SendingDate").val("");
}
function saveFailed() {
alert("saveFailed");
}
</script>
}
Any help is highly appreciated
When I tried to edit this post, it showed an error saying that I cannot edit this post because it is mostly code, it asked me to add more details. And because there are no more details in my mind to add, I decided to add this text. I'm sorry...

ASP MVC - Html.Action [post] firing for every post request

I have a newsletter subscription form in a website I'm creating (it's part of the layout) and I'm using Html.Action() in my layout.cshtml file to call my Subscribe() action method. The method has 'two' versions: one for get and other for post requests.
Problem:
the Subscribe() POST action method gets called whenever any other form is submitted - I don't want that: it should only be called when someone clicks on the 'subscribe' button (then there's an ajax call to update the page without reloading)
For instance, in my contacts page, when I submit the form, the Subscribe() post method also gets called
I'm not sure why this happens but I believe it is because there's a post request and hence my Subscribe() POST method gets called automatically instead of the Subscribe() GET one.I tried using Html.Partial for this instead but it doesn't work because I have to pass a model to the partial view through the layout.cshtml file
_layout.cshtml:
// more code
#Html.Action("Subscribe", "Newsletter", new { area = "" })
// mode code
NewsletterController:
public ActionResult Subscribe() {
NewsletterSubscribeVM model = new NewsletterSubscribeVM();
return PartialView("NewsletterSubscription", model);
}
/// always gets called, even when it shouldn't
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Subscribe(NewsletterSubscribeVM subscriber) {
string message;
if (!ModelState.IsValid) {
message = "Ops! Something went wrong.";
return Json(message, JsonRequestBehavior.AllowGet);
}
Newsletter sub = new Newsletter { Email = subscriber.SubscriberEmail };
this._service.Add(sub);
message = "Thank you for subscribing!";
return Json(message, JsonRequestBehavior.AllowGet);
}
}
NewsletterSubscription.chtml
#model Web.ViewModels.NewsletterSubscribeVM
#using (Html.BeginForm("Subscribe", "Newsletter", new { area = "" }, FormMethod.Post, new { id = "newsletter-form", #class = "col-xs-12" })) {
#Html.AntiForgeryToken()
#Html.Label("Subcribe!", new { id = "newsletter-msg", #class = "col-xs-12 no-padding" })
#Html.TextBoxFor(m => m.SubscriberEmail, new { placeholder = "Email", #class = "col-xs-7", id = "newsletter-box" })
<button id="newsletter-btn" class="col-xs-1">
<i class="fa fa-arrow-right absolute-both-centered" aria-hidden="true"></i> </button>
}
Javascript:
$('#newsletter-btn').click(function (e) {
var form = $('#newsletter-form');
e.preventDefault();
// if the user has inserted at least one character and the 'thank you' msg isn't showing yet:
if ($('#newsletter-box').val().length != 0 && $('#subscription-status-msg').length == 0) {
$.ajax({
method: "POST",
url: "/newsletter/subscribe",
data: form.serialize(),
dataType: "json",
success: function(data) {
$('<div id="subscription-status-msg" class="col-xs-12 no-padding">' + data + '</div>').appendTo($("#newsletter-form")).hide().fadeIn(500);
}
})
}
})
Thanks in advance!

Fill dependent drop down list by using json

I have two drop down list that second one fill by on-change of first one.
I using json for ,but it doesn't work. here is my code:
<div class="col-md-6">
#Html.LabelFor(model => model.Counterparts.First().Name, new {#class = "control-label"})
#Html.DropDownListFor(m => m.CounterpartId, new SelectList(Model.Counterparts, "Id", "Name"), "select", new {#id = "SelectDepartment", onchange = "getData();"})
</div>
<div class="col-md-6">
#Html.LabelFor(model => model.Accounts.First().Name, new { #class = "control-label" })
#Html.DropDownListFor(m => m.AccountId, new SelectList(Model.Accounts, "Id", "Name"), "select", new { #class = "form-control" })
</div>
<script type="text/javascript" language="javascript">
function getData() {
var e = document.getElementById("SelectDepartment");
var counterpartid = e.options[e.selectedIndex].value;
alert('/ProcessCounterpart/GetCounterpartAccounts/' + counterpartid.toString());
$.getJSON('/ProcessCounterpart/GetCounterpartAccounts/' + counterpartid.toString(), function (data) {
alert(data);
});
}
</script>
public JsonResult GetCounterpartAccounts(int id)
{
var accounts = _accountService.GetAllAccountByCounterpartId(id);
return Json(accounts,JsonRequestBehavior.AllowGet);
}
I would personally suggest to use a selectize plugin. It has onchange event that fires each time you change the drop down. You can also fill options via ajax call which is what are you looking for. Here is an example:
$("#countriesDropDown").selectize({
load: function(query, callback) {
$.get("api/getCountries").done(function (data) {
if(data !== '') { callback(data) })
.fail(function(xmlHttpRequest, textStatus, errorThrown) {});
},
onChange: function(value) { loadCitylistByCountry(value); }
});
var $citiesSelectize = $("#citiesDropDown").[0].selectize;
function loadCitylistByCountry(value) {
$citiesSelectize.load(function(callback) {
$.get("api/getCitiesByCountry?coutryId=" + value).done(function (data) {
if(data !== '') { callback(data) })
.fail(function(xmlHttpRequest, textStatus, errorThrown) {});
});
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="countriesDropDown" type="text" />
<input id="citiesDropDown" type="text" />
Please note that this is just an example. This code might not even work. This is to only show you how you can use it. You can go to their site there you will find a lot of examples as well as the api.
Hope this will help
I suggest that you research ways on how to implement Cascading DropdownLists in MVC. There are many articles online such as:
Easiest way to create a cascade dropdown in ASP.NET MVC 3 with C#
http://www.c-sharpcorner.com/UploadFile/4d9083/creating-simple-cascading-dropdownlist-in-mvc-4-using-razor/
I'd go the JQuery route though; as it is the easiest one. The idea here is to populate both dropdowns and then use JQuery to hide or disable the options in the Child dropdown (i.e., the second one) based on selection in the Parent (i.e., the first one).

Partial view render on button click

I have Index view:
#using System.Web.Mvc.Html
#model MsmqTestApp.Models.MsmqData
<!DOCTYPE html>
<html>
<head>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
<meta name="viewport" content="width=device-width" />
<title>MsmqTest</title>
</head>
<body>
<div>
<input type="submit" id="btnBuy" value="Buy" onclick="location.href='#Url.Action("BuyItem", "MsmqTest", new { area = "Msmq" })'" />
<input type="submit" id="btnSell" value="Sell" onclick="location.href='#Url.Action("SellItem", "MsmqTest", new { area = "Msmq" })'" />
</div>
<div id="msmqpartial">
#{Html.RenderPartial("Partial1", Model); }
</div>
</body>
</html>
and partial:
#using System.Web.Mvc.Html
#model MsmqTestApp.Models.MsmqData
<p>
Items to buy
#foreach (var item in Model.ItemsToBuy)
{
<tr>
<td>#Html.DisplayFor(model => item)
</td>
</tr>
}
</p>
<p>
<a>Items Selled</a>
#foreach (var item in Model.ItemsSelled)
{
<tr>
<td>#Html.DisplayFor(model => item)
</td>
</tr>
}
</p>
And controller:
public class MsmqTestController : Controller
{
public MsmqData data = new MsmqData();
public ActionResult Index()
{
return View(data);
}
public ActionResult BuyItem()
{
PushIntoQueue();
ViewBag.DataBuyCount = data.ItemsToBuy.Count;
return PartialView("Partial1",data);
}
}
How to do that when i Click one of button just partial view render, now controller wants to move me to BuyItem view ;/
The first thing to do is to reference jQuery. Right now you have referenced only jquery.unobtrusive-ajax.min.js but this script has dependency on jQuery, so don't forget to include as well before it:
<script src="#Url.Content("~/Scripts/jquery.jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
Now to your question: you should use submit buttons with an HTML form. In your example you don't have a form so it would be semantically more correct to use a normal button:
<input type="button" value="Buy" data-url="#Url.Action("BuyItem", "MsmqTest", new { area = "Msmq" })" />
<input type="button" value="Sell" data-url="#Url.Action("SellItem", "MsmqTest", new { area = "Msmq" })" />
and then in a separate javascript file AJAXify those buttons by subscribing to the .click() event:
$(function() {
$(':button').click(function() {
$.ajax({
url: $(this).data('url'),
type: 'GET',
cache: false,
success: function(result) {
$('#msmqpartial').html(result);
}
});
return false;
});
});
or if you want to rely on the Microsoft unobtrusive framework you could use AJAX actionlinks:
#Ajax.ActionLink("Buy", "BuyItem", "MsmqTest", new { area = "Msmq" }, new AjaxOptions { UpdateTargetId = "msmqpartial" })
#Ajax.ActionLink("Sell", "SellItem", "MsmqTest", new { area = "Msmq" }, new AjaxOptions { UpdateTargetId = "msmqpartial" })
and if you want buttons instead of anchors you could use AJAX forms:
#using (Ajax.BeginForm("BuyItem", "MsmqTest", new { area = "Msmq" }, new AjaxOptions { UpdateTargetId = "msmqpartial" }))
{
<button type="submit">Buy</button>
}
#using (Ajax.BeginForm("SellItem", "MsmqTest", new { area = "Msmq" }, new AjaxOptions { UpdateTargetId = "msmqpartial" }))
{
<button type="submit">Sell</button>
}
From what I can see you have already included the jquery.unobtrusive-ajax.min.js script to your page and this should work.
Maybe not the solution you were looking for but, I would forget about partials and use Javascript to call the server to get the data required and then return the data to the client as JSON and use it to render the results to the page asynchronously.
The JavaScript function;
var MyName = (function () {
//PRIVATE FUNCTIONS
var renderHtml = function(data){
$.map(data, function (item) {
$("<td>" + item.whateveritisyoureturn + "</td>").appendTo("#msmqpartial");
});
};
//PUBLIC FUNCTIONS
var getData = function(val){
// call the server method to get some results.
$.ajax({ type: "POST",
url: "/mycontroller/myjsonaction",
dataType: "json",
data: { prop: val },
success: function (data) {
renderHtml();
},
error: function () {
},
complete: function () {
}
});
};
//EXPOSED PROPERTIES AND FUNCTIONS
return {
GetData : getData
};
})();
And on the Server....
public JsonResult myjsonaction(string prop)
{
var JsonResult;
// do whatever you need to do
return Json(JsonResult);
}
hope this helps....

Resources