Send one Model from ViewModel back to controller - c#

I am using a ViewModel to pass various models to the view. Now in a form, I would like to return one model (ResetTypeSingleModel) out of this ViewModel but the controller’s method can only expect the ViewModel:
My ViewModel:
public class ResetViewModel {
public IEnumerable<ResetTypeModel> TableResetType {get; set;}
public ResetTypeSingleModel SingleResetType {get; set;}
}
My controller (of course I changed it to ResetTypeSingleModel):
public IActionResult ResetDevice([FromForm] ResetViewModel rtm){ }
My view:
#using (Html.BeginForm("ResetDevice", "Sccm", FormMethod.Post, new { id = "form_reset" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.DropDownListFor(model=> model.SingleResetType.resettype_id, new SelectList(Model.TableResetType, "resettype_id" , "resettype_name"), "Please select", new { #CssClass="ddl_resettype", #id="ddl_resettype", #class = "form-control" })
<input type="submit" value="Reset" class="btn btn-primary" />
}
How can I accomplish that I submit to the controller only this model and not the complete viewModel?
Thanks
Stephan
Update:
So I tried to change the names of the fields but then the pattern etc. does not work anymore:
#{ string name = #Html.NameFor(model => Model.SingleResetType.hostname).ToString().Split(".").Reverse().ToList()[0];}
#Html.LabelFor(model => model.SingleResetType.hostname)
#Html.TextBox(name, null, new { #class = "form-control", #id=name, name =name, })
So I guess the only way is to use javascript

Related

DropDownListFor returns selected value always 0

I have a code where I try to bind data from a Model to DropDownListFor and it successfully displays the drop down in the View when executed but it doesn't return the selected value except for 0 always. I have checked and tried numerous solutions posted here but it doesn't seem to work out.
AdminController.cs (Create() action for the Create.cshtml View and ProductSave() Form action)
public ActionResult Create()
{
var categoryList = _context.Categories.ToList();
var viewModel = new CreateViewModel
{
Categories = categoryList,
};
return View(viewModel);
}
[HttpPost]
public ActionResult ProductSave(CreateViewModel viewModel)
{
return Content(viewModel.SelectedId.ToString());
}
CreateViewModel.cs
public class CreateViewModel
{
public IEnumerable<Category> Categories { get; set; }
public int SelectedId { get; set; }
public Product Product { get; set; }
}
Create.cshtml (I only included the necessary fields for the View here)
#using (Html.BeginForm("ProductSave", "Admin",null,FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="default-form-wrap style-2">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Product.Category_Id, new { #class = "control-label"})
<div class="nice-select single-select">
#Html.DropDownListFor(model => model.SelectedId,new SelectList(Model.Categories,"Id","CategoryName"),"Select Category", new { #class = "form-control" } )
#Html.ValidationMessageFor(model => model.Product.Category_Id, "", new { #class = "text-danger" })
</div>
</div>
<div class="row text-center">
<div class="col">
<input type="submit" value="Create" class="btn btn-base" />
</div>
</div>
</div>
When selected a Category the value is changed accordingly
Screenshot
I tried return Content() in the form action method to see the output from the bound viewmodel after I hit submit button
return Content(viewModel.SelectedId.ToString());
But here's what I get
Screenshot
I just want to get the output corresponding to the selected list item.
Looking at your HTML the culprit is probably:
<div class="nice-select single-select">
The DropdownListFor should be generating a <select> element with an ID value that MVC would resolve back to populate the model on the return trip. Your HTML is generating a <ul>.
You could remove that outer <div> or the class definitions and it should work. Getting that "nice-select" to work might take some reading around what it is and interacting with MVC tied controls to get it compatible with the form postback. You might need to use some Javascript and an #Html.HiddenFor so when a value is selected from the <ul> the hidden input that is tied to the model is updated and available for the post-back.

ASP.NET Core MVC - Passing Model Data Back to Controller from View

Should be a relatively simple question but currently I'm not able to pass model values updated in the view back to the controller.
As you can see below we have a very simple dropdown list in the view. The 'tenant' string in the Model should be getting set with whichever value is selected in the dropdown list.
However, when clicking on the submit button the 'tenant' string returns null in the controller...
What am I missing?
Thanks!
Model:
public class TenantModel
{
public List<string> tenantList { get; set; }
public string tenant { get; set; }
}
View:
#model TenantModel
#{
ViewData["Title"] = "Manage";
}
<h1>Manage Databases</h1>
#Html.DropDownListFor(m => m.tenant, new SelectList(Model.tenantList), "Select Tenant", new { #class = "dropdown", #style = "width: 220px" })
<br />
<button type="submit" onclick="location.href='#Url.Action("DropDbs", "Environments", Model)'" class="btn btn-danger">Destroy</button>
Controller
public class EnvironmentsController : Controller
{
public IActionResult Manage()
{
var tenantModel = new TenantModel();
tenantModel.tenantList = Utils.TenantList();
return View(tenantModel);
}
public IActionResult DropDbs(TenantModel tenantModel)
{
return RedirectToAction("Manage");
}
}
I want to have multiple buttons on the form each targeting a different action inside my controller.
In Asp.net core MVC, you can use tag helper asp- to implement it.
<form method="post">
#Html.DropDownListFor(m => m.tenant, new SelectList(Model.tenantList), "Select Tenant", new { #class = "dropdown", #style = "width: 220px" })
<br />
<button type="submit" asp-action="Action1" asp-controller="Controller" class="btn btn-danger">Operate1</button>
<button type="submit" asp-action="Action2" asp-controller="Controller" class="btn btn-danger">Operate2</button>
<button type="submit" asp-action="Action3" asp-controller="Controller" class="btn btn-danger">Operate3</button>
</form>
You're all doing wrong. You're redirecting to action and passing a Model but problem is that not going to fill by your dropdown that why it's null.
You need to wrap your dropdown in form tag and post form to the action.

