MVC model binding, Initial Model Missing - c#

In this instance I have two pages, in the post method of the first I return the view/viewmodel of the second like so :
[HTTPPost]
public Task<ActionResult> Page1(Page1Model model)
{
var Page2Model = GrabDataMethod(model);
return View("Page2", Page2Model); //Point 1
}
[HTTPPost]
public Task<ActionResult> Page2(Page2Model model //Point 2)
{
var updatedModel= RunFiltersMethod(model)
return View(updatedModel);
}
Now, in this case Page2 renders properly from (Point 1) with all values passed in above from the GrabDataMethod. However, when I POST for Page2 the Page2Model I receive at (Point 2) has none of the original entries, e.g. everything not modified directly by Page2 itself is null or default (in fact it seems the model from the post method is a new model entirely). I've made a horrible workaround for the time being, but I need a proper fix, is there any reason that this would be happening?
Page2 View Code
#model Mvc2013.Models.Page2Model
#using (Html.BeginForm("Page2", "Controller"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true)
<div class="row">
<div class="col-md-12">
#Html.Kendo().Chart(<!-- code removed, this is working -->)
</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="col-md-8">
#Html.LabelFor(model => model.Prop1, new { #class = "control-label" })
</div>
<div class="col-md-4">
#Html.EditorFor(model => model.Prop1, new { #class = "control-label" })
#Html.ValidationMessageFor(model => model.Prop1)
</div>
</div>
<div class="col-md-3">
<div class="col-md-8">
#Html.LabelFor(model => model.Prop2, new { #class = "control-label" })
</div>
<div class="col-md-4">
#Html.EditorFor(model => model.Prop2, new { #class = "control-label"})
#Html.ValidationMessageFor(model => model.Prop2)
</div>
</div>
</div>
<!-- This carries on similarly for lots more attributes -->
<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>
}

Inside the Beginform you should add #Html.HiddenFor(model => model.Something) where the Something are the properties which you want back on the postback.
If you don't then the property values will be default values.

Related

MVC partial view controller not running on load

Im trying to load a MVC partial view in to a modal popup. The popup contains a drop down list which I am trying to populate on the ActionResult that loads when the view is loaded. The problem is the controller code isn't getting ran.
My code is as follows -
View:
<div id="modal_form_horizontal_addnote" class="modal fade">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header bg-primary">
<button type="button" class="close" data-dismiss="modal">×</button>
<h5 class="modal-title">Add Note</h5>
</div>
#Html.Partial("AddNote", new IHD.Core.Models.CreateTicketNoteModel { TicketHeaderId = Model.Id })
</div>
</div>
The partial view:
#model IHD.Core.Models.CreateTicketNoteModel
#using (Html.BeginForm("AddNote", "Ticket", FormMethod.Post))
{
#Html.HiddenFor(model => model.TicketHeaderId)
#Html.AntiForgeryToken()
<div class="row">
<div class="col-md-12">
<div class="tab-content">
<div class="tab-pane active" id="highlighted-justified-tab1">
<div class="form-group">
#Html.LabelFor(model => model.WorkType, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-12">
#Html.DropDownListFor(model => model.WorkType, new SelectList(Model.WorkTypeOptions, "Key", "Value", Model.WorkType), new { #name = "select", #class = "form-control" })
#Html.ValidationMessageFor(model => model.WorkType, "", new { #class = "text-danger" })
</div>
</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>
</div>
}
The controller:
public ActionResult AddNotePhase(int id = 0, int taskId = 0)
{
CreateTicketPhaseNoteModel note = new CreateTicketPhaseNoteModel();
note.TicketHeaderId = id;
// Populate the work type dropdown list
note.WorkTypeOptions.Add("", "-- Select a Work Type --");
note.WorkTypes = _workTypeService.GetAll().ToList();
foreach (var c in note.WorkTypes)
{
note.WorkTypeOptions.Add(c.Id.ToString(), c.Description);
}
//note.PhaseTask = taskId;
return PartialView(note);
}
You controller code isn't being hit because you are passing a model to the partial view and not calling the action. You need to use #{Html.RenderAction();} instead to call your action with the appropriate values.

Submitting a form from a partial view

