Passing JSON data from controller action to a razor view - asp.net

I would like to know how can I pass data from controller to view in mvc3 razor.
The view is in .cshtml file.
I have a variable in the controller where I am storing the data I need to push to the listbox control in the view.
var results = data.Select(item => new { id = item, label = item.Value, value = item.Key });
Doing this:
return Json(results, JsonRequestBehavior.AllowGet);
Gives me only a popup with the data which needs to be pushed to the listbox:
In the view the listbox resides in accordion control:
<div id="accordion">
#{int i=0;}
#foreach (var item in Model.Parameters)
{
<h3>#Html.LabelFor(m => item.Name, item.Prompt)</h3>
<div>
<div class="editor-field">
<select multiple id="#("Select" +item.Name)" name="#("Select" +item.Name)"></select>
</div>
</div>
i++;
}
</div>
So, I would like to know what should I do to push the items to the listbox instead of showing a popup for the control
Beginner in MVC, Thanks for your understanding.
Thanks in advance, Laziale
EDIT: Json format output
{System.Linq.Enumerable.WhereSelectListIterator<System.Collections.Generic.KeyValuePair<string,string>,<>f__AnonymousType1<System.Collections.Generic.KeyValuePair<string,string>,string,string>>}

returning JSON to your razor view is probably not the best method. I would suggest use a viewModel which is a c# class by itself.
namespace Test
{
public class ViewModel
{
public bool GivingAPresentation { get; set; }
}
}
public class MyController: Controller
{
public virtual ActionResult Index()
{
var model=new ViewModel(){GivingAPresentation =true;}
return View(model);
}
}
Your view code:
#model Test.ViewModel <!-- Note the full namespace -->
<br>GivingAPresentation: #Model.GivingAPresentation </br>
If you are forced to work with a JSON object that is returned from your action then you need to deserialize it first and then work with that object. you can read this post http://www.drowningintechnicaldebt.com/ShawnWeisfeld/archive/2010/08/22/using-c-4.0-and-dynamic-to-parse-json.aspx on how to parse JSON to a c# dynamic object.
Let me know if that helps.

Related

Validation of the MVC5 View Model does not reach the controller

