How to update view model in a partial view in MVC5? - asp.net-mvc

I have a View and inside that view I have a div that will contain a partial view.
My issue is this. The user selects an item from the dropdownlist and I load the partial view with the model. The user change changes some of the textboxes and clicks the button to submit the partial view (which is in a Html.BeginForm).
When I go to examine the model in the controller the model doesn't contain the changes that the user made.
Why doesn't the model reflect the changes the user made?
In the main view:
<div id="personInfo" style="display:none;"></div>
My partial view:
#model MyProject.MyModel
#(Html.Kendo().DropDownList().Name("ddlFilters")
.AutoBind(true)
.OptionLabel("--- Select Filter ---")
.DataValueField("ID")
.DataTextField("MYFILTER")
.DataSource(ds =>
ds.Read(r => r.Action("GetPersonFilters", "Home"))
)
.Events(x => x.Select("ddlFilters_onSelect"))
)
#using (Html.BeginForm("PersonAction", "Home", FormMethod.Post, new { #class = "form-horizontal", id = "personForm" }))
{
// Strongly typed Kendo fields. Several DropDownListFor and TextBoxFor
#Html.Kendo().TextBoxFor(x => x.FirstName).HtmlAttributes(new { #class = "form-control kendoTextBox required " })
// Button to post the form data to the controller.
}
My Javascript:
function ddlFilters_onSelect(e) {
var itm = this.dataItem(e.item);
clearForm();
if (itm.ID > 0) {
// Ajax call to get data....
$.ajax({
url: "/Home/GetPerson",
type: "GET",
data: { "myID": itm.ID }
})
.done(function (result) {
//var aaa = data;
$("#personInfo").html(result);
})
.fail(function (xhr, status, err) {
alert(xhr.responseText);
});
}
};
Model:
public partial class MyModel
{
public decimal ID { get; set; }
public string FirstName{ get; set; }
public string LastName{ get; set; }
public string MiddleName{ get; set; }
}
EDIT:
Controller Code:
// Initial call to main view
public ActionResult CreateNewPerson()
{
return View();
}
// Call to load Partial View initially
public PartialViewResult GetPersonInfo()
{
return PartialView("_PersonForm", new MyModel());
}
// Call to load partial view with data
public PartialViewResult GetPerson(int myID)
{
myData = GetFromDB(myID);
return PartialView("_PersonForm", myData);
}
// Method to save partial form
[HttpPost]
public ActionResult PersonAction(MyModel filter)
{
if (ModelState.IsValid)
{
// Go update DB
}
return View("CreateNewPerson");
}

This is not exactly the scenario you described, but this is how my team uses partials:
1) In the ViewModel for your Main View, add a property (e.g. MyModel) for the Model of the partial view.
2) When calling the partial View in the cshtml, make sure you tell MVC where to bind the content of the partial View:
#Html.Partial("_PersonAction", Model.MyModel, new ViewDataDictionary(Html.ViewData) {
TemplateInfo = new TemplateInfo { HtmlFieldPrefix = Html.NameFor(m => m.MyModel).ToString() }
})
Note how we use the TemplateInfo to set the right context for the partial, so the inputs rendered in the partial are prefixed with the correct names to make the modelbinding work. E.g. <input name="MyModel.FirstName">
You can probably fake this in javascript, but don't ask me how.
3) Our controller actions accept the ViewModel of the main page. The <form> is on the main page and surrounds the partial call.

Related

Single strongly Typed Partial View for two similar classes of different types

