I'm having a little issue with an Edit view on a ViewModel. When I post my Edit view to the server for the first time, it needs to return the Edit view again with the same ViewModel where the database ID has been appended to.
This is the Edit method in the appropriate controller:
[HttpPost]
public ActionResult Edit(InvoiceDetailsViewModel invoice) {
using (var context = new HyperContext(WebSecurity.CurrentUserId)) {
if (ModelState.IsValid) {
if (invoice.ID == 0) {
var dbItem = Mapper.Map<eu.ecmt.RecruitmentDatabase.Models.Invoice>(invoice);
context.Invoices.Add(dbItem);
context.SaveChanges();
var newInvoice = Mapper.Map<InvoiceDetailsViewModel>(dbItem);
FillViewBag(context, newInvoice);
newInvoice.Description = "TEST";
return PartialView(newInvoice);
}
else {
context.Entry(Mapper.Map<eu.ecmt.RecruitmentDatabase.Models.Invoice>(invoice)).State = System.Data.EntityState.Modified;
context.SaveChanges();
return Content(Boolean.TrueString);
}
}
FillViewBag(context, invoice);
return PartialView(invoice);
}
}
The relevant part here is where the invoice.ID is 0, the invoice is saved to the DB to get an ID and returned to the Edit view.
In that view I got these lines for starters:
#model eu.ecmt.RecruitmentDatabase.ViewModels.InvoiceDetailsViewModel
#using (Html.BeginForm("Edit", "Invoice", FormMethod.Post, new { id = "invoices-edit-form" })) {
#Html.ValidationSummary(true)
<script type="text/javascript">
$(document).ready(function () {
//$("#tabs").tabs();
InitProfileUI();
});
</script>
if (Model.ID != 0) {
<script type="text/javascript">
$(document).ready(function () {
LoadList('/InvoiceDetail/List/#Model.ID', '', 'invoice-details');
});
</script>
}
<fieldset>
<legend>Edit contract</legend>
#Html.HiddenFor(m => m.ID)
#Html.HiddenFor(m => m.InvoiceNumber)
#Html.HiddenFor(m => m.Created)
#Html.HiddenFor(m => m.CreatedBy)
#Html.HiddenFor(m => m.Modified)
#Html.HiddenFor(m => m.ModifiedBy)
When first rendering this view, the script element containing the LoadList call is not in the output. When the form is posted and the view is rendered with the updated viewmodel, that element is in the output. The hidden field containing the ID of the invoice, though, still shows 0. So, in essence, what is happening here, is that the Model object in the ViewData dictionary is the correct version, the object that is being used in the expressions seems to be another, older, version.
Anyone care to explain this and point me into the right direction?
It seems to be behavior by design, according to this post.
Summary: the HTMLHelper will first use the values in the POST, then it will use the values from the actual model. Removing them from ModelState in the controller method did the trick:
[HttpPost]
public ActionResult Edit(InvoiceDetailsViewModel invoice) {
using (var context = new HyperContext(WebSecurity.CurrentUserId)) {
if (ModelState.IsValid) {
if (invoice.ID == 0) {
ModelState.Remove("ID");
ModelState.Remove("Created");
ModelState.Remove("CreatedBy");
ModelState.Remove("Modified");
ModelState.Remove("ModifiedBy");
var dbItem = Mapper.Map<eu.ecmt.RecruitmentDatabase.Models.Invoice>(invoice);
context.Invoices.Add(dbItem);
context.SaveChanges();
invoice = Mapper.Map<InvoiceDetailsViewModel>(dbItem);
FillViewBag(context, invoice);
return PartialView(invoice);
}
else {
context.Entry(Mapper.Map<eu.ecmt.RecruitmentDatabase.Models.Invoice>(invoice)).State = System.Data.EntityState.Modified;
context.SaveChanges();
return Content(Boolean.TrueString);
}
}
FillViewBag(context, invoice);
return PartialView(invoice);
}
}
Related
I have a partialview _Psite which contains two dropdownlist and a text box, second one is based one first as Jsonresult (cascading dropdowns). So now suppose if customer select values in first dropdownlist, second one will load based on jquery and json.Then when he enter wrong values in text box validation fails(Session["username"] == null) it will display the same partial view after post in order to reenter .The problem now i am facing is the two dropdownlist is resetting in to default values.I have googled but couldn't find a solution
Following is view of _Psite
#using (Ajax.BeginForm("_firstGridAll", "mnis", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "PsitegridContent" }))
{
<div style="float: left">
#Html.DropDownList("REGION_CODE", (SelectList)ViewBag.Categories, "Select region code")
#Html.ValidationMessageFor(m => m.REGION_CODE)
</div>
<div class="tested">
<select id="GEO_ZONE_CODE" name="GEO_ZONE_CODE"></select>
</div>
<div class="tested">
#Html.TextBoxFor(m => m.PSITE_ID)
#Html.ValidationMessageFor(m => m.PSITE_ID)
</div>
<div class="Testedagain">
<input type="submit" value="Search" />
</div>
}
Controller is
public ActionResult _Psite()
{
if (TempData["values"].ToString() == "value persists")
{
ViewBag.change = true;
// ViewBag.Categories = TempData["EnterUniqueKeyHere"];
// return PartialView("_failValidation");
}
var categories = db1.MN_PSITE.Select(c => new
{
REGION_CODE = c.REGION_CODE,
CategoryName = c.REGION_CODE
}).Distinct().ToList();
ViewBag.Categories = new SelectList(categories, "REGION_CODE", "CategoryName");
ViewBag.error = false;
ViewBag.change = false;
return PartialView();
}
and the controller for validating data is following
[HttpPost]
public ActionResult _firstGridAll(string REGION_CODE, string GEO_ZONE_CODE, string PSITE_ID)
{
if (ModelState.IsValid == true)
{
Session["username"] = null;
var items = db1.MN_PSITE.Where(x => x.REGION_CODE == REGION_CODE).Where(y => y.GEO_ZONE_CODE == GEO_ZONE_CODE).Where(z => z.PSITE_ID == PSITE_ID);
//db1.MN_PSITE.Where(x => x.REGION_CODE == Region).Where(y => y.GEO_ZONE_CODE == GeoZONE).Where(z => z.PSITE_ID == Psiteid);
foreach (var it in items)
{
Session["username"] = it.PSITE_SLNO.ToString();
return PartialView(items.ToList());
}
if (Session["username"] == null) //validation fails
{
TempData["error"] = "value doesnot exisit,please renter the details";
return RedirectToAction("_Psite");
}
}
//count = 0;
return PartialView(db1.MN_PSITE.ToList());
}
UPDATE
i am using Entityframework generated classes as model no view viewmode ,do here 'db' is an instance of entity class
If you were posting a view model into your action instead of individual parameters, then you would be able to simply pass that model back out in your partial at the end of the action.
It will only be a small class with a few properties, so will take a minute to create. Do it. It will give you what you want.
i have some problems with my input data in asp.net mvc on controller site. My model include another model which is binded on some textboxs.
My main model Order include a model Customer (with a string property of first name).
Example main page (i use dev express controls), i give the model to the group:
#model Models.Order
#using (Html.BeginForm("Index", "Main"))
{
<div>
#Html.DevExpress().NavBar(settings =>
{
settings.Name = "nbWebshopSteps";
settings.EnableClientSideAPI = true;
settings.Width = new Unit(100, UnitType.Percentage);
settings.Groups.Add(group =>
{
group.Text = "Bestellung bestätigen";
group.Expanded = false;
group.ShowExpandButton = DefaultBoolean.False;
group.ContentStyle.Border.BorderColor = Color.FromArgb(247, 242, 212);
group.SetHeaderTemplateCollapsedContent(c => Html.RenderPartial("../Main/NavBarHeaderCollapsed", c.Group));
group.SetHeaderTemplateContent(c => Html.RenderPartial("NavBarHeader", c.Group));
group.SetContentTemplateContent(c => Html.RenderPartial("PersonalData", Model));
});
}).GetHtml()
}
My personal data pagem, which is rendered into the group (it comes from a dll):
#model Objects.Customer <div>
#Html.DevExpress().TextBox(settings =>
{
settings.Name = "txtFirstName";
settings.Properties.Caption = "Vorname";
settings.EncodeHtml = false;
settings.Properties.CaptionStyle.Font.Size = 14;
settings.Width = 385;}).Bind(Model.Firstname).GetHtml()</div>
After the postback my customer model is empty (first name).
[HttpPost]
public ActionResult Index(Order order)
{
}
Can someone help me?
Thanks
Take a look at this article.
DevExpress support recommend to use code like this:
#Html.DevExpress().TextBox(settings => {
//settings.Name = "Street";
settings.Name = ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName("Street");
}).Bind(Model.Street).GetHtml()
I have a Captcha control for my MVC 4 page and I cannot get it to show a message if the input was incorrect. I'm used to doing things through jquery and on success do something, but when I do something like that here I lose the ModelState.IsValid.
So, when I run this code the Captcha control loads fine on the page it shows the 5 letters in an image with a line that says 'Refresh' and a textbox beneath that for input with a submit button on my index page to post to the controller.
When I get input wrong it refreshes the image with no message saying anything was wrong, I know it was wrong because my controller says ModelState.IsValid is false but I want to load a new image and display that the input was incorrect.
When I get input correct it refreshes the image still with no message or anything. I want it to stay there and say that the input was correct and disable the textbox.
My question: How can I do what I described above?
My code is below:
Controllers/HomeController.cs
using System.Web.Mvc;
using CaptchaDemo.MVC4.ViewModels;
using CaptchaMvc;
using CaptchaMvc.Attributes;
using CaptchaMvc.Infrastructure;
namespace CaptchaDemo.MVC4.Controllers
{
public class HomeController : Controller
{
// GET: /Home/
public ActionResult Index()
{
CaptchaUtils.CaptchaManager.StorageProvider = new CookieStorageProvider();
ViewBag.Title = "Captcha MVC 4 Demo";
return View();
}
public ActionResult _Captcha()
{
CaptchaViewModel model = new CaptchaViewModel();
return View(model);
}
public ActionResult AjaxForm()
{
return View(new CaptchaViewModel());
}
[HttpPost, CaptchaVerify("Captcha is not valid")]
public ActionResult AjaxForm(CaptchaViewModel model)
{
if (ModelState.IsValid)
{
ModelState.Clear();
TempData["Message"] = "Message: captcha is valid.";
model.strMessage = "efefwf";
if (Request.IsAjaxRequest())
return PartialView("_Captcha", model);
//return Json(model, JsonRequestBehavior.AllowGet);
return View(model);
}
TempData["ErrorMessage"] = "Error: captcha is not valid.";
if (Request.IsAjaxRequest())
return PartialView("_Captcha", model);
return View(model);
}
}
}
ViewModels/CaptchaViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace CaptchaDemo.MVC4.ViewModels
{
public class CaptchaViewModel
{
public string strMessage { get; set; }
}
}
Views/Home/Index.cshtml
#using (Html.BeginForm("AjaxForm", "Home", FormMethod.Post, new { #id = "AjaxCaptchaForm", #class = "ajax" }))
{
<div id="update">#Html.Partial("_Captcha")</div>
<input type="submit" />
}
<script type="text/javascript">
$(document).ready(function () {
$('#AjaxCaptchaForm').submit(function () {
$.post($(this).attr("action"), $(this).serialize(), function (results) {
$("#update").html(results);
});
return false;
});
});
</script>
Views/Shared/_Captcha.cshtml
#using CaptchaMvc.HtmlHelpers
#model CaptchaDemo.MVC4.ViewModels.CaptchaViewModel
#Html.ValidationSummary(true)
#Html.ValidationMessageFor(model => model.strMessage)
#Html.Captcha(5)
<span>#Model.strMessage</span>
In case someone still need help with this:
There are two options one:
#Html.Captcha("Refresh", "Captcha is not valid "
, 4, "The Captcha is required", true)
The last true set the bool addValidationSpan.
Another option:
<span class="field-validation-valid text-danger" data-valmsg-for="CaptchaInputText" data-valmsg-replace="true" id="vali_CaptchaInputText"></span>
<span class="field-validation-valid text-danger" data-valmsg-for="CaptchaDeText" data-valmsg-replace="true" id="vali_CaptchaDeText"></span>
Also the <script src="~/Scripts/jquery-2.1.4.js"></script> need to be loaded before this line is rendered.
#Html.Captcha(5) renders an input with id="CaptchaInputText". To display a native warning message you need to append you model with CaptchaInputText property and add
#Html.ValidationMessageFor(m => m.CaptchaInputText, String.Empty, new { #class = "validation-error", #style = "color:red;" })
into your view.
I have a devExpress xtraReport that is being supplied by a strongly typed dataset. As long as I'm hard coding two parameters into the Actions, it loads the data into the dataset and displays in the report. Once I try to make it pass the values from the main page down through the partial, it fails. My first attempt was to pass the parameters through the ViewBag, wasn't working, so switched to a model, still not working right.
main page controller
public ActionResult SubsequentVisitReport(int noteType = 1, int noteId = 9)
{
ViewBag.noteType = noteType;
ViewBag.noteId = noteId;
ReportParameters reportParamters = new ReportParameters();
reportParamters.noteType = noteType;
reportParamters.noteId = noteId;
return View(reportParamters);
}
main page cshtml - added in the EditorFor to make sure the model makes it there (it does). Have tried calling the Partial both with and without putting 'Model'
#model ReportParameters
#Html.EditorFor(m => m.noteId)
#Html.EditorFor(m => m.noteType)
#Html.HiddenFor(m => m.id)
#Html.HiddenFor(m => m.noteType)
#Html.HiddenFor(m => m.noteId)
#Html.Partial("_SubsequentVisitReport", Model)
controller for the partial - this does not receive the data from the model and I don't understand why. The model is NOT null, all the values are 0 (zero).
[HttpPost]
public ActionResult _SubsequentVisitReport(ReportParameters model)
{
int noteType = model.noteType;
int noteId = model.noteId;
rptSubsequentVisit report = new rptSubsequentVisit();
try { report.DataSource = getSubsequentVisitData(model.noteType, model.noteId).Tables[0]; }
catch { return RedirectToAction("Not_Authorized"); }
ViewData["Report"] = report;
return PartialView("_SubsequentVisitReport");
}
The view for the partial
#model ReportParameters
#Html.HiddenFor(m => m.id)
#Html.HiddenFor(m => m.noteType)
#Html.HiddenFor(m => m.noteId)
#Html.DevExpress().DocumentViewer(settings =>
{
// The following settings are required for a Report Viewer.
settings.Name = "reportViewer1";
settings.Report = (rptSubsequentVisit)ViewData["Report"];
// Callback and export route values specify corresponding controllers and their actions.
// These settings are required as well.
settings.CallbackRouteValues = new { Controller = "Reports", Action = "_SubsequentVisitReport"};
settings.ExportRouteValues = new { Controller = "Reports", Action = "_SubsequentVisitReportExport" };
}).GetHtml()
The data needs to persist through the partial both to load the note for viewing, but also for the export function.
What am I doing wrong, or is there another better way to do this?
Thanks,
Dave K.
The settings.CallbackRouteValues object tells the DocumentViewer where to request the actual report, and it can take parameters. Unfortunately it will be a separate request, so you can't send your model, only simple values that can be passed as strings. In this example, they are using a custom model for the report, but the model has to be re-created from raw values in each action.
If you convert your partial action to take integer parameters:
public ActionResult _SubsequentVisitReport(int noteType, int noteId)
you should be able to tack those arguments on the end of the CallbackRouteValues:
settings.CallbackRouteValues = new { Controller = "Reports",
Action = "_SubsequentVisitReport",
noteType = model.noteType,
noteId = model.noteId};
I have a view the contains an #Html.DropDownListFor. When the form/view loads, if one of the model properties has values (IEnumerable), then it will create a bunch of divs with the corresponding data. If that property does not have any values (aka Count() == 0), then it is supposed to show a single button on the form (which will create the data for that property).
So, when the user selects one of the options from the Dropdown, I fire an ajax call to the exact same action method that populated the current form/view, but this time, it sends a value in the id field.
I have a breakpoint in my action method and I verified that it is getting hit, and it has the correct parameter value and creates the correct data for the model that gets passed to the view, BUT...when the model is sent to the view to re-populate, NONE of the items/controls on the form change. I even put breakpoints in the cshtml file and it's going through there with the correct data also.
So, here's my controller:
public ActionResult Index(int? id)
{
var seasonId = id;
if (seasonId == null)
{
var player = _playerRepository.Query().FirstOrDefault(p => p.PlayerId == _userIdentity.PlayerId);
if (player.DefaultSeasonId != null)
seasonId = (int)player.DefaultSeasonId;
else
{
return View(new ScheduleModel
{
Player = player,
AvailableSeasons = _seasonRepository.Query().Select(s => s)
});
}
}
return View(CreateScheduleModelForSeason((int)seasonId));
}
Here's the beginning of my view:
#model LeagueManager.Models.ScheduleModel
#{
ViewBag.Title = "Schedule(s)";
}
<div class="row">
#Html.LabelFor(m => m.AvailableSeasons)
#Html.DropDownListFor(m => m.SelectedSeasonId, new SelectList(Model.AvailableSeasons, "SeasonId", "SeasonName"), new { id = "seasonSelect" })
</div>
<form method="post" action="Schedule/GenerateSchedule">
<h2>The Season's Schedules/Weeks and Matchups</h2>
<div>
<div>
#if (Model.SchedulesAndMatches == null || (Model.SchedulesAndMatches != null && !Model.SchedulesAndMatches.Any()))
{
<input type="submit" class="btn btn-primary" value="Generate Schedule" />
}
</div>
And here's the ajax call:
#* Season Selector *#
$('select#seasonSelect').change(function () {
var selectedSeasonId = $(this).val();
$.ajax({
url: '/Schedule/Index',
data: { id: selectedSeasonId }
});
});
Again, all of the actual code is working, it's just not re-rendering the view...
Example: when calling the ActionResult method with an id = 1, it loads the entire schedule. When switching to id = 2 via the dropdown (then getting called again via the ajax), it stays with the same schedule.
On the flip side: when calling the ActionResult method with an id = 2, it loads the single button. When switching to id = 1 via the dropdown, it re-populates the correct data in the model, but the view/form does not reflect the new information.
Please help!
When you call action using ajax you can’t return view, you have to return json data.
So your solution is remove ajax call and set window.location with your post url..
#* Season Selector *#
$('select#seasonSelect').change(function () {
var selectedSeasonId = $(this).val();
window.location = '/Schedule/Index/' + selectedSeasonId;
});