I have an Asp.Net MVC5 project, where when using the requires attribute on the ViewModel, I cannot reach my controller in the event of an invalid ViewModel. But, it only happens on a specific screen.
I need that, even with my wrong VM, this request reaches my controller, so that another action takes place in my View (in this case, a spinner is hidden).
An example of my codes:
ViewModel:
public class ParameterizationViewModel
{
/// <summary>
/// Name of Parameterization.
/// </summary>
[Required(ErrorMessageResourceName = "LabelErrorFieldRequired", ErrorMessageResourceType = typeof(ResourcesGSC.Language))]
[Display(Name = "LabelName", ResourceType = typeof(ResourcesGSC.Language))]
public string Name { get; set; }
}
Controller:
public class ParameterizationController : BaseController
{
[HttpGet]
public ActionResult Index(string id)
{
var model = new ParameterizationViewModel();
if (String.IsNullOrEmpty(id))
{
//Code omitted
//Here, I structure my model to be a clean view
}
else
{
//Code omitted
//Here, I structure my model to be a screen filled with recovered data
}
return View(model);
}
[HttpPost]
public ActionResult Index(ParameterizationViewModel model)
{
if (!ModelState.IsValid)
{
//Here, I validate my ViewModel. I need you to get here, but it doesn't.
return View(model);
}
//Code omitted
//Here, follow the flow of persistence with WebService
}
}
View:
#model Project.Models.Parameterization.ParameterizationViewModel
#{
ViewBag.Title = ResourcesGSC.Language.LabelParameterizationMenu;
}
#using (Html.BeginForm("", "Parameterization", FormMethod.Post, new { }))
{
<div class="form-group">
<div class="row mb-3">
<div class="col-lg-6 col-md-12">
#Html.LabelFor(m => m.Name, new { })
#Html.TextBoxFor(m => m.Name, new { #class = "form-control", placeholder = "" })
#Html.ValidationMessageFor(m => m.Name, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="row mb-3">
<div class="col">
<button type="submit" class="btn btn-primary float-right">
#ResourcesGSC.Language.LabelBtnSave
</button>
</div>
</div>
}
I can't understand what happens. I have the same code in several other parts, which work perfectly.
I've searched through everything I got and I can't solve it...
Someone can help on this issue?
In addition, the validation message is displayed on the screen. But I can't get to my controller, as it happens on other screens
It appears that the client-side validation is getting in your way when you don't want it to. By default, if you use the MVC boilerplate code it will automatically setup and turn on client-side validation for your forms. This will validate things like required fields on the client-side in javascript and prevent the form from posting to the server if it doesn't pass this client-side validation.
You can read how it works here: https://www.blinkingcaret.com/2016/03/23/manually-use-mvc-client-side-validation/ (this article tell how to manually use it, but does a good job explaining how it works)
But if you want to handle all validation server-side in your controller, you can disable the client-side validation a few ways:
Remove the script references to jquery.validate.js and jquery.validate.unobtrusive.js in your bundle config
add <add key="ClientValidationEnabled" value="false"/> and <add key="UnobtrusiveJavaScriptEnabled" value="false"/> to your weeb.config under the <appSettings> node
Add HtmlHelper.ClientValidationEnabled = false; and HtmlHelper.UnobtrusiveJavaScriptEnabled = false; to individual views or actions
Don't use jQuery validator. It will check for validation error(s) and won't let the request reach your controller in case validation fails. Since you said that client side validation is happening, I can only guess that this is the case. Disable jQuery validator and even if your view model is not valid the request will reach the controller.
If you have used default template of mvc 5 project then look for it in bundle.config file. There you can comment it out.

Dropdownlist returning null viewdata Id in partialview

I am working on a multi-step form which must commit the first step to database before the other steps add up and commit finally after last step. Looking at the options I decided to use sessions(however I will be happy to use any better alternative). I managed to get this to work and later decided to use ajax for the form submission hence the requirement of partialviews. The problem is the dropdowns are returning null viewdata values - ie they are not binding. Can anyone suggest the best way to compose in the controller to make this work. It works fine if I revert to return views instead of return partialviews.
Controller
[AllowAnonymous]
public ActionResult ContactViewDetails()
{
ViewBag.CountryList = new SelectList(db.Countries, "CountryId", "CountryName");
return PartialView("ContactViewDetails");
}
model
public int CountryId{get;set;}
public virtual Country Country { get; set; }
.......and others
Default Page View
<script src="~/Scripts/jquery-2.1.3.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
<div id="divContainer">
#Html.Partial("RegViewDetails")
</div>
PartialView: ContactViewDetails.cs
#{
AjaxOptions options = new AjaxOptions();
options.HttpMethod = "POST";
options.InsertionMode = InsertionMode.Replace;
options.UpdateTargetId = "divContainer";
}
#using (Ajax.BeginForm("ContactViewDetails", "OnlineApplication", options, new { #class = "form-horizontal" }))
{
#Html.DropDownListFor(x => x.CountryId, (IEnumerable<SelectListItem>)ViewBag.CountryList, new { #class = "chooseOption" })
#Html.......others
}
The ContactViewDetails page is the second step in the form succession The first step is RegViewDetails page as you can see in the Default page view. After validation RegViewDetails returns ContactViewDetails Partial
......
return PartialView("ContactViewDetails");

Using 'Html.EditorFor' to pass data to controller using ViewBag