I have a Register Primary View which shows two different types of Addresses 1. Home Address 2. Mailing Address
public class RegisterModel
{
public AddressModel HomeAddress { get; set; }
public AddressModel MailAddress { get; set; }
}
public class AddressModel
{
public string Street1 { get; set; }
public string Street2 { get; set; }
public string State { get; set; }
public string City { get; set; }
}
My main Register View is Strongly Typed to RegisterModel as follows
#model MyNamespace.Models.RegisterModel
#{
Layout = "~/Views/_Layout.cshtml";
}
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "myForm" }))
{
<div id="form">
#Html.Action("MyAddressPartial")
#Html.Action("MyAddressPartial")
</div>
}
MyAddressPartialView as follows : -
#model MyNamespace.Models.AddressModel
#{
Layout = "~/Views/_Layout.cshtml";
}
<div id="Address">
#Html.TextBoxFor(m=>m.Street1 ,new { #id="Street1 "})
#Html.TextBoxFor(m=>m.Street2,new { #id="Street2"})
#Html.TextBoxFor(m=>m.State ,new { #id="State "})
#Html.TextBoxFor(m=>m.City,new { #id="City"})
</div>
My RegisterController:-
// Have to instantiate the strongly Typed partial view when my form first loads
// and then pass it as parameter to "Register" post action method.
// As you can see the #Html.Action("MyAddressPartial") above in main
// Register View calls this.
public ActionResult MyAddressPartial()
{
return PartialView("MyAddressPartialView", new AddressModel());
}
I submit my Main Form to below mentioned action method in same Register Controller.
[HttpPost]
public ActionResult Register(RegisterModel model,
AddressModel homeAddress,
AddressModel mailingAddress)
{
//I want to access homeAddress and mailingAddress contents which should
//be different, but as if now it comes same.
}
I don't want to create a separate class one for MailingAddress and one for HomeAddress. if I do that then I will have to create two separate strongly typed partial views one for each address.
Any ideas on how to reuse the classes and partial views and make them dynamic and read their separate values in Action Method Post.
Edit 1 Reply to scott-pascoe:-
In DisplayTemplates Folder, I added following AddressModel.cshtml
<div>
#Html.DisplayFor(m => m.Street1);
#Html.DisplayFor(m => m.Street2);
#Html.DisplayFor(m => m.State);
#Html.DisplayFor(m => m.City);
</div>
Also In EditorTemplate Folder, I added following AddressModel.cshtml but with EditorFor
<div>
#Html.EditorFor(m => m.Street1);
#Html.EditorFor(m => m.Street2);
#Html.EditorFor(m => m.State);
#Html.EditorFor(m => m.City);
</div>
Now how do i use them in RegisterView and also how i read values in Controller's post Action Method ? What else would have to be modified ? I have added almost entire code above. I am pretty beginner to MVC.
The typical ASP.NET MVC method for doing this is to use EditorTemplates and DisplayTemplates for your custom types.
In ~/Views/Shared, Create two folders, DisplayTemplates, and EditorTemplates.
In the DisplayTemplates folder create a partial view with the name of your Model, ie (AddressModel), and create a DisplayFor Template.
In the EditorTemplates folder create another partial view named AddressModel.cshtml and create an EditorFor Template.
MVC will then automatically use your templates and give you the data that you are asking for.
Use #Html.EditorFor (or #Html.DisplayFor, for display) in your view:
#model MyNamespace.Models.RegisterModel
#{
Layout = "~/Views/_Layout.cshtml";
}
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "myForm" }))
{
<div id="form">
#Html.EditorFor(m => m.HomeAddress)
#Html.EditorFor(m => MailAddress)
</div>
}
You will not need to have a separate controller action for the parts, just populate the addresses in the RegisterModel before in your controller. Like this:
[HttpGet]
public ActionResult Register() // this will be the page people see first
{
var model = new RegisterModel();
return View(model); // assuming your view is called Register.cshtml
}
[HttpPost]
public ActionResult Register(RegisterModel model){
DosomethingWithHomeAddress(model.HomeAddress);
DosomethingWithMailAddress(model.MailAddress);
model.IsSaved = true; // some way to let the user knwo that save was successful;
// if this is true, display a paragraph on the view
return View(model);
}

ASP.NET MVC Dropdown Selected Item

