Updated partial view model values in contoller after form submit - asp.net-mvc

In my sample MVC application I have a model
class SampleModel
{
public int Id { get; set; }
public string Name { get; set; }
public List<Certification> Certifications { get; set; }
}
class Certification
{
public int Id { get; set; }
public string CertificationName { get; set; }
public int DurationInMonths { get; set; }
}
My View (I need the certification details to be shown in a partial view)
#model SampleApplication.Model.SampleModel
<!-- other code... -->
#using (Html.BeginForm("SaveValues","Sample", FormMethod.Post, new { id= "saveForm" }))
{
#Html.HiddenFor(m => m.Id, new { id = "hdnID" })
#Html.TextBoxFor(m => m.Name, new { id = "txtName" })
#{Html.RenderPartial("_CertDetails.cshtml", Model.Certifications);}
<input type="submit" id="btnSubmit" name="btnSubmit" value="Update" />
}
Partial View
#model List<SampleApplication.Model.Certification>
<!-- other code... -->
#if (#Model != null)
{
for (int i = 0; i < #Model.Count; i++)
{
#Html.HiddenFor(m => m[i].Id , new { id = "CId" + i.ToString() })
#Html.TextBoxFor(m => m[i].CertificationName,new{ id ="CName" + i.ToString() })
#Html.TextBoxFor(m => m[i].DurationInMonths,new{ id ="CDur" + i.ToString() })
}
}
Controller
[HttpPost]
public ActionResult SaveValues(SampleModel sm)
{
//Here i am not getting the updated Certification details (in sm)
}
How I get the updated values of partial view in my controller after the form post? I am able to get the updated certification values when I am not using partialview.
Is this the right way or should I follow some other methods?

If sm.Certifications is coming back null, that means that either nothing was posted for that, or the modelbinder was unable to attach the posted data properly.
In your partial, you're defining the fields properly with an indexer, but initially, Certifications is a null list, so this code is never actually be run. That means, elsewhere you have some JavaScript logic that is adding new Certification fields to the page, dynamically, and my guess is that the field names that JavaScript is generating do not follow the indexing convention that the modelbinder expects. All your fields should be in the format of:
ListProperty[index].PropertyName
So in your case, your JS should be generating names like:
Certifications[0].CertificationName
In order for the data to be bound properly.

Oh Nooo... It was my mistake :( . I gave Certification List as my partialview model
#model List<SampleApplication.Model.Certification>
But I should use the same model(Main page model) in the partial view also.
#model SampleApp.Models.SampleModel
In the partial view the coding will be like
#for (int i = 0; i < #Model.Certifications.Count; i++)
{
#Html.HiddenFor(m => m.Certifications[i].Id, new { id = "CId" + i.ToString() })
#Html.TextBoxFor(m => m.Certifications[i].CertificationName, new { id = "CName" + i.ToString() })
#Html.TextBoxFor(m => m.Certifications[i].DurationInMonths, new { id = "CDur" + i.ToString() })<br /><br />
}
Now i am getting the updated values in my controller.
Thanks #Chris Pratt for the hint.

Related

pass multiple models data from controller to view MVC C# [duplicate]

I want to have 2 models in one view. The page contains both LoginViewModel and RegisterViewModel.
e.g.
public class LoginViewModel
{
public string Email { get; set; }
public string Password { get; set; }
}
public class RegisterViewModel
{
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
Do I need to make another ViewModel which holds these 2 ViewModels?
public BigViewModel
{
public LoginViewModel LoginViewModel{get; set;}
public RegisterViewModel RegisterViewModel {get; set;}
}
I need the validation attributes to be brought forward to the view. This is why I need the ViewModels.
Isn't there another way such as (without the BigViewModel):
#model ViewModel.RegisterViewModel
#using (Html.BeginForm("Login", "Auth", FormMethod.Post))
{
#Html.TextBoxFor(model => model.Name)
#Html.TextBoxFor(model => model.Email)
#Html.PasswordFor(model => model.Password)
}
#model ViewModel.LoginViewModel
#using (Html.BeginForm("Login", "Auth", FormMethod.Post))
{
#Html.TextBoxFor(model => model.Email)
#Html.PasswordFor(model => model.Password)
}
There are lots of ways...
with your BigViewModel
you do:
#model BigViewModel
#using(Html.BeginForm()) {
#Html.EditorFor(o => o.LoginViewModel.Email)
...
}
you can create 2 additional views
Login.cshtml
#model ViewModel.LoginViewModel
#using (Html.BeginForm("Login", "Auth", FormMethod.Post))
{
#Html.TextBoxFor(model => model.Email)
#Html.PasswordFor(model => model.Password)
}
and register.cshtml same thing
after creation you have to render them in the main view and pass them the viewmodel/viewdata
so it could be like this:
#{Html.RenderPartial("login", ViewBag.Login);}
#{Html.RenderPartial("register", ViewBag.Register);}
or
#{Html.RenderPartial("login", Model.LoginViewModel)}
#{Html.RenderPartial("register", Model.RegisterViewModel)}
using ajax parts of your web-site become more independent
iframes, but probably this is not the case
I'd recommend using Html.RenderAction and PartialViewResults to accomplish this; it will allow you to display the same data, but each partial view would still have a single view model and removes the need for a BigViewModel
So your view contain something like the following:
#Html.RenderAction("Login")
#Html.RenderAction("Register")
Where Login & Register are both actions in your controller defined like the following:
public PartialViewResult Login( )
{
return PartialView( "Login", new LoginViewModel() );
}
public PartialViewResult Register( )
{
return PartialView( "Register", new RegisterViewModel() );
}
The Login & Register would then be user controls residing in either the current View folder, or in the Shared folder and would like something like this:
/Views/Shared/Login.cshtml: (or /Views/MyView/Login.cshtml)
#model LoginViewModel
#using (Html.BeginForm("Login", "Auth", FormMethod.Post))
{
#Html.TextBoxFor(model => model.Email)
#Html.PasswordFor(model => model.Password)
}
/Views/Shared/Register.cshtml: (or /Views/MyView/Register.cshtml)
#model ViewModel.RegisterViewModel
#using (Html.BeginForm("Login", "Auth", FormMethod.Post))
{
#Html.TextBoxFor(model => model.Name)
#Html.TextBoxFor(model => model.Email)
#Html.PasswordFor(model => model.Password)
}
And there you have a single controller action, view and view file for each action with each totally distinct and not reliant upon one another for anything.
Another way is to use:
#model Tuple<LoginViewModel,RegisterViewModel>
I have explained how to use this method both in the view and controller for another example: Two models in one view in ASP MVC 3
In your case you could implement it using the following code:
In the view:
#using YourProjectNamespace.Models;
#model Tuple<LoginViewModel,RegisterViewModel>
#using (Html.BeginForm("Login1", "Auth", FormMethod.Post))
{
#Html.TextBoxFor(tuple => tuple.Item2.Name, new {#Name="Name"})
#Html.TextBoxFor(tuple => tuple.Item2.Email, new {#Name="Email"})
#Html.PasswordFor(tuple => tuple.Item2.Password, new {#Name="Password"})
}
#using (Html.BeginForm("Login2", "Auth", FormMethod.Post))
{
#Html.TextBoxFor(tuple => tuple.Item1.Email, new {#Name="Email"})
#Html.PasswordFor(tuple => tuple.Item1.Password, new {#Name="Password"})
}
Note that I have manually changed the Name attributes for each property when building the form. This needs to be done, otherwise it wouldn't get properly mapped to the method's parameter of type model when values are sent to the associated method for processing. I would suggest using separate methods to process these forms separately, for this example I used Login1 and Login2 methods. Login1 method requires to have a parameter of type RegisterViewModel and Login2 requires a parameter of type LoginViewModel.
if an actionlink is required you can use:
#Html.ActionLink("Edit", "Edit", new { id=Model.Item1.Id })
in the controller's method for the view, a variable of type Tuple needs to be created and then passed to the view.
Example:
public ActionResult Details()
{
var tuple = new Tuple<LoginViewModel, RegisterViewModel>(new LoginViewModel(),new RegisterViewModel());
return View(tuple);
}
or you can fill the two instances of LoginViewModel and RegisterViewModel with values and then pass it to the view.
Use a view model that contains multiple view models:
namespace MyProject.Web.ViewModels
{
public class UserViewModel
{
public UserDto User { get; set; }
public ProductDto Product { get; set; }
public AddressDto Address { get; set; }
}
}
In your view:
#model MyProject.Web.ViewModels.UserViewModel
#Html.LabelFor(model => model.User.UserName)
#Html.LabelFor(model => model.Product.ProductName)
#Html.LabelFor(model => model.Address.StreetName)
Do I need to make another view which holds these 2 views?
Answer:No
Isn't there another way such as (without the BigViewModel):
Yes, you can use Tuple (brings magic in view having multiple model).
Code:
#model Tuple<LoginViewModel, RegisterViewModel>
#using (Html.BeginForm("Login", "Auth", FormMethod.Post))
{
#Html.TextBoxFor(tuple=> tuple.Item.Name)
#Html.TextBoxFor(tuple=> tuple.Item.Email)
#Html.PasswordFor(tuple=> tuple.Item.Password)
}
#using (Html.BeginForm("Login", "Auth", FormMethod.Post))
{
#Html.TextBoxFor(tuple=> tuple.Item1.Email)
#Html.PasswordFor(tuple=> tuple.Item1.Password)
}
Add this ModelCollection.cs to your Models
using System;
using System.Collections.Generic;
namespace ModelContainer
{
public class ModelCollection
{
private Dictionary<Type, object> models = new Dictionary<Type, object>();
public void AddModel<T>(T t)
{
models.Add(t.GetType(), t);
}
public T GetModel<T>()
{
return (T)models[typeof(T)];
}
}
}
Controller:
public class SampleController : Controller
{
public ActionResult Index()
{
var model1 = new Model1();
var model2 = new Model2();
var model3 = new Model3();
// Do something
var modelCollection = new ModelCollection();
modelCollection.AddModel(model1);
modelCollection.AddModel(model2);
modelCollection.AddModel(model3);
return View(modelCollection);
}
}
The View:
enter code here
#using Models
#model ModelCollection
#{
ViewBag.Title = "Model1: " + ((Model.GetModel<Model1>()).Name);
}
<h2>Model2: #((Model.GetModel<Model2>()).Number</h2>
#((Model.GetModel<Model3>()).SomeProperty
a simple way to do that
we can call all model first
#using project.Models
then send your model with viewbag
// for list
ViewBag.Name = db.YourModel.ToList();
// for one
ViewBag.Name = db.YourModel.Find(id);
and in view
// for list
List<YourModel> Name = (List<YourModel>)ViewBag.Name ;
//for one
YourModel Name = (YourModel)ViewBag.Name ;
then easily use this like Model
My advice is to make a big view model:
public BigViewModel
{
public LoginViewModel LoginViewModel{get; set;}
public RegisterViewModel RegisterViewModel {get; set;}
}
In your Index.cshtml, if for example you have 2 partials:
#addTagHelper *,Microsoft.AspNetCore.Mvc.TagHelpers
#model .BigViewModel
#await Html.PartialAsync("_LoginViewPartial", Model.LoginViewModel)
#await Html.PartialAsync("_RegisterViewPartial ", Model.RegisterViewModel )
and in controller:
model=new BigViewModel();
model.LoginViewModel=new LoginViewModel();
model.RegisterViewModel=new RegisterViewModel();
I want to say that my solution was like the answer provided on this stackoverflow page: ASP.NET MVC 4, multiple models in one view?
However, in my case, the linq query they used in their Controller did not work for me.
This is said query:
var viewModels =
(from e in db.Engineers
select new MyViewModel
{
Engineer = e,
Elements = e.Elements,
})
.ToList();
Consequently, "in your view just specify that you're using a collection of view models" did not work for me either.
However, a slight variation on that solution did work for me. Here is my solution in case this helps anyone.
Here is my view model in which I know I will have just one team but that team may have multiple boards (and I have a ViewModels folder within my Models folder btw, hence the namespace):
namespace TaskBoard.Models.ViewModels
{
public class TeamBoards
{
public Team Team { get; set; }
public List<Board> Boards { get; set; }
}
}
Now this is my controller. This is the most significant difference from the solution in the link referenced above. I build out the ViewModel to send to the view differently.
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
TeamBoards teamBoards = new TeamBoards();
teamBoards.Boards = (from b in db.Boards
where b.TeamId == id
select b).ToList();
teamBoards.Team = (from t in db.Teams
where t.TeamId == id
select t).FirstOrDefault();
if (teamBoards == null)
{
return HttpNotFound();
}
return View(teamBoards);
}
Then in my view I do not specify it as a list. I just do "#model TaskBoard.Models.ViewModels.TeamBoards" Then I only need a for each when I iterate over the Team's boards. Here is my view:
#model TaskBoard.Models.ViewModels.TeamBoards
#{
ViewBag.Title = "Details";
}
<h2>Details</h2>
<div>
<h4>Team</h4>
<hr />
#Html.ActionLink("Create New Board", "Create", "Board", new { TeamId = #Model.Team.TeamId}, null)
<dl class="dl-horizontal">
<dt>
#Html.DisplayNameFor(model => Model.Team.Name)
</dt>
<dd>
#Html.DisplayFor(model => Model.Team.Name)
<ul>
#foreach(var board in Model.Boards)
{
<li>#Html.DisplayFor(model => board.BoardName)</li>
}
</ul>
</dd>
</dl>
</div>
<p>
#Html.ActionLink("Edit", "Edit", new { id = Model.Team.TeamId }) |
#Html.ActionLink("Back to List", "Index")
</p>
I am fairly new to ASP.NET MVC so it took me a little while to figure this out. So, I hope this post helps someone figure it out for their project in a shorter timeframe. :-)
Create one new class in your model and properties of LoginViewModel and RegisterViewModel:
public class UserDefinedModel()
{
property a1 as LoginViewModel
property a2 as RegisterViewModel
}
Then use UserDefinedModel in your view.
you can always pass the second object in a ViewBag or View Data.
This is a simplified example with IEnumerable.
I was using two models on the view: a form with search criteria (SearchParams model), and a grid for results, and I struggled with how to add the IEnumerable model and the other model on the same view. Here is what I came up with, hope this helps someone:
#using DelegatePortal.ViewModels;
#model SearchViewModel
#using (Html.BeginForm("Search", "Delegate", FormMethod.Post))
{
Employee First Name
#Html.EditorFor(model => model.SearchParams.FirstName,
new { htmlAttributes = new { #class = "form-control form-control-sm " } })
<input type="submit" id="getResults" value="SEARCH" class="btn btn-primary btn-lg btn-block" />
}
<br />
#(Html
.Grid(Model.Delegates)
.Build(columns =>
{
columns.Add(model => model.Id).Titled("Id").Css("collapse");
columns.Add(model => model.LastName).Titled("Last Name");
columns.Add(model => model.FirstName).Titled("First Name");
})
...
)
SearchViewModel.cs:
namespace DelegatePortal.ViewModels
{
public class SearchViewModel
{
public IEnumerable<DelegatePortal.Models.DelegateView> Delegates { get; set; }
public SearchParamsViewModel SearchParams { get; set; }
....
DelegateController.cs:
// GET: /Delegate/Search
public ActionResult Search(String firstName)
{
SearchViewModel model = new SearchViewModel();
model.Delegates = db.Set<DelegateView>();
return View(model);
}
// POST: /Delegate/Search
[HttpPost]
public ActionResult Search(SearchParamsViewModel searchParams)
{
String firstName = searchParams.FirstName;
SearchViewModel model = new SearchViewModel();
if (firstName != null)
model.Delegates = db.Set<DelegateView>().Where(x => x.FirstName == firstName);
return View(model);
}
SearchParamsViewModel.cs:
namespace DelegatePortal.ViewModels
{
public class SearchParamsViewModel
{
public string FirstName { get; set; }
}
}

Manually created input is breaking model posting back values

I have the following ViewModel:
public class DataSyncViewModel
{
public ConfigurableDataSyncOptions DataSyncOptions { get; set; }
public int Id { get; set; }
public string SystemName { get; set; }
}
I then loop through the DataSyncOptions to list some textboxes in my view:
#if (#Model.DataSyncOptions != null)
{
if (Model.DataSyncOptions.TextConfigurableOptions != null)
{
for (int i = 0; i < Model.DataSyncOptions.TextConfigurableOptions.Count; i++)
{
<div class="span6">
<h4>#Model.DataSyncOptions.TextConfigurableOptions[i].OptionText?</h4>
<p>
#Html.EditorFor(m => Model.DataSyncOptions.TextConfigurableOptions[i].OptionValue)
</p>
#Html.HiddenFor(m => Model.DataSyncOptions.TextConfigurableOptions[i].OptionName)
</div>
}
}
}
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.SystemName)
This works and when the form posts back, I can access SystemName and Id from my controller.
However, if I replace
#Html.EditorFor(m => Model.DataSyncOptions.TextConfigurableOptions[i].OptionValue)
with
#if (Model.DataSyncOptions.TextConfigurableOptions[i].OptionType == "")
{
#Html.EditorFor(m => Model.DataSyncOptions.TextConfigurableOptions[i].OptionValue)
}
else
{
//This appears to be causing issues
<input type="#Model.DataSyncOptions.TextConfigurableOptions[i].OptionType" name="Model.DataSyncOptions.TextConfigurableOptions[#i].OptionValue" value="#Model.DataSyncOptions.TextConfigurableOptions[i].OptionValue" />
}
my values stop posting back with the model. I am trying to allow a plugin creator of my app to specify the input type of an option they have added so am creating the input manually.
Any ideas as to why changing the input generation breaks the model binding on postback?
Instead of Model write Model Class Name in name attribute which is DataSyncViewModel to bind:
<input type="#Model.DataSyncOptions.TextConfigurableOptions[i].OptionType"
name="DataSyncOptions.TextConfigurableOptions[#i].OptionValue"
value="#Model.DataSyncOptions.TextConfigurableOptions[i].OptionValue" />
Actually when we write #Model it is in actualy the instance of type to which our View is strongly typed, and in this case Model is simple string not Razor so it will remain as Model and as name is different of input so it will not be binded to Model property in post.

How keep reference to the object navigation property when creating a new object

I have the following as part of the Edit view:-
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Rack.RackID)
#Html.HiddenFor(model => model.Resource.RESOURCEID)
#Html.HiddenFor(model => model.Rack.timestamp)
#Html.HiddenFor(model=>model.Rack.Technology)
<span class="f"> #Html.TextBoxFor(model => model.Rack.Technology.Tag, new
{ #readonly = "readonly" })</span>
<span class="f">Rack Name </span>
#Html.EditorFor(model => model.Resource.RESOURCENAME)
#Html.ValidationMessageFor(model => model.Resource.RESOURCENAME)
</div>
<div >
<span class="f">Product Name </span>
#Html.DropDownList("productName", ((IEnumerable<TMS.Models.ComponentDefinition>)ViewBag.Products).Select(option => new SelectListItem {
Text = (option == null ? "None" : option.COMPONENTNAME),
Value = option.COMPONENTNAME.ToString(),
Selected = (Model != null) && (Model.Resource.ComponentDefinition != null ) && (option.COMPONENTNAME == Model.Resource.ComponentDefinition.COMPONENTNAME)
}), "Choose...")
#Html.ValidationMessageFor(model => model.Resource.COMPONENTID)
</div>
<input type="submit" value="Save" class="btn btn-primary"/>
}</div>
I have the following Post Edit action method:-
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(RackJoin rj,FormCollection formValues)
{
string controllername = RouteData.Values["controller"].ToString();
try
{
if (ModelState.IsValid)
{
var message = "";
var status = "";
long assetid = new long();
XmlDocument doc = new XmlDocument();
using (var client = new WebClient())
{ var query = HttpUtility.ParseQueryString(string.Empty);
foreach (string key in formValues)
{
query[key] = this.Request.Form[key];
}
and the RackJoin model class is :-
public class RackJoin
{
public Rack Rack { get; set; }
public Resource Resource { get; set; }}
but the problem I am facing is that when I post back the object to the Post Edit action method, I will loose all its navigation properties such as (Rack.Technology, Rack.SiteDefinistion) , so how I can maintain all the object navigation properties, so if the Post edit action methods wants to access these navigation properties or the edit view is rendered after a model state error they will not get a null reference exception .
The reason why this isn't working is because Technology is a complex type so can't be rendered via HiddenFor, if you need to persist all the information for this property you will have to individually render each property e.g.
#Html.HiddenFor(model => model.Rack.Technology.Name)
However, I am beginning to question your design here. If a lot of the information is irrelevant to this particular view then in fact it shouldn't even be there. View models should be tailored to the view so I would recommend you have a specific model designed to capture the relevant information only i.e.
public class RackJoinViewModel
{
public int RackID { get; set; }
public int ResourceID { get; set; }
public DateTime TimeStamp { get; set; }
public string Technology { get; set; }
...
}
This should simplify the view rendering as there are no complex types e.g.
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.RackID)
#Html.HiddenFor(model => model.ResourceID)
#Html.HiddenFor(model => model.TimeStamp)
#Html.HiddenFor(model => model.Technology)
...
}
Not only is this cleaner but it also eradicates the problem of trying to persist irrelevant information across the request.

Retrieving values from partial view during post method

I have a view which contains a dropdown list and on dropdownlist item being selected I load a partial view. And when the form is submitted I want to be able to get both the values from main view and partial view during form submit.
Here is the main view
#model AdminPortal.Areas.Hardware.Models.CreateModule
#{
ViewBag.Title = "Create Module";
Layout = "~/Views/shared/_BootstrapLayout.basic.cshtml";
}
#Html.ValidationSummary(true)
<fieldset class="form-horizontal">
<legend>Add a Module <small>Create</small></legend>
#using (Html.BeginForm("CreateModule", "Module", new{id="AddModuleForm"}))
{
#Html.ValidationSummary(true)
<div class ="controls">
<div class="input-block-level">#Html.TextBoxFor(model => model.ModuleId, new {#placeholder = "ModuleID"})</div>
<br/>
<div class ="input-block-level" id="selectedModuleTypeName">#Html.DropDownListFor(model => model.SelectedModuleTypeName, Model.TypeNames,"Select Moduletype", new{id = "ModuleList"})</div>
<br/>
<div id="partialDiv"></div>
</div>
<div class="form-actions" id="buttons">
<button type="submit" class="btn btn-primary" id="Submit">Save changes</button>
#Html.ActionLink("Cancel", "ModuleList", null, new { #class = "btn " })
</div>
}
</fieldset>
<div>
#Html.ActionLink("Back to List", "ModuleList")
</div>
<script>
$("#buttons").hide();
$("#ModuleList").on("change", function() {
var modId = $(this).val();
$.get('#Url.Action("GetModulePropertyName", "Module")', { moduleTypeValue: modId }, function(result) {
$("#partialDiv").html(result);
});
//uncomment following section to check if the partial view is working properly
/*.done(function() { alert("done"); })
.fail(function() { alert("fail"); })
.always(function() { alert("completed"); });*/
});
$("#buttons").show();
</script>
and here is the partial view
#model IEnumerable<string>
#foreach(var names in Model)
{
<div class="input-block-level">#Html.TextBoxFor(m=>names, new{Value="", placeholder=names})</div>
<br/>
}
Here is my model
public class CreateModule
{
//Empty form to handle form serialization
public CreateModule()
{
}
[Required]
public string ModuleId { get; set; }
[DataType(DataType.DateTime)]
public DateTime DateEntered { get; set; }
[Required]
public string SelectedModuleTypeName { get; set; }
public IEnumerable<SelectListItem> TypeNames { get; set; }
public List<Property> Properties { get; set; }
}
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
Here is the method that script in main view forwards to
[HttpGet]
public ActionResult GetModulePropertyName(string moduleTypeValue)
{
var moduleKindId = _repository.GetModuleKindId(moduleTypeValue);
var modulePropertyNames = _repository.GetModuleKindPropertyNames(moduleTypeValue);
return PartialView("GetModulePropertyName",modulePropertyNames);
}
and finally here is httppost method for the main view
[HttpPost]
public ActionResult CreateModule(CreateModule moduleV)
{
var module = new Module
{
ModuleTypeId = Convert.ToInt64(moduleV.SelectedModuleTypeName),
ModuleId = moduleV.ModuleId,
DateEntered = moduleV.DateEntered,
};
if (ModelState.IsValid)
{
_repository.AddModule(module);
Success("Module added successfully!");
return RedirectToAction("ModuleList", "Module", new {area = "Hardware"});
}
Error("Something went wrong!");
return RedirectToAction("CreateModule", "Module", new { area = "Hardware" });
}
Current situation:
When the form is posted, the properties value of the model that is being passed via partial view is null. I get other values, like typename, Module ID.
What I'd want:
I also want to get the value of properties that is being passed via partial view.
You don't have any input field for the Properties property anywhere in your form. So it will always be null. That's normal.
Here's how you could proceed. Start by setting the correct navigational property so that the helper generates correct names of the corresponding input fields.
Also make sure that you are passing an IEnumerable<Property> model to the partial if you want to be able to get them back correctly:
[HttpGet]
public ActionResult GetModulePropertyName(string moduleTypeValue)
{
var moduleKindId = _repository.GetModuleKindId(moduleTypeValue);
IList<Property> model = ...
return PartialView("GetModulePropertyName", model.ToList());
}
and in your partial view use an editor template:
#model IList<Property>
#{
// This indicates the current navigational context to the helpers
ViewData.TemplateInfo.HtmlFieldPrefix = "Properties";
}
#Html.EditorForModel()
and the last step is to define a custom editor template for the Property class: ~/Views/Shared/EditorTemplates/Property.cshtml (note that the name and location of the template is important)
#model Property
<div class="input-block-level">
#Html.HiddenFor(m => m.Name)
#Html.TextBoxFor(m => m.Value, new { placeholder = Model.Name })
</div>
<br />
Try using the
List<Property>
as a model in your partial view and pass the CreateModule.Properties as model from your View
The problem is model binder can not figure out there
#Html.TextBoxFor(m=>names, new{Value="", placeholder=names})
belongs to as the "names" is not a property on your model class. If you need to bind to the CreateModule.Properties you need to change the partial view to emit textboxes with aproprate names, like this one:
#model IEnumerable<string>
#
{
int i=0;
}
#foreach(var names in Model)
{
<div class="input-block-level">#Html.TextBox("Properties[" + i + "].Value")</div>
<br/>
}