I'm trying to submit a form from a modal generated in a partial view. And I don't know how to get back the submitted form.
Here is the view:
#model Myproject.ViewModels.GetMonitorFromDeviceViewModel
#{
ViewBag.Title = "GetMonitorFromDevice";
Layout = "~/Views/Shared/ManagementPage.cshtml";
}
<div id="Accordion">
#{
foreach (var type in Model.AvailableTypesAvailableMonitors)
{
<div class="card">
<div class="card-header">
<a class="card-link" data-toggle="collapse" data-parent="#accordion" href="##type">
#type
</a>
</div>
<div id="#type" class="collapse show">
<div class="card-body">
#{
foreach (var monitor in Model.ActiveMonitors)
{
if (monitor.Type == #type)
{
<p>
#monitor.Name
<span class="btn btn-xs btn-primary btnEdit" onclick="createModal('#Url.Action("NameMonitor", "DeviceManager" , new { idDevice = monitor.DeviceOwner.ID, monitorName = monitor.Name })')">Details</span>
</p>
}
}
}
</div>
</div>
</div>
}
}
</div>
And here is my modal at the bottom of the page:
<div class="modal fade" id="myModal" role="dialog" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog">
<div class="modal-content" id="modelContent">
</div>
</div>
</div>
<script>
function createModal(url) {
$('#modelContent').load(url);
$('#myModal').modal('show');
}
</script>
And finally, here is my partial view that is displayed as a modal:
#model MyProject.ViewModels.NameMonitorModal
#using (Html.BeginForm("NameMonitor", "DeviceManager", FormMethod.Post))
{
<div class="modal-body">
<div class="form-group">
#Html.LabelFor(model => model.NewName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.NewName, new { htmlAttributes = new { #class = "form-control", #Value = Model.TrueName } })
#Html.ValidationMessageFor(model => model.NewName, "", new { #class = "text-danger" })
</div>
</div>
#Html.HiddenFor(model => model.TrueName)
#Html.HiddenFor(model => model.IdDevice)
</div>
<div class="modal-footer">
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save name" class="btn btn-default" data-dismiss="modal" />
</div>
</div>
</div>
}
In my controller, I have an action for my partial view called ActionResult NameMonitor.
In order to catch the submited form, I tried to add another action with the [HttpPost] tag with the same name but doesn't work. I also tried to use the main page action with the [HttpPost] tag but it doesn't work either. As you can see, I have specified the action and controller in the form itself but its still not working.
Now, I'm a little bit out of idea of how I can get the information from my modal back.
data-dismiss="modal" will close the modal without submitting the form, see Dismiss and submit form Bootstrap 3
You can change the submit button to call a JavaScript function to submit the form, then close the modal.
function submitModal() {
$('#myFormId').submit();
$('#myModal').modal('hide');
}

Ajax.BeginForm posts to surface controller and updates target id with full page instead of partial view