I have a DropDownListFor that is on my Index page and one in my Create page. Both dropdownlists serve the same purpose.
What I want is when the user selects an item in the Index dropdownlist in the index page, it saves that selected item's value which is a GUID to the session and when the Create page loads, I want the dropdownlist in there to select the item based on the GUID in the session.
At the moment when the user clicks on "Create" and goes to the create page, I am merely setting up an object and sending that object to the Create View.
Edit:
I am sending the user over to the Create page by doing this:
Html.ActionLink("Create New Listing", "Create", null, new { #class = "btn btn-primary" }))
How do I send the GUID of the selecteditem over to the view?
I guess you have a situation like this. Here is the Index view:
#model Models.IndexViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm("SaveGuid", "Flow"))
{
Html.DropDownListFor(x => x.SelectedGuid, Model.Guids, new { onchange = "this.form.submit();" });
}
Here is the Index model:
public class IndexViewModel
{
public Guid SelectedGuid { get; set; }
public SelectList Guids { get; set; }
}
The Index and SaveGuid Action look like this:
private List<Guid> Guids = new List<Guid> { Guid.NewGuid(), Guid.NewGuid() }; // for testing only
public ActionResult Index()
{
var model = new IndexViewModel { Guids = new SelectList(Guids, Guids.First()) };
return View(model);
}
public ActionResult SaveGuid(IndexViewModel model)
{
Session["SelectedGuid"] = model.SelectedGuid;
return new RedirectResult("Create");
}
The Create View looks like this...
#model MvcBootStrapApp.Models.CreateViewModel
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm("SaveGuid", "Flow"))
{
#Html.DropDownListFor(x => x.SelectedGuid, Model.Guids, new { onchange = "this.form.submit();" });
}
#using (Html.BeginForm("SaveCreate", "Flow"))
{
// setup other controls
<input type="submit" value="Submit" />
}
Using a CreateViewModel like this...
public class CreateViewModel
{
public Guid SelectedGuid { get; set; }
public SelectList Guids { get; set; }
// include other model properties
}
The Create and CreateSave ActionResults look like this...
public ActionResult Create()
{
Guid selectedGuid = Guids.First();
if (Session["SelectedGuid"] != null)
selectedGuid = (Guid)Session["SelectedGuid"];
return View(new CreateViewModel
{
Guids = new SelectList(Guids, selectedGuid),
SelectedGuid = selectedGuid
});
}
public ActionResult SaveCreate(CreateViewModel model)
{
// save properties
return new RedirectResult("Index");
}
I used two forms to allow both the change of selected Guid and to postback all the Create properties.
If you want to use Session, what I think you need is to use a form to post to an ActionResult to save the dropdownlist's value and then redirect to the Create page.
public ActionResult SaveGuid(Guid value)
{
Session["SelectedGuid"] = value;
return new RedirectResult("Create");
}
Then in your Create ActionResult, pass the Session value to the Create View's Model.
public ActionResult Create()
{
var selectedGuid = (Guid)Session["SelectedGuid"];
return View(new CreateViewModel { SelectedGuid = selectedGuid, /* include other properties */ };
}
In your view you can set the selected option on the SelectList passed to your DropDownListFor...
#Html.DropDownListFor(
x => x.SelectedGuid,
new SelectList(Model.ListOfStuff, "Key", "Value", Model.SelectedGuid)
)

Validate JQuery UI modal form within another form in MVC 4

I have a form in MVC 4 which contains several fields and, depending on the value of a combo, I need to open a modal dialog form and load into that one 3 additional fields that will impact against the same entity that I'm creating/editing in the main form.
For this modal dialog I'm using the one from jQuery UI.
Now, what I need to do is to validate (Required) the fields within the modal dialog in order to allow the user to retain the entered values which will be submited later by the main form.
My problem is how to perform the validation of those 3 fields from within the modal form (because they wouldn't be able to submit the main form until dialog is closed).
Any hints or ideas?
Regards,
Cesar.
You could use AJAX to submit the form modal to the server. The modal form will have of course a separate view model associated with it. Let's exemplify:
Main view model:
public class MyViewModel
{
[DisplayName("select a value")]
public string SelectedValue { get; set; }
public IEnumerable<SelectListItem> Values { get; set; }
public string SomeOtherProperty { get; set; }
}
Modal dialog view model:
public class DialogViewModel
{
[Required]
public string Prop1 { get; set; }
[Required]
public string Prop2 { get; set; }
[Required]
public string Prop3 { get; set; }
}
Then you could have a controller containing 4 actions:
public class HomeController : Controller
{
// Renders the main form
public ActionResult Index()
{
var model = new MyViewModel
{
Values = new[]
{
new SelectListItem { Value = "1", Text = "item 1" },
new SelectListItem { Value = "2", Text = "item 2" },
new SelectListItem { Value = "3", Text = "item 3" },
}
};
return View(model);
}
// Processes the submission of the main form
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return Content(
string.Format(
"Thanks for filling out the form. You selected value: \"{0}\" and other property: \"{1}\"",
model.SelectedValue,
model.SomeOtherProperty
)
);
}
// Renders the partial view which will be shown in a modal
public ActionResult Modal(string selectedValue)
{
var model = new DialogViewModel
{
Prop1 = selectedValue
};
return PartialView(model);
}
// Processes the submission of the modal
[HttpPost]
public ActionResult Modal(DialogViewModel model)
{
if (ModelState.IsValid)
{
// validation of the modal view model succeeded =>
// we return a JSON result containing some precalculated value
return Json(new
{
value = string.Format("{0} - {1} - {2}", model.Prop1, model.Prop2, model.Prop3)
});
}
// Validation failed => we need to redisplay the modal form
// and give the user the possibility to fix his errors
return PartialView(model);
}
}
Next you could have a main view (~/Views/Home/Index.cshtml):
#model MyViewModel
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(x => x.SelectedValue)
#Html.DropDownListFor(x => x.SelectedValue, Model.Values, new { id = "ddl" })
</div>
<div>
#Html.LabelFor(x => x.SomeOtherProperty)
#Html.TextBoxFor(x => x.SomeOtherProperty, new { id = "otherProperty" })
#Html.ActionLink(
"click here to open a modal and help you fill the value",
"Modal",
"Home",
null,
new { id = "showModal" }
)
</div>
<button type="submit">OK</button>
}
<div id="modal"></div>
and a partial view to contain the modal form (~/Views/Home/Modal.cshtml):
#model DialogViewModel
#using (Ajax.BeginForm(new AjaxOptions { OnSuccess = "handleModalSubmit" }))
{
<div>
#Html.LabelFor(x => x.Prop1)
#Html.EditorFor(x => x.Prop1)
#Html.ValidationMessageFor(x => x.Prop1)
</div>
<div>
#Html.LabelFor(x => x.Prop2)
#Html.EditorFor(x => x.Prop2)
#Html.ValidationMessageFor(x => x.Prop2)
</div>
<div>
#Html.LabelFor(x => x.Prop3)
#Html.EditorFor(x => x.Prop3)
#Html.ValidationMessageFor(x => x.Prop3)
</div>
<button type="submit">OK</button>
}
OK, now all that's left is write some javascript to make the whole thing alive. We start by making sure that we have included all the required scripts first:
<script src="#Url.Content("~/Scripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery-ui-1.8.20.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
and then write our own:
$(function () {
$('#showModal').click(function () {
$.ajax({
url: this.href,
type: 'GET',
cache: false,
data: { selectedValue: $('#ddl').val() },
success: function (result) {
$('#modal').html(result).dialog('open');
}
});
return false;
});
$('#modal').dialog({
autoOpen: false,
modal: true
});
});
function handleModalSubmit(result) {
if (result.value) {
// JSON returned => validation succeeded =>
// close the modal and update some property on the main form
$('#modal').dialog('close');
$('#otherProperty').val(result.value);
} else {
// validation failed => refresh the modal to display the errors
$('#modal').html(result);
}
}