I have an issue using ViewBag with Html.EditorFor. I am trying to pass data from a form field named "ID" from view 'Create.chtml' back to controller. I would like to use ViewBag for this. When I tried to do as I did in the View below, it's throwing an error:
Compiler Error Message: CS1973: 'System.Web.Mvc.HtmlHelper' has no applicable method named 'EditorFor' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.
Please let me know where I am doing it wrong.
Controller:
[HttpPost]
public ActionResult Create(TABLE_CODES dc)
{
try
{
using (var db = new InpEntities())
{
TABLE_CODES codes = new TABLE_CODES(); //TABLE_CODES has data with various columns - ID, NAME, DATE, SOURCE etc.
ViewBag.keys = codes;
db.AddToTABLE_CODES(dc);
db.SaveChanges();
}
return RedirectToAction("Index");
catch
{
return View();
}
}
View: Create.chtml
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<div class="editor-label">ID</div>
<div class="editor-field">
#Html.EditorFor(ViewBag.keys.ID) #****** THIS IS NOT WORKING ********#
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#Html.EditorFor requires a strongly-typed model. It cannot be used with data from the ViewBag.
If you must use ViewBag to pass data to the view, use #Html.TextFor(...) instead to manually setup the input field.
However, I recommend you use a strongly-typed model.
Edit:
Model:
public class MyModel
{
public TABLE_CODES Keys { get; set; }
}
Controller:
var model = new MyModel();
model.Keys = new TABLE_CODES();
return View(model);
View:
#model MvcApplication1.MyModel
....
#Html.EditorFor(m => m.Keys)

MVC3 Html editor helpers display old model value

After form submit Html editor helpers (TextBox, Editor, TextArea) display old value not a current value of model.text
Display helpers (Display, DisplayText) display proper value.
Is there any way editor helpers to display current model.text value?
Model
namespace TestProject.Models
{
public class FormField
{
public string text { get;set; }
}
}
Controller
using System.Web.Mvc;
namespace TestProject.Controllers
{
public class FormFieldController : Controller
{
public ActionResult Index (Models.FormField model=null)
{
model.text += "_changed";
return View(model);
}
}
}
View
#model TestProject.Models.FormField
#using (Html.BeginForm()){
<div>
#Html.DisplayFor(m => m.text)
</div>
<div>
#Html.TextBoxFor(m => m.text)
</div>
<input type="submit" />
}
When you submit the form to an MVC action the values of the input fields are recovered from the POSTEd values available in the form and not from the model. That makes sense right? We don't want the user to show a different value in a textbox than they have just entered and submitted to the server.
If you want to show the updated model to the user then you should have another action and from the post action you have to redirect to that action.
Basically you should have two actions one action that renders the view to edit the model and another one saves the model to database or whatever and redirect the request to the former action.
An example:
public class FormFieldController : Controller
{
// action returns a view to edit the model
public ActionResult Edit(int id)
{
var model = .. get from db based on id
return View(model);
}
// action saves the updated model and redirects to the above action
// again for editing the model
[HttpPost]
public ActionResult Edit(SomeModel model)
{
// save to db
return RedirectToAction("Edit");
}
}
When using HTML editors such as HTML.EditorFor() or HTML.DisplayFor(), if you attempt to modify or change the model values in the controller action you won't see any change unless you remove the ModelState for the model property you want to change.
While #Mark is correct, you don't have to have a separate controller action (but you usually would want to) and you don't need to redirect to the original action.
e.g. - call ModelState.Remove(modelPropertyName)...
public ActionResult Index (Models.FormField model=null)
{
ModelState.Remove("text");
model.text += "_changed";
return View(model);
}
And if you want to have separate actions for GET and POST (recommended) you can do...
public ActionResult Index ()
{
Models.FormField model = new Models.FormField(); // or get from database etc.
// set up your model defaults, etc. here if needed
return View(model);
}
[HttpPost] // Attribute means this action method will be used when the form is posted
public ActionResult Index (Models.FormField model)
{
// Validate your model etc. here if needed ...
ModelState.Remove("text"); // Remove the ModelState so that Html Editors etc. will update
model.text += "_changed"; // Make any changes we want
return View(model);
}
I had some similar problem, I hope I can help others have similar problem:
ActionExecutingContext has Controller.ViewData.
as you can see:
new ActionExecutingContext().Controller.ViewData
This ViewData contains ModelState and Model. The ModelState shows the state of model has passed to controller for example. When you have an error on ModelState the unacceptable Model and its state passed to View. So you will see the old value, yet. Then you have to change the Model value of ModelState manually.
for example for clearing a data:
ModelState.SetModelValue("MyDateTime", new ValueProviderResult("", "", CultureInfo.CurrentCulture));
Also you can manipulate the ViewData, as here.
The EditorFor, DisplayFor() and etc, use this ViewData contents.