Have been trying to get a member profile management area working with ajax as each section of the page is hidden within a show hide div.
I have used ajax before in MVC applications but have never used it with umbraco surface controllers before. I'm unsure why returning a partial view in the controller is outputting the whole page and not just the partial view that I am giving to it.
Controller:
[HttpPost]
[ActionName("MvcMemberEditProfileDetails")]
public ActionResult MvcMemberEditProfileDetails(MvcMemberEditProfileDetailsModel model)
{
var memberService = Services.MemberService;
var currentUser = Membership.GetUser();
var member = memberService.GetByEmail(currentUser.Email);
bool result = false;
if (ModelState.IsValid)
{
...
}
if (result)
{
...
}
return PartialView("MvcMemberEditProfileDetails", model);
}
View:
#model Umbraco714.Models.MvcMemberEditProfileDetailsModel
#using (Ajax.BeginForm("MvcMemberEditProfileDetails", "MvcMember", new AjaxOptions() { HttpMethod = "POST", UpdateTargetId = "MvcMemberEditProfileDetails", InsertionMode = InsertionMode.Replace }))
{
if (Model.Result != null)
{
if (Model.Result == true)
{
<div id="result" class="alert alert-success">
#Html.Raw(Model.ResultMessage)
</div>
}
else
{
<div id="result" class="alert alert-danger">
#Html.Raw(Model.ResultMessage)
</div>
}
}
<div class="form-default">
<div class="row">
<div class="col-md-6">
<div class="form-group">
#Html.LabelFor(m => m.FirstName)
#Html.TextBoxFor(m => m.FirstName)
#Html.ValidationMessageFor(m => m.FirstName)
</div>
</div>
<div class="col-md-6">
<div class="form-group">
#Html.LabelFor(m => m.LastName)
#Html.TextBoxFor(m => m.LastName)
#Html.ValidationMessageFor(m => m.LastName)
</div>
</div>
<div class="col-md-6">
<div class="form-group">
#Html.LabelFor(m => m.CompanyName)
#Html.TextBoxFor(m => m.CompanyName)
#Html.ValidationMessageFor(m => m.CompanyName)
</div>
</div>
<div class="col-md-6">
<div class="form-group">
#Html.LabelFor(m => m.CompanyPosition)
#Html.TextBoxFor(m => m.CompanyPosition)
#Html.ValidationMessageFor(m => m.CompanyPosition)
</div>
</div>
<div class="col-xs-12">
<div class="form-group">
#Html.LabelFor(m => m.CompanyBio)
<span class="bs-tooltip" data-toggle="tooltip" data-placement="top" title="Max 1000 characters long"><i class="fa fa-info-circle" aria-hidden="true"></i></span>
#Html.TextAreaFor(m => m.CompanyBio, new { #rows = "4", #maxlength = "1000" })
#Html.ValidationMessageFor(m => m.CompanyBio)
</div>
#TempData["Status"]
<div class="form-group nomargin">
<div class="text-right">
<button class="button" type="submit"><i class="fa fa-floppy-o" aria-hidden="true"></i> Update</button>
</div>
</div>
</div>
</div>
</div>
}
I have everything that needs to be included (as far as I'm aware) well before the form and there are no console errors.
<script src="/scripts/jquery.js"></script>
<script src="/scripts/bootstrap.min.js"></script>
<script src="/scripts/jquery-val.js"></script>
<script src="/scripts/jquery.unobtrusive-ajax.min.js"></script>
I have also made sure that UnobtrusiveJavaScriptEnabled is set to true in the web.config but I'm still getting a full page rendered when I post the form.
Initially:
When the page loads and the form shows
After:
When the form is submitted and the correct partial view is returned but inside of an entire
Feeling pretty dumbfounded that I've spent more that a couple of hours looking into this even though it's clearly working in a sort of way.
Is it possible / a known thing for this to happen? I searched around but couldn't find any topics with a similar issue, unless I was just wording things wrong.
Just looking for a nudge in the right direction if anybody has any ideas?
Umbraco can be funny with partials. Try returning CurrentUmbracoPage() in the controller.
As for the message, you could use TempData as it only lasts for one request, rather than the model.

remove items to a bound list model in ASP MVC.NET

I followed an online tutorial to dynamically add items to a bound list model using ajax, which works perfectly. (http://www.mattlunn.me.uk/blog/2014/08/how-to-dynamically-via-ajax-add-new-items-to-a-bound-list-model-in-asp-mvc-net/comment-page-2/#comment-68909)
My question is, how would I correctly remove items from the list?
Right now what I have done is add a delete link which when clicked removes the item from the view. However, when I submit the form I noticed that the modelState is no longer valid and it has null entries for the item that was removed from the view. So I guess the model is not being updated.
Test.cshtml
#model TesterManager.Models.Test
<div class="form-group">
#Html.LabelFor(model => model.Software, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div class="form-group">
<div class="col-md-5">
#Html.DropDownListFor(m => m.Software, TesterManager.Models.Helper.GetTestSoftwares(), "Choose a USB Card", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Software, "", new { #class = "text-danger" })
</div>
<div class="col-md-5">
#Html.EditorFor(model => model.Version, new { htmlAttributes = new { #class = "form-control", #title = "Enter a USB FW Version", #placeholder = "Enter a USB FW Version" } })
#Html.ValidationMessageFor(model => model.Version, "", new { #class = "text-danger" })
</div>
<div class="col-md-2">
Delete
</div>
</div>
</div>
</div>
AdminTesterConfigurations.cshtml (snippet):
#model TesterManager.Models.AdminTesterConfigurations
<div class="form-group">
<div class="col-md-6">
....
</div>
</div>
<hr />
<div class="form-group">
<div class="col-md-12">
<h3>Test Software</h3>
<div id="test-list">
#Html.EditorForMany(x => x.Tests, x => x.Index)
</div>
<input type="button" id="add-test" value="Add" />
</div>
</div>
RequestEditViewModel.cshtml:
#model TesterManager.Models.RequestEditViewModel
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.EditorFor(model => model.ShippingLocation)
#Html.EditorFor(model => model.RequesterTesterConfigurations)
#Html.EditorFor(model => model.AdminTesterConfigurations)
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
Edit.cshtml:
#model TesterManager.Models.RequestEditViewModel
#Styles.Render("~/Content/Edit")
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#section Scripts
{
<script>
jQuery(document).ready(function ($) {
$('#add-test').on('click', function () {
jQuery.get('/TesterManager/Request/AddTest').done(function (html) {
$('#test-list').append(html);
});
});
});
</script>
}
#using (Html.BeginForm())
{
<h2>Edit</h2>
#Html.AntiForgeryToken()
#Html.EditorFor(x => x);
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
RequestController.cs
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(RequestEditViewModel request)
{
if (ModelState.IsValid)
{
Request domainRequest = new Request(request);
requests.Add(domainRequest);
return RedirectToAction("Index");
}
return View(request);
}
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public ActionResult AddTest()
{
var request = new RequestEditViewModel();
request.AdminTesterConfigurations.Tests.Add(new Test());
return View(request);
}
I realized that the problem was that when I would remove the item from the list in the view, I was not removing the hidden input for the collection indexer as well. I updated the code to remove the hidden input and it works fine now.

Give partial view an object after a post

I am relatively new to programming C# with ASP.NET MVC and I have a question. I am building some kind of cinema add show feature. I currently got the code where i can get all the CinemaShows with the right HallID based on a selected date (and send to the controller by a [Post]. Now I've put the information I need in a list of a ViewModel and I want to pass it along with the partial view so that when the POST method is called, the right info shows in a partial view. I will show the views and controllers code.
#using IVH7A5.WebUI.Models;
#using IVH7A5.Domain.Entities;
#model addShowViewModel
#{
ViewBag.Title = "addShow";
}
<div style="float:inherit">
#using (Html.BeginForm("getDateShows", "Show")) {
#Html.AntiForgeryToken()
<div class="form-horizontal">
<div class=" form-group">
<label class="control-label col-md-2">Datum</label>
<div class="col-md-10">
<input type="date" class="form-control" id="datum" name="datum" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-default">Toepassen</button>
</div>
</div>
</div>
}
</div>
<div>
#{Html.RenderPartial("_ShowsByCinema");}
</div>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Show</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<label class="control-label col-md-2">Zalen</label>
<div class="col-md-10">
<select class="form-control" name="cinemanumbers" id="cinemanumbers">
#foreach (Cinema cin in Model.Cinemas) {
<option value="#cin.cinemaNumber">#cin.cinemaNumber</option>
}
</select>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2">Zalen</label>
<div class="col-md-10">
<select class="form-control" name="film" id="film">
#foreach (Movie movie in Model.Movies) {
<option value="#movie.MovieTitle">#movie.MovieTitle</option>
}
</select>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2">3D</label>
<div class="col-md-10">
<div class="checkbox">
#Html.EditorFor(model => model.Shows.ThreeD)
#Html.ValidationMessageFor(model => model.Shows.ThreeD, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2">Starttijd</label>
<div class="col-md-10">
#Html.EditorFor(model => model.Shows.StartTime, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Shows.StartTime, "", 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>
}
Note I do have some Dutch labels, as seen I use a different ViewModel that I want to pass to the partial view. My guess is that the RenderPartial() line needs some work.
My partial view:
#using IVH7A5.WebUI.Models;
#using IVH7A5.Domain.Entities
#model List<ShowsByCinema>
<div class="row">
<div class="col-lg-6 col-lg-offset-3 col-md-8 col-md-offset-2 col-sm-12 movie-film-times">
<h3>Zalen en tijden:</h3>
<div class="movie-by-day">
#foreach (ShowsByCinema x in Model) {
<div class="row">
<div class="col-xs-3 movie-day">
<strong>#x.cinemaNumber</strong>
</div>
<div class="col-xs-9">
#foreach (Show s in x.Shows) {
#s.StartTime.ToString("HH:mm") #:- #s.StartTime.AddMinutes(s.movie.MovieLength)
}
</div>
</div>
}
</div>
</div>
</div>
And, of course, the controller:
using IVH7A5.Domain.Abstract;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using IVH7A5.Domain.Entities;
using IVH7A5.WebUI.Models;
namespace IVH7A5.WebUI.Controllers {
public class ShowController : Controller {
private ICinemaRepository cinemaRepository;
private IMovieRepository movieRepository;
private IShowRepository showRepository;
public ShowController(ICinemaRepository cinemaRepo, IMovieRepository movieRepo, IShowRepository showRepo) {
cinemaRepository = cinemaRepo;
showRepository = showRepo;
movieRepository = movieRepo;
}
// GET: Show
public ActionResult Index() {
var model = new addShowViewModel();
model.Cinemas = cinemaRepository.cinemas;
model.Movies = movieRepository.Movies;
return View("addShow", model);
}
[HttpPost]
public ActionResult getDateShows(FormCollection form) {
List<ShowsByCinema> ShowsByCinemaList = new List<ShowsByCinema>();
DateTime date = Convert.ToDateTime(form["datum"]);
foreach (Cinema c in cinemaRepository.cinemas) {
ShowsByCinemaList.Add(new ShowsByCinema {
cinemaNumber = c.cinemaNumber,
Shows = showRepository.getshowsByCinemaAndDate(date, c.cinemaID)
});
}
return PartialView("_ShowsByDayPartial", ShowsByCinemaList);
}
}
}
So, to give a quick summary: I would like to know to render the partial view with the proper ViewModel when I call the POST method getDateShows. I am very new to programming in general and I would like to apologize if it contains code that is in contradiction with the code conventions. I hope someone can give me the solution to my problem. If any more information/code is required, feel free to ask.

Categories