Pass List<Object> from Razor View To Post Action - asp.net-mvc

I have model View like This:
public class ContactHomePage
{
public string Query { get; set; }
public List<SqlParameter> SqlParameters { get; set; }
}
In Index action (that works as HTTPGet) I fill ContactHomePage and passed to View, and in the view, I want to pass this model to action GetRecords, this post action like this:
public async Task<IActionResult> GetRecords(JqGridRequest request, string query, List<SqlParameter> sqlParameters)
{
}
and my ajax post url like this:
url: '#Url.Action("GetRecords", "Contact", new { query = Model.Query, sqlParameters = Model.SqlParameters})'
in running the app, query object that was in string mode has its data but sqlParameters with type List hasn't any value!
now my question is, how can post List<Object> from razor view to post action?

If your ActionResult is type of Get, you can't sent complex data types. A solution is convert your complex object (Model.SqlParameters) to string, using Newtonsoft.Json.JsonConvert, and then parse it on ActionResult to your object. Something like this:
At razor view (Newtonsoft.Json.JsonConvert.SerializeObject):
#Url.Action("MyActionResult", "MyController", new { objectStringfied = Newtonsoft.Json.JsonConvert.SerializeObject(new List<MyObject>()) })
At controller (Newtonsoft.Json.JsonConvert.DeserializeObject):
public virtual ActionResult MyActionResult(string objectStringfied)
{
List<MyObject> model = Newtonsoft.Json.JsonConvert.DeserializeObject<List<MyObject>>(objectStringfied);
// ...
}

Related

String as a Model

I thought this should have been an easier task :
Edit:
It seems till this day Asp.Net MVC couldn't provide a neat solution on this case:
If you want to pass a simple string as a model and you don't have to define more classes and stuff to do so...
Any ideas ??
Pass simple string as a model
here I'm trying to have a simple string model.
I'm getting this error :
"Value cannot be null or empty" / "Parameter name: name"
The View :
#model string
#using (Html.BeginForm())
{
<span>Please Enter the code</span>
#Html.TextBoxFor(m => m) // Error Happens here
<button id="btnSubmit" title="Submit"></button>
}
The Controller :
public string CodeText { get; set; }
public HomeController()
{
CodeText = "Please Enter MHM";
}
[HttpGet]
public ActionResult Index()
{
return View("Index", null, CodeText);
}
[HttpPost]
public ActionResult Index(string code)
{
bool result = false;
if (code == "MHM")
result = true;
return View();
}
There's a much cleaner way of passing a string as a model into your view. You just need to use named parameters when returning your view:
[HttpGet]
public ActionResult Index()
{
string myStringModel = "I am passing this string as a model in the view";
return View(model:myStringModel);
}
I know you've already accepted an answer here - I'm adding this because there's a general gotcha associated with using a string model.
String as a model type in MVC is a nightmare, because if you do this in a controller:
string myStringModel = "Hello world";
return View("action", myStringModel);
It ends up choosing the wrong overload, and passing the myStringModel as a master name to the view engine.
In general it is easier simply to wrap it in a proper model type, as the accepted answer describes, but you can also simply force the compiler to choose the correct overload of View() by casting the string to object:
return View("action", (object)myStringModel);
The other issue you're having here of using TextBoxFor having issues with an 'unnamed' model - well you shouldn't be surprised by that... The only reason to use TextBoxFor is to ensure the fields are named correctly for binding when the underlying value is a property on a model type. In this case there is no name, because you're passing it as a top-level model type for a view - so you it could be argued that you shouldn't be using TextBoxFor() in the first place.
Either wrap the string in a view model object:
Model:
public class HomeViewModel
{
public string CodeText { get; set; }
}
Controller:
private HomeViewModel _model;
public HomeController()
{
_model = new HomeViewModel { CodeText = "My Text" };
}
[HttpGet]
public ActionResult Index()
{
return View("Index", _model);
}
View:
#Html.TextBoxFor(m => m.CodeText);
Or use EditorForModel:
#Html.EditorForModel()
You can simply use an overload of View() method.
View(string ViewName, object model)
in action method, call View with that signature.
return View("MyView", myString);
in view(.cshtml), define the model type as string
#model string
Then, #Model will return the string (myString).

The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[System.Int32]'

