Model Value is not setting - c#

I am creating an CRUD Application in Asp.Net Core
After Add Operation I am redirecting to same view with setting model value as null to get another entry
Below is my code
public IActionResult Add(OptionMasterVM model)
{
try
{
model.QuestionList = context.QuestionMaster.Select(x => new SelectListItem { Text = x.QuestionName, Value = x.QuestionId.ToString() }).ToList();
if (HttpContext.Request.Method == "POST")
{
OptionMaster _optionmaster = new OptionMaster();
_optionmaster = model.OptionMaster;
using (var ctx = new QuestionnaireEntities(_configuration))
{
ctx.OptionMaster.Add(_optionmaster);
ctx.SaveChanges();
}
TempData["Msg"] = "Option Added Successfully , Add Another Option";
model.OptionMaster.OptionValue = string.Empty;
model.OptionMaster.OptionRating = 0;
return View(model);
}
}
catch (Exception ex)
{
logger.LogError(ex);
}
finally
{
}
return View(model);
}
Here I am setting Option Value to empty and rating to Zero to take next entry , but on view it does not show empty and zero , on view it show previously filled value.
After Setting below code these two fields should be reset but they don't
model.OptionMaster.OptionValue = string.Empty;
model.OptionMaster.OptionRating = 0;
Is there any other way to set model object as null in Asp.net Core ?

This can happen because Razor helpers use values from ModelState, rather than the model itself. Your OptionValue is probably displayed using a helper, for example:
#Html.TextBoxFor(m => m.OptionMaster.OptionValue)
When you change model values within an action, you need remove the old values from ModelState before rendering the View.
The easiest way of doing this is to call ModelState.Clear()
model.OptionMaster.OptionValue = string.Empty;
model.OptionMaster.OptionRating = 0;
ModelState.Clear(); // ensure these changes are rendered in the View
return View(model);

The values displayed for bound form fields come from ModelState, which is composed based on values from Request, ViewData/ViewBag, and finally Model. After posting, obviously, you'll have values set in Request, which will therefore be the values in ModelState. It works this way, so that when there's a validation error and the user is returned to the form to correct their mistakes, the values they posted will be there for them to edit.
Long and short, you need to follow the PRG (Post-Redirect-Get) pattern. Essentially, after posting, you only return the view on error. If the post is successful, you redirect. This not only clears ModelState, but also prevents accidental re-posts if the user attempts to refresh the page.
If you want to take the user back to the same view, simply redirect to the same action, but you need to do a redirect, not return the view.

Related

Intermittent results with ASP.NET MVC TempData - sometimes it loses it's value

In an ASP.NET MVC app, I have a controller action which calls business layer to add an entity.
If for business reasons, the entity could not be added, an Error property is set to true along with a description.
If true, I set a TempData key to the error message and then redirect to a view which has code to display the error stored in TempData if it exists. Sometimes the conditional block is shown and sometimes not.
Here is the relevant code in my controller
var added = ModelHelper.CreateSessionCode(model);
if(added.Error)
{
TempData["SessionCodesMessage"] = model.ErrorDescription;
TempData["MessageClass"] = "alert-danger";
}
else
{
TempData["SessionCodesMessage"] = "Created session code";
TempData["MessageClass"] = "alert-success";
}
return RedirectToAction("Index");
Then in my view I have this code:
#if (TempData["SessionCodesMessage"] != null)
{
<div class="alert #TempData["MessageClass"] alert-dismissable" style="margin-top: 8px;">
×
#(TempData["SessionCodesMessage"])
</div>
}
And it seems to be inconsistent when the message is displayed or not. Could this be a browser cache issue or similar? From stepping through the code I can confirm that the execution goes into both controller conditional blocks depending on the result of adding the entity.
Okay so, TempData is like ViewData but with a difference. It can contain data between two successive requests, but after that they are destroyed.
If you want to keep TempData value then you can use Keep:
TempData.Keep()
In your case:
var added = ModelHelper.CreateSessionCode(model);
if(added.Error)
{
TempData["SessionCodesMessage"] = model.ErrorDescription;
TempData.Keep("SessionCodesMessage");
TempData["MessageClass"] = "alert-danger";
TempData.Keep("MessageClass");
}
else
{
TempData["SessionCodesMessage"] = "Created session code";
TempData.Keep("SessionCodesMessage");
TempData["MessageClass"] = "alert-success";
TempData.Keep("MessageClass");
}
return RedirectToAction("Index");
OR
You can also use Peek if you want to be explicit about every time you want to retrieve it without having it deleted.
var added = ModelHelper.CreateSessionCode(model);
if(added.Error)
{
//second request, PEEK value so it is not deleted at the end of the request
TempData["SessionCodesMessage"]; = "Created session code";
object sessioncodevalue= TempData.Peek("SessionCodesMessage");
TempData["MessageClass"]; = "alert-success";
object messageclassvalue= TempData.Peek("MessageClass");
}
else
{
//second request, PEEK value so it is not deleted at the end of the request
TempData["SessionCodesMessage"]; = "Created session code";
object sessioncodevalue= TempData.Peek("SessionCodesMessage");
TempData["MessageClass"]; = "alert-success";
object messageclassvalue= TempData.Peek("MessageClass");
}
return RedirectToAction("Index");
You can use Peek when you always want to retain the value for another request. And use Keep when retaining the value depends on additional logic.
You can refer to this article for more information on these functions and how you can use them in your View: https://www.c-sharpcorner.com/UploadFile/ansh06031982/using-tempdata-peek-and-keep-in-Asp-Net-mvc/