MVC Model child object null on HTTP post

Hope someone can help me. I am new to MVC, coming from a winforms/console/vb6background.
Apologies if this has already been answered, I am stuggling to understand how I can resolve the below issue.
I have a view model :
public class testvm
{
public int id { get; set; }
public DateTime date { get; set; }
public student studentID { get; set; }
public testvm() { }
public testvm (student s)
{
studentID = s;
}
}
I am pre-populating the student child object of this ViewModel before it is passed to the view.
Student Model :
public class student
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
}
The problem I have is when the model is returned to the create HTTP post method the student child object is blank.
The controller code :
// GET: testvms/Create
public ActionResult Create(int sId)
{
student a = db.students.Find(sId);
testvm b = new testvm(a);
return View(b);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,date,student")] testvm testvm)
{
if (ModelState.IsValid)
{
db.testvws.Add(testvm);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(testvm);
}
View code:
#model WebApplication2.Models.testvm
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>testvm</h4>
<hr />
#Html.HiddenFor(model => model.studentID.ID)
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.date, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.date, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.date, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
The model object on the view is populated with the student information. When this is passed back to Create POST controller the student child object is null!
Can somebody please advise where I am going wrong or of the correct way to achieve this?
My application will have many forms that will all need to be pre-populated with student information. Each student will have many forms that will need to be filled out for them.
Many thanks in advance,
Rob
For every property in domain model (in your case testvm) you must have an EditorFor or Input element (like TextBoxFor or so) on your view(or HiddenFor for ID or other non user ui data).It may be a pain binding nested models in MVC as the DefaultModelBinder may not be able to bind whole object.However it would be safer approach to expose only the required properties on view like
#Html.HiddenFor(model => model.studentID.ID)
#Html.HiddenFor(model => model.studentID.Name)
and later on Controller Side
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(testvm testvm)
{
var originalobj=db.get //get fresh copy from data store
originalobj.Name=testvm.Name;
// ...other properties
//perform required operations on originalobj
}
you may use AutoMapper for this Purpose as
Mapper.CreateMap<testvm,testvm>();
originalobj=Mapper.Map<testvm,testvm>(testvm,originalobj);
you may find more information about Automapper on :
https://github.com/AutoMapper/AutoMapper/wiki/Getting-started
Your property name is called studentId (even though standard C# property naming convention dictates that it should have been called StudentId):
public student studentID { get; set; }
But in your Bind attribute you seem to have specified some student property which doesn't really exist on your view model:
[Bind(Include = "id,date,student")]
So you probably want to get rid of this Bind attribute from your controller action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(testvm testvm)
{
...
}
Also note that you only have a hidden field for the student id inside your form:
#Html.HiddenFor(model => model.studentID.ID)
You don't have a corresponding hidden field for the student name property, so it will never be posted back to your controller action.
Your attribute [Bind(Include = "id,date,student")] should include the names of the properties that you want to be set, student isn't in your model, but studentID is, they have to match.
You don't have to explicitly specify all of the field names that you want to be bound to your model, by default they will be bound anyway unless you tell the binder NOT to bind it by using [Bind(Exclude = "id,date,student")]. Therefore as it currently stands, I'd recommend removing your Include attribute to ease maintenance unless there is an important reason for using it and simply ensure that the models that you bind to only include the values you need.
Secondly, you have to make sure that any values that you are posting back from a form in your view have the same parameter names and are structured the same as the ones that you want to be bound to the request model.
This:
#Html.HiddenFor(model => model.studentID.ID)
Is not the same as:
#Html.HiddenFor(model => model.studentID)

Template ViewModel's SelectList property magically selected when rendered in view

I have a master-detail view that allow users to dynamically add child records through jQuery DOM manipulation0, and then posting the whole master-detail form back to my HttpPost Edit method. My master view model is like this:
public class FooViewModel
{
// Other properties skipped for brevity
public ICollection<BarViewModel> Bars { get; set; }
}
My child view model:
public class BarViewModel
{
public int BazId { get; set; }
public IEnumerable<SelectListItem> BazSelectList { get; set; }
}
In my Edit action, I populate my view model through Entity Framework and Automapper:
public class FooController : Controller
{
public ActionResult Edit(int id)
{
// Fetch from db through Entity Framework,
// project to view model through AutoMapper
var viewModel = FooRepository.GetById(id)
.Project()
.To<FooViewModel>()
.Single();
// Populate ViewBag with an empty template BarViewModel to be manipulated
// through jQuery
ViewBag.BarTemplateViewModel = new BarViewModel
{
BazSelectList = FooRepository.GetBazSelectList()
};
return View(viewModel);
}
[HttpPost]
public ActionResult Edit(FooViewModel viewModel)
{
// Skipped for brevity
}
}
From the debugger, I made sure all SelectListItem in BazSelectList had the Selected property value of false when a GET request is triggered to my Edit action method. Then my view is rendered:
#model FooViewModel
#using (Html.BeginForm())
{
#*Other properties skipped for brevity*#
#*Model binding magic here, editor template rendered for each BarViewModel*#
#Html.EditorFor(m => m.Bars)
<button type="button" class="btn btn-default">Add Bar</button>
}
#* BarViewModel template here *#
#Html.Partial("EditorTemplates/BarViewModel",
(BarViewModel)ViewBag.BarTemplateViewModel)
My BarViewModel editor template:
#model BarViewModel
<div class="form-group">
#Html.LabelFor(m => m.BazId, new { #class = "col-md-5 control-label" })
<div class="col-sm-6">
#Html.DropDownListFor(m => m.BazId,
Model.BazSelectList,
string.Empty,
new { #class = "form-control" })
</div>
</div>
When the view renders, ViewBag.BarTemplateViewModel.BazSelectList has one of the SelectListItem selected when it shouldn't, since I am passing an empty BarViewModel instance to the ViewBag. This is confirmed since I can see BazSelectList being pre-selected when I clicked the "Add Bar" button. The expected behaviour is to have a non-selected dropdown list. Can anyone help?
Eventually I found a solution. Wrapping the IEnumerable<SelectListItem> to a call to the SelectList constructor solved my problem, i.e. changing this:
#Html.DropDownListFor(m => m.BazId,
Model.BazSelectList,
string.Empty,
new { #class = "form-control" })
to
#Html.DropDownListFor(m => m.BazId,
new SelectList(Model.BazSelectList, "Value", "Text"),
string.Empty,
new { #class = "form-control" })

pass a different model to the partial view

I am trying to pass a different model to the partial view from a view. I have two separate controller actions for both of them and two different view models. But when I call the partial view from within the view it gives me the error
The model item passed into the dictionary is of type 'Application.ViewModels.Model1ViewModel', but this dictionary requires a model item of type 'Application.ViewModels.PartialViewModel'.
I am calling it like this:
#Html.Partial("_CreateUniFunctionPartial")
the model call in the view is
#model Application.ViewModels.Model1ViewModel
and model in partial view file is
#model Application.ViewModels.PartialViewModel
I am not sure how to pass the partial view so it doesnt give this error.
EDIT
Partial view
#model Application.ViewModels.PartialViewModel
#using (Html.BeginForm("partialview", "ApplicationDetail", FormMethod.Post))
{
<div class="form-horizontal">
<h4>UniFunctionViewModel</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.detail, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.detail, new { #placeholder = "Further Information" })
#Html.ValidationMessageFor(model => model.detail)
</div>
</div>
</div>
}
you are using the right method but not passing in the right arguments
you might want to try it like this:
#Html.Partial("~/[path_to_root_only_if_exists]/_CreateUniFunctionPartial.cshtml", new Application.ViewModels.PartialViewModel())
if you do not pass in a model, it will automatically take the one from its parent, which in your case is
Application.ViewModels.Model1ViewModel
One thing you will need to do is regenerate a model or utilize a property in the model. For example:
public class OuterViewModel
{
public InnerViewModel InnerViewModel { get; set; }
}
public class InnerViewModel
{
public string SomeProperty { get; set; }
}
In the top page, you can accept the OuterViewModel, then pass the InnerViewModel to the Partial.
Outer.cshtml:
#model OuterViewModel
#Html.Partial("_InnerPartial", Model.InnerViewModel)
_InnerPartial.cshtml:
#model InnerViewModel
#using (Html.BeginForm("Inner", "Controller"))
{
<div>
#Html.AntiForgeryToken()
#Html.TextBoxFor(m => m.SomeProperty)
<input type="submit" value="Save" />
</div>
}
This is quite simple to do. There is a html directive which can render a partial view. Following is the code sample:
#Html.Partial("nameOfPartial", Model)
Now here Model could be from your main controller.
or you can define a new controller action with partialviewresult as return type and try to render it in the page like this:
#{Html.RenderAction("Someaction", "somecontroller");}

Categories

Resources