Model returning null value

This is my code for my model
public class ManufacturerModel
{
public int ProductTYpeId { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public List<SelectListItem> manf { get; set; }
public Manufacturer manufacturer { get; set; }
}
This is my code in cshtml file
#using (Html.BeginForm("addmanufacturer", "Admin", FormMethod.Post, new { id = "formPageID" }))
{
<div class="row">
<label>Select Existing Manufacturer<span style="color: Red">*</span>:</label>
<div class="formRight">
#Html.DropDownList("Id", Model.manf)
</div>
</div>
<div class="row">
<label>Manufacturer Name<span style="color: Red">*</span>:</label>
<div class="formRight">
#Html.TextBoxFor(m => m.manufacturer.name)
#Html.ValidationMessageFor(m => m.manufacturer.name)
</div>
</div>
}
I am posting this form and when I am trying to fetch the value from the manfacurer ie - ManufacturerModel manufacturer = new ManufacturerModel();
using a model object all the value are coming out null.
in the text box If I replace it with m => m.Name then I am able to get proper value of Name.
can any one suggest what the problem is
I am using the manf to bind a dropdown. If In case I post back the form and the if it is return the value becomes blank, I need to refill the value..
public ActionResult addmanufacturer(string id)
{
if (id == null)
id = "0";
ManufacturerModel manufacturer = new ManufacturerModel();
manufacturer.ProductTYpeId = Convert.ToInt32(id);
manufacturer.manf = GetManf();
manufacturer.Id = -1;
return View(manufacturer);
}
I think problem will be becouse of:
#using (Html.BeginForm("Action", "Controller", FormMethod, HTML ATTRIBUTES )) { }
You probably want this overload:
#using (Html.BeginForm("Action", "Controller", Route Values, FormMethod, html attributes )) { }
Important is say him that you want route values and not a html atrributes, so try this
#using (Html.BeginForm("addmanufacturer", "Admin", new { id = "formPageID" }, FormMethod.Post, null )) { }
Hope, it helps.
M.

Resources