ASP.Net MVC 4
I am trying to populate a list of Countries (data from Country table in DB) in a dropdownlist. I get the following error:
The model item passed into the dictionary is of type
System.Collections.Generic.List`1[System.Int32]', but this dictionary requires a model item of type 'BIReport.Models.Country'.
I am new to ASP.Net MVC and I don't understand that error. What I feel is what Index method is returning doesn't match with the model that I am using in the View.
Model::
namespace BIReport.Models
{
public partial class Country
{
public int Country_ID { get; set; }
public string Country_Name { get; set; }
public string Country_Code { get; set; }
public string Country_Acronym { get; set; }
}
}
Controller::
public class HomeController : Controller
{
private CorpCostEntities _context;
public HomeController()
{
_context = new CorpCostEntities();
}
//
// GET: /Home/
public ActionResult Index()
{
var countries = _context.Countries.Select(arg => arg.Country_ID).ToList();
ViewData["Country_ID"] = new SelectList(countries);
return View(countries);
}
}
View::
#model BIReport.Models.Country
<label>
Country #Html.DropDownListFor(model => model.Country_ID, ViewData["Country_ID"] as SelectList)
</label>
Where am I going wrong?
You are selecting CountryIDs, therefore you will have a list of integers passed into the view.
I think you really want something like this:
public ActionResult Index()
{
var countries = _context.Countries.ToList();
ViewData["Country_ID"] = new SelectList(countries, "Country_ID", "Country_Name");
return View();
}
I'm not really sure why you have single country as a model for your view.
Update:
I'm still not sure why the model is a country, if you are just going to post the ID of the selected country you don't necessarily need a model at all (or just have an integer). This will be just fine though:
View
#model MvcApplication1.Models.Country
#Html.DropDownListFor(m => m.Country_ID, ViewData["Country_ID"] as SelectList)
the problem is in line 1 of your view. change it like this :
#model IEnumerable<BIReport.Models.Country>
also there is no need to pass the model to view if you already did it by :
ViewData["Country_ID"] = new SelectList(countries);
When you say #model BIReport.Models.Country it means your view is expecting a model consisting single country details. On the contrary you need a list of countries to be displayed in the drop-down list. Hence you should tell the view to look for a list of country details instead.
Therefore #model IEnumerable.

MVC 3 Edit Function not working

I can't seem to get the edit function of my view to work..i have a page that lists, a page that shows specific detail and on that page, i should be able to edit the information of the form..PROBLEM: when i run the application it says:No parameterless constructor defined for this object. What am i doing wrong...?
In the Home Controller i have:
Edit Functions:
[HttpGet]
public ViewResult EditSchoolDetails(int id)
{
var institution = _educationRepository.GetInstititionById(id);
var model = (Mapper.Map<Institution, InstitutionModel>(institution));
return View(model);
}
post
[HttpPost]
public ActionResult EditSchoolDetails( InstitutionModel institutionModel, int id)
{
if (ModelState.IsValid) {
//_get from repository and add to instituion
var institution = _educationRepository.GetInstititionById(institutionModel.Id);
// Map from the view model back to the domain model
var model = Mapper.Map<Institution, InstitutionModel>(institution);
//UpdateModel(model);
SaveChanges();
return RedirectToAction("ViewSchoolDetails", new {institutionModel = institutionModel, id = id});
}
return View(institutionModel);
}
InstitutionModel
public class InstitutionModel {
public InstitutionModel() {
NAABAccreditations = new List<AccreditationModel>();
}
public int Id { get; set; }
public string Name { get; set; }
public bool IsNAAB { get { return NAABAccreditations.Any(); } }
public string Website { get; set; }
public AddressModel Address { get; set; }
public IEnumerable<AccreditationModel> NAABAccreditations { get; set; }
}
Does the Institution class have a parameterless constructor? If not, that will be the problem. You are passing an InstitutionModel to the the edit view, so the post action should probably take an InstitutionModel too, then you can map back to the original Institution model:
public ActionResult EditSchoolDetails(int id, InstitutionModel institutionModel)
{
if (ModelState.IsValid)
{
//add to database and save changes
Institution institutionEntity = _educationRepository.GetInstititionById(institution.Id);
// Map from the view model back to the domain model
Mapper.Map<InstitutionModel, Institution>(institutionModel, institutionEntity);
SaveChanges();
return RedirectToAction("ViewSchoolDetails",);
}
return View(institutionModel);
}
Notice also how it returns the view model back to the view if the model state isn't valid, otherwise you will lose all your form values!
Here's a similar question too which might help: ASP.NET MVC: No parameterless constructor defined for this object
Is it possible you need to pass a parameter to ViewSchoolDetails? I notice in the return statement you commented out that you were passing it an id, but in the return statement you're using, you're not passing in anything.
EDIT
This (from your comment below):
parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult ViewSchoolDetails(Int32)
...tells me you need to pass a parameter to ViewSchoolDetails
EDIT 2
I saw your edit, and would say this: if the method you are calling is
public ActionResult ViewSchoolDetails(InstitutionModel institutionModel, int id)
Then you MUST pass it an object of type InstitutionModel and an int as parameters or you will get an exception. Meaning, you need
RedirectToAction("ViewSchoolDetails", new {institutionModel = institutionModel, id = id});
Whenever i get this, i have forgotten to create a parameter-less constructor on my view-model. I always add one now just in case it's needed and i forget.
Does InstitutionModel have one?

selecting and adding multiple values using IEnumerable in MVC

Im trying to use IEnumerable so it holds multiple selections so a user can choose more then one value in my listbox.
This is how my ViewModel looks like:
public class CreateViewModel
{
public IEnumerable<string> SelectedValues { get; set; }
public IEnumerable<CoreValue> CoreValues { get; set; }
}
This is my GET in my controller:
public ActionResult Create()
{
CreateViewModel model = new CreateViewModel();
model.CoreValues = AdminRepository.GetAllCoreValues();
return View(model);
}
This how my view looks like:
#Html.ListBoxFor(m => m.SelectedValues, new MultiSelectList(Model.CoreValues, "Id", "Name"))
How do I code my POST action so that SelectedValues will be used to contain the selected values on the post?
Before Ienumerable I had string declared to CoreValueName in my ViewModel and I did something like this in my POST action which worked perfectly but now I changed it to Ienumerable and I have am not sure how to code so my selectedvalues will be used to contain the selected values in my post.
Question question = new Question();
var CoreValueID = int.parse(model.CoreValueName);
var GetAllCoreValuesID = AdminRep.GetByCoreValueID(CoreValueID);
question.CoreValue.Add(GetAllCoreValuesID);
AdminRepository.AddQuestion(question);
AdminRepository.save();
This version of my POST action works perfectly beacuse model.SubjectType is declared as string in my Viewmodel:
[HttpPost]
public ActionResult Create(AdminCreateViewModel model)
{
Question question = new Question();
var SubjectTypeID = int.Parse(model.SubjecTypeName);
var GetAllSubjecTypesID = AdminRep.GetBySubjectTypeID(SubjectTypeID);
question.SubjectType.Add(GetAllSubjecTypesID);
AdminRep.AddQuestion(question);
AdminRep.save();
}
This version of my Post action is not working beacuse Model.Selectedvalues is declared as Ienumerable string in my ViewModel. I tried to int.parse my model.SelectedValues but that doesnt work with Ienumerable. And GetByCoreValueID method takes int as argument so I have no idea how do to this. Do I need foreach?
[HttpPost]
public ActionResult Create(AdminCreateViewModel model)
{
Question question = new Question();
var CoreValueID = model.SelectedValues;
var GetAllCoreValuesID = AdminRep.GetByCoreValueID(CoreValueID); //
question.CoreValue.Add(GetAllCoreValuesID);
AdminRep.AddQuestion(question);
AdminRep.save();
Thanks in advance!
You could have your POST controller action take the view model as argument. Then the SelectedValues property will contain a list of values that the user selected:
[HttpPost]
public ActionResult Index(CreateViewModel model)
{
model.SelectedValues will cnotain the selected values from the user here
...
}

Two data sources in one create view

This is what my data model classes look like:
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Position Position { get; set; }
}
public class Position
{
public string Title { get; set; }
}
I have a Create view where I want to have two text boxes for first name and last name, and then a dropdown box that has the position title. I tried doing it this way:
View (only the relevant part):
<p>
<label for="Position">Position:</label>
<%= Html.DropDownList("Positions") %>
</p>
Controller:
//
// GET: /Employees/Create
public ActionResult Create()
{
ViewData["Positions"] = new SelectList(from p in _positions.GetAllPositions() select p.Title);
return View();
}
//
// POST: /Employees/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Employee employeeToAdd)
{
try
{
employeeToAdd.Position = new Position {Title = (string)ViewData["Positions"]};
_employees.AddEmployee(employeeToAdd);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
However, when I click submit, I get this exception:
System.InvalidOperationException was unhandled by user code
Message="There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'Positions'."
I'm pretty sure I'm doing this wrong. What is the correct way of populating the dropdown box?
You can store:
(string)ViewData["Positions"]};
in a hiddn tag on the page then call it like this
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Employee employeeToAdd, string Positions)
{
In the Create() (WITH POST ATTRIBUTE) employee since the ViewData["Positions"] is not set you are getting this error. This value should form part of your post request and on rebinding after post should fetch it from store or get it from session/cache if you need to rebind this..
Remember ViewData is only available for the current request, so for post request ViewData["Positions"] is not yet created and hence this exception.
You can do one quick test... override the OnActionExecuting method of the controller and put the logic to fetch positions there so that its always availlable. This should be done for data that is required for each action... This is only for test purpose in this case...
// add the required namespace for the model...
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// add your logic to populate positions here...
ViewData["Positions"] = new SelectList(from p in _positions.GetAllPositions() select p.Title);
}
There may be other clean solutions to this as well probably using a custom model binder...
I believe that ViewData is for passing information to your View, but it doesn't work in reverse. That is, ViewData won't be set from Request.Form. I think you might want to change your code as follows:
// change following
employeeToAdd.Position = new Position {Title = (string)ViewData["Positions"]};
// to this?
employeeToAdd.Position = new Position {Title = (string)Request.Form["Positions"]};
Good luck!

Resources