MVC Ajax Begin Form save and then using the ID returned after saving in the view

I have a model with two entities (linked with a foreign key) and each entity has its own tab rendered using a partial view. Each tab also has its own Ajax form. When I save the entity in the first tab I now have the ID of the entity which I want to return to the two partial views in order to enable the saving of the second entity or saving updates to the first entity. I cannot get this value back to the view.
The model:
public class Entity1
{
int ID1 { get; set; }
[Some attributes]
string field1 { get; set; }
}
public class Entity2
{
int ID2 { get; set; }
[Some attributes]
string field2 { get; set; }
}
public class MyModel
{
Entity1 entity1 = new Entity1()
Entity2 entity2 = new Entity2()
}
The controller:
public class MyController : Controller
{
[HttpGet]
public ActionResult Index()
{
var model = new MyModel();
model.entity1.ID1 = 0;
model.entity2.ID2 = 0;
return PartialView(model);
}
[HttpPost]
public ActionResult Index(MyModel model)
{
SaveMyModel(model)
// have tried ModelState.Clear(); here
return PartialView(model);
}
}
And finally one of the two partial views
#model MyModel
#using (Ajax.BeginForm("Index", "Home",
new AjaxOptions
{
HttpMethod = "POST"
}
))
{
#Html.LabelFor(m => m.Entity1.field1)
#Html.EditorFor(m => m.Entity1.field1)
#Html.HiddenFor(m => m.Entity1.ID1)
<div class="form-actions">
<button type="submit">
Next section</button>
</div>
}
My save function either inserts or updates depending on the value of ID1.
The problem is that the values of ID1 always stays at zero and the hidden field is not refreshed on the return. I have tried single stepping through the razor refresh and the correct ID is being sent to the view.
The above is a simplification but it does encapsulate the problem.
Thank you in advance.
UPDATE
I can get this to work if:
I only have a single entity in my model
I add ModelState.Clear(); before the save
I was running into the same issue on my project. The only way for me to resolve it was to not include the id when the it was 0. That way when it came back the id was replaced. So in your example you would do the following:
#model MyModel
#using (Ajax.BeginForm("Index", "Home",
new AjaxOptions
{
HttpMethod = "POST"
}
))
{
#Html.LabelFor(m => m.Entity1.field1)
#Html.EditorFor(m => m.Entity1.field1)
#if(Model.Entity1.ID1 !=0){
Html.HiddenFor(m => m.Entity1.ID1)
}
<div class="form-actions">
<button type="submit">
Next section</button>
</div>
}
You need to remove the value from the ModelState if you intend to modify it in your POST controller action:
ModelState.Remove("Entity1.ID1");
This way you don't need to clear the entire ModelState using ModelState.Clear but only the value you are actually modifying. This way the Html helper will pick the value from your model and not the one in the ModelState.