Simple Form update without DB persistence

I have a simple ASP.NET MVC 3 dummy app (just learning MVC coming from WebForms).
And I'm pretty confused about how to update a form without actually having some DB stuff in between. I just have a form with a textbox and after I press the button I want to see the string in uppercase. But my nothing happens.
The controller:
public ActionResult Index()
{
ToUppercaseModel model = new ToUppercaseModel { TheString = "testing" };
return View(model);
}
[HttpPost]
public ActionResult Index(ToUppercaseModel model)
{
model.TheString = model.TheString.ToUpper();
return View(model);
}
The model:
public class ToUppercaseModel
{
[Display(Name = "My String")]
public string TheString { get; set; }
}
And the view:
#using (Html.BeginForm()) {
<div>
<div class="editor-label">
#Html.LabelFor(m => m.TheString)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.TheString)
</div>
<p>
<input type="submit" value="Convert" />
</p>
</div>
}
This is a simple as it gets I think. Now obviously the return View(model); in the 2nd Index method is not working. I saw some stuff about RedirectToAction() and storing the data in TempData. Most example just submit some id, but since I don't have db that does not work.
If I do this:
return RedirectToAction("Index", model);
I get a
No parameterless constructor defined for this object.
Error Message. This should be simple, no? I think I understand the Post/Redirect/Get concept, but don't see how to apply it for something simple as this.
Thanks for some clarification.
When MVC renders a view it will use the attempted value of a field rather than the model's value if it exists (eg in a datefield I put "Tuesday", this won't model bind but you'll want to show the user the field with their input and highlighted as invalid), you're changing the model's value but not the attempted value.
The attempted value is held in the modelstate dictionary:
ModelState["KeyToMyValue"].Value.Value.AttemptedValue
Accessing and changing these values can be tricky unless you want a load of magic strings in your code, and as validation happens on modelbinding your changed value won't be validated.
My recommendation in these circumstances is to call ModelState.Clear(), this will remove all validation and attempted values, then change your model directly. Finally you want to get your validation on the model by using TryValidateModel(yourModel).
Be aware that this method is probably the easiest non-hacky method of doing this but will remove attempted values that could not bind from the returned view.
You have to call 1 method from the 2 method, but you have to change it
public ActionResult Index(ToUppercaseModel model)
and send to 1 method your model.
public ActionResult Index(ToUppercaseModel? model)
{
if (model == null)
ToUppercaseModel model = new ToUppercaseModel { TheString = "testing" };
return View(model);
}
I think I got a solution, it works, but not sure if this is the way it should be?
Basically I just put my model into the TempData and call the normal Index method again.
public ActionResult Index()
{
ToUppercaseModel model = null;
if (TempData["FeaturedProduct"] == null)
{
model = new ToUppercaseModel { TheString = "testing" };
}
else
{
model = (ToUppercaseModel)TempData["FeaturedProduct"];
}
return View(model);
}
[HttpPost]
public ActionResult Index(ToUppercaseModel model)
{
model.TheString = model.TheString.ToUpper();
TempData["FeaturedProduct"] = model;
//return View(model);
return RedirectToAction("Index");
}

Resources