asp.net core mvc 2.1 return a populated model to browser and lost all fields

In the Create action of the controller, based on user input, we plan to populate the model object with some data, to minimize data entry:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Item item, string str)
{
// if only str is provided
if (string.IsNullOrEmpty(item.KeyInfo) && !string.IsNullOrEmpty(str))
{
Helpers.FillItemModel(item, str); //fill data
}
else if (ModelState.IsValid)
{
_context.Add(item);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Create));
}
return View(item);
}
However, although we can confirm the item object has been populated with data to several fields, by setting break point at the last line: return View(item), when the browser gets the response, all fields are empty.
But if we comment out the entire code segment, only leave the return statement and do a post with some data that was manually entered, the browser will receive correct data in all fields.
Thank you for your time.
To update ModelState value you have to reset the ModelState first as follows:
// if only str is provided
if (string.IsNullOrEmpty(item.KeyInfo) && !string.IsNullOrEmpty(str))
{
ModelState.Clear();
Helpers.FillItemModel(item, str); //fill data
}
ModelState.Clear() will reset the whole model. If you don't want that and just want to update few fields value keeping other field value intact then use ModelState["fieldName"].Value = "newValue in your helper class.

Saving Errors When Clearing a Form (MVC)

I'm new to using MVC 5 and .NET framework 4.5 and I have encountered an issue. I have a form, and I want to reset the fields of the form when it errors out, but keep the errors and display to the user. Currently I can clear the fields on error, but this also gets rid of the errors. I have tried
originalModel.field= "";
but this doesn't clear the field. I have also tried just using
ModelState.Clear();
But this doesn't do anything either.
This is what I'm currently working with (which clears everything):
if (!ModelState.IsValid)
{
TestModel blank= new TestModel();
ModelState.Clear();
return View("View.cshtml", blank);
}
If you absolutely need this behavior, you can go and set the value of this property of your view model to empty string in the Modelstate dictionary.
[HttpPost]
public virtual ActionResult Index(CreatePostVm model)
{
if (ModelState.IsValid == false)
{
ModelState["PostTitle"].Value =
new ValueProviderResult(string.Empty, string.Empty,CultureInfo.InvariantCulture);
return View(model);
}
// to do : Return something
}
The above code will set the PostTitle property of your CreatePostVm view model to an empty text. So in your view, the input field will be an empty text while still displaying the validation error message for PostTitle field.

DropDownListFor SelectedValue and Disable using Session State