how to get partialview data in controller

I am using three partialview on a single view, I have a submit button on clicking of which I want to send information to database, I have to retrieve data from all the partialview.
Can You please provide me correct information to do it.
Darin I m using L2S so when I drag my stored procedure, I get code some thing like this in
[global::System.Data.Linq.Mapping.FunctionAttribute(Name="SP_Name")]
public int SP_Name(
[global::System.Data.Linq.Mapping.ParameterAttribute(Name="EmployeeID", DbType="Int")] System.Nullable<int> EmployeeID
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), EmployeeID);
encounterID = ((System.Nullable<int>)(result.GetParameterValue(293)));
return ((int)(result.ReturnValue));
}
}
Updated
<script language="javascript" type="text/javascript">
$(function () {
$('#Form1').submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (data) {
var message = data.Result;
$('#Result').html(message);
}
});
return false;
});
});
</script>
In my Controller I am using
public ActionResult Index(FormCollection frm)
{
My Code ---------------------
return Json(new { Result = "Success" });
}
When I return this I m getting a file in post back and it ask me to save it.
I have checked using flidder, in URL it shows me that the path as / only
where as If I fill any particular partialview It shows something like /Controller Name/Partialview
Can You help me with this problem
Well, sending data to a controller action is usually done by performing an HTTP request to this controller action. There are different ways of performing an HTTP request:
Use a <form> tag pointing to this action
Use AJAX
So if you go with the first approach you could have a single <form> wrapping all the partials which would have multiple submit buttons (with different names). Then when you click on one submit buttons all the input fields will be sent to the controller action and then inside the controller action you could process the data based on which submit button was clicked.
If you use the second option, well, then simply harvest the values you need to be sent uipon button click and send them along the AJAX request.
UPDATE:
As requested in the comments section here's how the first technique could be put into action. It uses two partials instead of three but it could be easily extrapolated.
As always you start by defining a view model which will represent the data you would like to work with on this particular view:
public class MyViewModel
{
public Partial1ViewModel Model1 { get; set; }
public Partial2ViewModel Model2 { get; set; }
}
public class Partial1ViewModel
{
public string Foo { get; set; }
}
public class Partial2ViewModel
{
public string Bar { get; set; }
}
Then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
Model1 = new Partial1ViewModel { Foo = "foo" },
Model2 = new Partial2ViewModel { Bar = "bar" },
};
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// Here you have access to model.Model1.Foo and model.Model2.Bar =>
var button = "";
if (!string.IsNullOrEmpty(Request["submit1"]))
{
// submit1 button was used
button = "submit1";
}
else if (!string.IsNullOrEmpty(Request["submit2"]))
{
// submit2 button was used
button = "submit2";
}
var result = string.Format("thanks for submitting using {0}", button);
return Content(result, "text/plain");
}
}
and then a main view (~/Views/Home/Index.cshtml):
#model MyViewModel
#using (Html.BeginForm())
{
#Html.EditorFor(x => x.Model1)
#Html.EditorFor(x => x.Model2)
}
and the two corresponding editor templates (or partials if you will):
~/Views/Home/EditorTemplates/Partial1ViewModel.cshtml:
#model Partial1ViewModel
<h2>Partial 1</h2>
<div>
#Html.LabelFor(x => x.Foo)
#Html.EditorFor(x => x.Foo)
<input type="submit" value="Submit me!" name="submit1" />
</div>
~/Views/Home/EditorTemplates/Partial2ViewModel.cshtml:
#model Partial2ViewModel
<h2>Partial 2</h2>
<div>
#Html.LabelFor(x => x.Bar)
#Html.EditorFor(x => x.Bar)
<input type="submit" value="Submit me!" name="submit2" />
</div>

Resources