As stated in the title: how does razor know when the page should be rendered in error or normal state?
I need to know this information so I can display a page in error mode with validation messages displayed etc. Unfortunately I cannot simply do View() because the code which finds the model in invalid state is in subview.
EDIT
I'm trying to do something like this to force razor to render a view in error mode:
// just for tests....
// model: a model which was marked as invalid in different controller
// state: state of the model from that controller
public ActionResult asdf(TModel model, ModelStateDictionary state) {
var result = View(this.Partial, model);
result.ViewData.ModelState.Clear();
foreach (var x in state) {
result.ViewData.ModelState.Add(x.Key, x.Value);
}
return result;
}
EDIT2
Final solution. In the previous attempt auto-deserialization from Json to c# types didn't work (sic!) so I've decided to receive a plain json string and deserialize it with another library like so:
public ActionResult ErrorIndex(string jsonParamsString) {
var param = Newtonsoft.Json.JsonConvert.DeserializeObject<ForceInvalidStateRequestArg<TModel>>(jsonParamsString);
if (param != null && param.Errors != null) {
this.ModelState.Clear();
foreach (var s in param.Errors) {
this.ModelState.AddModelError(s.PropertyName, s.ErrorMessage);
}
}
var result = View(this.PartialName, param == null ? this.NewModel : param.Model);
return result;
}
Information about model validity is stored in ModelState object.
Which is accessible in controller like:
this.ModelState.IsValid
In view:
this.ViewContext.ViewData.ModelState
To your EDIT:
You can add model validation errors like this:
this.ModelState.AddModelError("key", "an error message");
So if you want to force #razor to render validation error messages. It could look like:
public ActionResult asdf(TModel model, ModelStateDictionary state)
{
var result = View(this.Partial, model);
result.ViewData.ModelState.Clear();
foreach (var pair in state.Where(m=> m.Value != null && m.Value.Errors.Any()))
{
result.ViewData.ModelState.AddModelError(pair.Key, string.Join(",",pair.Value.Errors.Select(e=>e.ErrorMessage).ToArray()));
}
return result;
}
Related
I have the following code:
#foreach (var item in #ViewBag.list)
{
<span>#item.Id </span>
}
The ViewBag gets the data from a service that contains a class that gets the data from a database, but I'm getting a null exception error since the ViewBag is null because a user has to first enter a date to be able to get the list of ids.
public async Task<ActionResult> plan_post(DateTime dateselected)
{
DateTime date = dateselected;
var plan = await _ps.PlanbyDate(date, date);
ViewBag.list = plan;
return View();
}
I'm a student doing an internship can you help me with this?
this is the error:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
Fo fixed your problem you must debugging your plan object may be null !!!
But you must check if viewbag null not render foreach
if(ViewBag.list != null)
{
//write for each in here
}
You have more options for pass Model to view.
I recommend you use pass with View function
retune View(plan)
But must declaration datatyoe model in top page view
#model YourDataType
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.
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.
I have the following Details action in SampleController:
public ActionResult Details(int sampleNumber)
{
var sample = (Sample)Session["sample"];
if (sample == null)
{
var pallet = (Pallet)Session["pallet"];
sample = pallet.Samples.First(s
=> s.SampleNo.Equals(sampleNumber));
if (sample.Defects.Count < 1) // Postback issue?
{
var access = new Access();
sample.Defects = access.GetDefects(pallet.Grv.GRVNo,
pallet.PalletSeq, sampleNumber);
sample.GetImagePaths();
sample.Pallet = pallet;
Session["sample"] = sample;
}
}
return View(sample);
}
And this Update action:
public ActionResult Update(IEnumerable<HttpPostedFileBase> files, Sample sample)
{
var pallet = (Pallet)Session["pallet"];
sample.Pallet = pallet;
sample.SaveImages(files);
access.UpdateSample(sample);
access.UpdateDefects(sample);
Session["sample"] = sample;
return View("Details", sample);
}
I am trying to debug an issue, but somehow the line return View("Details", sample); is not calling the above Details action (the breakpoint does not stop the code).
It does return a view of the selected sample, but none of the operations present in Details are occurring.
I tried changing the return statement to
return View("Details", sample.SampleNo);
To match the signature of Details, but then I get :
The model item passed into the dictionary is of type 'System.Int32', but this dictionary requires a model item of type 'MVCQCPage.Models.Sample'.
How is that possible? the Details action does NOT ask for a Sample param, so why does this not just return Details and pass in the sampleNo (int) value?
Note that the above Details action is the only method of that name in SampleController.
You need to do RedirectToAction
public ActionResult Update(IEnumerable<HttpPostedFileBase> files, Sample sample)
{
var pallet = (Pallet)Session["pallet"];
sample.Pallet = pallet;
sample.SaveImages(files);
access.UpdateSample(sample);
access.UpdateDefects(sample);
Session["sample"] = sample;
return RedirectToAction("Details", sample.SampleNo);
}
Please check https://msdn.microsoft.com/en-us/library/system.web.mvc.controller.redirecttoaction(v=vs.118).aspx
As the other answers mention, I need to use RedirectToAction.
However, i also need to pass in a named sampleNumber parameter:
return RedirectToAction("Details", new { #sampleNumber = sample.SampleNo });
You need to use RedirectToAction:
public ActionResult Update(IEnumerable<HttpPostedFileBase> files, Sample sample)
{
var pallet = (Pallet)Session["pallet"];
sample.Pallet = pallet;
sample.SaveImages(files);
access.UpdateSample(sample);
access.UpdateDefects(sample);
Session["sample"] = sample;
return RedirectToAction("Details", sample);
}
The View() method returns the specified view without invoking the Details Action, however the RedirectToAction() method redirects to the specified action not the View().
I have a problem transfering data from one view to another via the controler actions.
I the first view is a grid.mvc-Grid displayed. By select on row of the grid I get the ID for that object.
by transfering this to an action in the controler I try to filter the data. That works fine.
Here is the filter:
[HttpGet]
public ActionResult PersonenById(int id)
{
var personen = new ObservableCollection<Person>();
//Getting the data here :-)
foreach (DataRow r in access.Rows)
{
Person p = new Person();
//do some stuff
personen.Add(p);
}
//return PartialView("Personen", personen); //does not work
TempData["personen"] = personen;
return RedirectToAction("Personen"); // redirect to another view
}
In method II the view is filled:
public ActionResult Personen()
{
var persons = new ObservableCollection<Person>();
if (TempData["Persons"] == null)
{
}
return View(persons); //Works perfect
}
else
{
persons = (ObservableCollection<Person>) TempData["Persons"];
return View(persons);//does not redirect to that View
}
}
(Sorry for the strange formating. :-))
Is there any different way to send data from a view to another?
I tried:
return partial;
return View("Persons",persons);
and a lot other stuff.
You can redirect in a .cshtml view.
Eg:
Context.Response.StatusCode = 403;
Context.Response.Redirect(
$"{Context.Request.PathBase}/Error/403", false);
Should work like this:
return RedirectToAction("Personen", model);
Also, the "Personen" action should have the model as an argument, like this:
public ActionResult Personen(Person model) ...
LE: I have also noticed you have tried to send the data through the TempData object. Make sure the indexed object's name is the same (e.g. TempData["person"] everywhere)
Hope it answers your question.