I have been introduced to Razor as applied with MVC 3 this morning, so please forgive me if my question seems terribly uninformed!
I am working with an app whose workflow involves allowing a user to select a value (warehouse) from a drop down list, and add a record (material) from that warehouse to another record (Materials Request). Once the first material has been added to the Materials Request, I need to permanently set the value of the drop down to the warehouse that was first selected, then disable the drop down control (or set to read only, perhaps). The existing code in the razor file uses the DropDownListFor() method, including a ViewBag collection of Warehouse records. I have seen discussions which suggest abandoning the ViewBag design, but honestly I don't have the desire to rewrite major portions of the code; at least it looks like a major rewrite from the perspective of my experience level. Here's the original code:
#Html.LabelPlusFor(m => m.WarehouseId, "*:")
#Html.DropDownListFor(m => m.WarehouseId, (IEnumerable<SelectListItem>)ViewBag.WarehouseCodes, "")<br />
I believe I have been able to select a value based on a session object, though I'm still not sure how to disable the control. Here's my change:
#{
int SelectedWarehouseId = -1;
if (HttpContext.Current.Session["SelectedWarehouseId"] != null)
{
SelectedWarehouseId = Int32.Parse(HttpContext.Current.Session["SelectedWarehouseId"].ToString());
}
}
#Html.LabelPlusFor(m => m.WarehouseId, "*:")
#{
if (SelectedWarehouseId > -1)
{
#Html.DropDownListFor(m => m.WarehouseId, new SelectList((IEnumerable<SelectListItem>)ViewBag.WarehouseCodes, "WarehouseId", "WarehouseDescription", (int)SelectedWarehouseId))<br />
}
else
{
#Html.DropDownListFor(m => m.WarehouseId, (IEnumerable<SelectListItem>)ViewBag.WarehouseCodes, "")<br />
}
}
When the material is added to the Material Request, the WarehouseId is passed to the controller and I can access that value as "model.WarehouseId" in the controller class. However, I'm not sure how to get that value back to the View (apologies for the large code block here):
[HttpPost]
[TmsAuthorize]
public ActionResult Create(ItemRequestViewModel model)
{
string deleteKey = null;
//Removed code
else if (Request.Form["AddToRequest"] != null)
{
// If the user clicked the Add to Request button, we are only
// interested in validating the following fields. Therefore,
// we remove the other fields from the ModelState.
string[] keys = ModelState.Keys.ToArray();
foreach (string key in keys)
{
if (!_addToRequestFields.Contains(key))
ModelState.Remove(key);
}
// Validate the Item Number against the database - no sense
// doing this if the ModelState is already invalid.
if (ModelState.IsValid)
{
_codes.ValidateMaterial("ItemNumber", model.ItemNumber, model.WarehouseId);
Session["SelectedWarehouseId"] = model.WarehouseId;
}
if (ModelState.IsValid)
{
// Add the new Item Request to the list
model.Items.Add(new ItemViewModel() { ItemNumber = model.ItemNumber, Quantity = model.Quantity.Value, WarehouseId = model.WarehouseId });
ModelState.Clear();
model.ItemNumber = null;
model.Quantity = null;
model.WarehouseId = null;
}
}
//Removed code
return CreateInternal(model);
}
private ActionResult CreateInternal(ItemRequestViewModel model)
{
if (model != null)
{
if (!String.IsNullOrEmpty(model.SiteId))
{
ViewBag.BuildingCodes = _codes.GetBuildingCodes(model.SiteId, false);
if (!String.IsNullOrEmpty(model.BuildingId))
ViewBag.LocationCodes = _codes.GetLocationCodes(model.SiteId, model.BuildingId, false);
}
//Removed code
}
//Removed code
ViewBag.WarehouseCodes = _codes.GetWarehouseCodes(false);
return View("Create", model);
}
So my questions are, how do I disable the drop down list, and how can I pass a value for the selected WarehouseId back to the view? I've also considered adding the value to the ViewBag, but to be honest I don't know enough about the ViewBag to recognize any unintended consequences I may face by just randomly modifying it's contents.
Thanks for any help offered on this.
Without going into which approach is better...
Your dropdown should be rendered as an HTML select element, in order to disable this you'll need to add a disabled="disabled" attribute to it.
The DropDownListFor method has a htmlAttributes parameter, which you can use to achieve this:
new { disabled = "disabled" }
when your pass model to your view like
return View("Create", model);
if WareHouseID is set in model then
Html.DropDownListFor(x=>x.WareHouseID, ...)
will automatically set the selected value and u don't have to do that session processing for this. So far as disabling a field is required, stewart is right. you can disable drop down this way but then it won't be posted to the server when u submit the form. you can set it to readonly mode like
new{#readonly = "readOnly"}

Form values not changing on ASP MVC

I have an edit form, that when posted, if successful should move on to the next record
Here is a snippet of the code in the controller:
if (issues.Count == 0)
{
Service.Save(item);
Service.SaveChanges();
return Edit(NextId, listingName);
}
else
{
ModelState.AddRuleViolations(issues);
}
return Edit(item.id, listingName);
The id for the next record is correctly passed to the action, but the autogenerated form still has the values of the old item, rather than the new one. I have debugged it and the item is getting loaded and passed to the view fine.
Try to do a RedirectToAction instead of returning the View directly.
return RedirectToAction("Edit", new { id = NextId, listingName = listingName });
Also, you are sending the same value of listingName in both cases (validation error and success). Is this correct?
Have you tried to return the Edit View explicitly instead of returning the method call?
Like so:
return View("Edit", NextId);
Perhaps it is still containing the old posted values and tries to repopulate the model accordingly...

Categories