How to display additional readonly view data in Edit view MVC - asp.net-mvc-3

I have a View that handles the Edit action for editing a weekly update on your weight and nutrition. Editing a singular model is all good. I am using EditorFor to create the fields.
My problem is that I want to also display a read only version of last week's results as a guide but I would like to use DisplayFor so that it formats bools to be disabled checkboxes and formats the dates based on my formatting in the model. I added the model to the Viewbag and tried to access it by using #Html.DisplayFor(x => x.BodyWeight, (myproject.Models.WeeklyReport)ViewBag.LastReport) however it just brings up the data in the model that I sent to the view and not the Viewbag data. What is the best method to display this kind of data while keeping the constraints/formatting of the model intact?
Thanks.
View
#model myproject.Models.WeeklyReport
<h2>Weekly Report - Week 1</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<table class="weeklyreport">
<tr>
<th>Week</th>
<td class="result-bold">Goals</td>
<td>Current Week</td>
</tr>
<tr>
<th>Body Weight</th>
<td class="result-bold">#Html.DisplayFor(x => x.BodyWeight, (myproject.Models.WeeklyReport)ViewBag.Goals)</td>
<td>#Html.EditorFor(model => model.BodyWeight)
#Html.ValidationMessageFor(model => model.BodyWeight)</td>
</tr>
<tr>
<th>Diary Reviewed</th>
<td class="result-bold">#Html.DisplayFor(x => x.DiaryReviewed, (myproject.Models.WeeklyReport)ViewBag.Goals)</td>
<td>#Html.EditorFor(model => model.DiaryReviewed)
#Html.ValidationMessageFor(model => model.DiaryReviewed)</td>
</tr>
</table>
Controller
public ActionResult Edit(int id)
{
WeeklyReport goal = new WeeklyReport()
{
BodyWeight = 60,
DiaryReviewed = true
};
WeeklyReport rpt = new WeeklyReport()
{
BodyWeight = 68,
DiaryReviewed = false
};
ViewBag.LastReport = goal;
return View(rpt);
}

You can create a viewmodels or partialViews and pass the viewbag item to the partial.
An example of viewmodel usage
public class WeeklyReportViewModel
{
public WeeklyReport LastReport { get; set; }
public WeeklyReport Report { get; set; }
}
combine the reports
public ActionResult Edit(int id)
{
WeeklyReport goal = new WeeklyReport()
{
BodyWeight = 60,
DiaryReviewed = true
};
WeeklyReport rpt = new WeeklyReport()
{
BodyWeight = 68,
DiaryReviewed = false
};
WeeklyReportViewModel viewmodel = new WeeklyReportViewModel()
{
LastReport = goal,
ActualReport = rpt
}
return View(viewmodel);
}
In the view
#model myproject.ViewModels.WeeklyReportViewModel
#Html.DisplayFor(x => x.LastReport.BodyWeight)

Related

How to update View using Ajax in asp.net mvc?

I have a simple form with drop down list with people names(exhibitors). After I choose one of them and click link “Get Exhibitor data” I want to update only a part of my Home site and show data of chosen exhibitor.
Structure of my folders in project:
My home controller looks like follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace WebApplication5.Controllers
{
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
return View();
}
public PartialViewResult GetExhibitorDataById(int? Id)
{
List<Exhibitor> exhibitors = new List<Exhibitor>()
{
new Exhibitor()
{
Id=1,
Name= "Tom",
Surname="Cruise"
},
new Exhibitor()
{
Id=2,
Name= "Jennifer",
Surname="Lopez"
},
};
if (Id == 1)
{
//return PartialView("_Exhibitor", exhibitors[0]);
Session["ExhibitorData"] = exhibitors[0];
return PartialView("_Exhibitor");
}
else if(Id==2)
{
//return PartialView("_Exhibitor", exhibitors[1]);
Session["ExhibitorData"] = exhibitors[1];
return PartialView("_Exhibitor");
}
else
{
//return PartialView("_Exhibitor", new Exhibitor());
Session["ExhibitorData"] = new Exhibitor();
return PartialView("_Exhibitor");
}
}
public class Exhibitor
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
}
}
My Index View code from Home folder looks like this:
#using WebApplication5.Controllers
<h2>Exhibitors</h2>
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
#Html.DropDownList("ExhibitorsList", new List<SelectListItem>
{
new SelectListItem {Text ="Tom Cruise", Value = "1" },
new SelectListItem {Text ="Jennifer Lopez", Value = "2" },
}, "Select Exhibitor" )
#Ajax.ActionLink("Get Exhibitor data", "GetExhibitorDataById", new { Id = 1 }, new AjaxOptions()
{
HttpMethod = "GET",
UpdateTargetId = "divExhibitors", // ID of the HTML element to update
InsertionMode = InsertionMode.Replace // Replace the existing contents
})
<div id="divExhibitors">
</div>
But I would like to set parameter Id of Ajax.ActionLink, to value from DropDownList called “ExhibitorsList” and I don’t know how to do that.
Partial View code “_Exhibitor” looks like this:
#using WebApplication5.Controllers
<table>
#if (Session["ExhibitorData"] != null)
{
<tr>
<td>Id</td>
#*#{HomeController.Exhibitor exhibitor = ((HomeController.Exhibitor)(#Session["ExhibitorData"]))};*#
#*<td>#exhibitor.Id</td>*#
<td>#((HomeController.Exhibitor)(#Session["ExhibitorData"])).Id</td>
</tr>
<tr>
<td>Name</td>
<td>#((HomeController.Exhibitor)(#Session["ExhibitorData"])).Name</td>
</tr>
<tr>
<td>surname</td>
<td>#((HomeController.Exhibitor)(#Session["ExhibitorData"])).Surname</td>
</tr>
}
</table>
When I tried to run my app and then click Aajax.ActionLink something gone wrong because after I clicked Ajax.ActionLink I go to different url.
Home View:
After Clicked Ajax.ActionLink
What I want is to choose person name from dropdownlist, then click Ajax.ActionLink and get data of chosen person(exhibitor) without any redirect or refresh the site as I wrote at the beginning. Im also curious if it possible to do that only with one View- "Index" without using partialview.
As Stephen Muecke said you should change your controller ina View to pass model and show it:
Controller:
public PartialViewResult GetExhibitorDataById(int? Id)
{
List<Exhibitor> exhibitors = new List<Exhibitor>()
{
new Exhibitor()
{
Id=1,
Name= "Tom",
Surname="Cruise"
},
new Exhibitor()
{
Id=2,
Name= "Jennifer",
Surname="Lopez"
},
};
//here how you pass model to View
ViewData.Model = exhibitors.FirstOrDefault(x => x.Id == Id);
return PartialView("_Exhibitor");
}
Your View will be:
#model WebApplication5.Controllers.Exhibitor
#using WebApplication5.Controllers
<table>
#if (Model != null)
{
<tr>
<td>Id</td>
<td>#Model.Id</td>
</tr>
<tr>
<td>Name</td>
<td>#Model.Name</td>
</tr>
<tr>
<td>surname</td>
<td>#Model.Surname</td>
</tr>
}
</table>
What i did is make your View strongly typed with #model WebApplication5.Controllers.Exhibitor and then use your model properties to show data.
Finally I found the solution. "The problem was due to the corrupted unobtrusive-ajax.min.js file." I found post asp.net mvc partialview #Ajax.ActionLink doesn't work and I had the same problem. I uninstalled unobtrusive-ajax.min.js and intalled it once again. It was good solution in my case and now ajax works properly.

Updating only the partial view contained in a mvc 3 page?

I have a MVC 3 page that returns a list of user responses with a partial view called "memo" (which displays/add memos) for each response. When I add a memo to a response, it should update the db and the list of memos for that response. It should be partial page update via ajax, which effects only the partial view "memo".
The view Response.chtml that contains "memo":
#using (Html.BeginForm("Response", "User", FormMethod.Post, new { id = "UserResponse" }))
{
.... code removed ....
#foreach (var response in Model)
{
<div class="qna"><input type="text" id=#response.responseId value="#response.ResponseText" />
<div>#Html.Partial("_memo", response.responseId)</div>
}
.....
The partial page "_memo.chtml":
<div>add memo</div>
<ul id="memos">
#foreach (var memo in Model) {
<li>#memo.Text</li>
}
</ul>
<form method="post" id="memoForm"
action="#Url.Action("AddMemo")">
#Html.TextArea("Memo", new { rows = 5, cols = 50 })
<br />
<input type="submit" value="Add" />
</form>
Controller for view User/Response:
[HttpGet]
public ActionResult Response(id)
{
.....
return View(responses);
I just started with the code above, need help filling the blanks.
If I pass the response Id to the partial view, how do I pull the list of memos for that response? Will it involve ajax? (instead of ..Partial("_memo", response.memos))
How do I update the partial view via ajax call. What is ajax call (sample code) on the client side and how would the controller look? When the ajax call is successful, how do I update the list memos div="memos" to reflect the new memo?
Will the form action from Response conflict with form action of the partial view Memo?
Answers to Questions:
You shouldn't pass the responseId to the partial, you should pass the memo collection from your response object and make your partial view strongly typed to that collection.
See full code example below.
You don't need the form in the partial since you're making a simple ajax call to add the new memo. See full code example below.
This is a modified example from a project I am currently working on:
There is a bit of code to follow, so here goes:
This is my model. There are several sections on a career planning form, one of which is a section to select and update competencies. The SelectCompetencies model has a collection of competencies within it. The user will have the ability to add competencies. When they do, it will be added to the database and will update the list of competencies in the partial.
public class CareerPlanningFormViewModel
{
// code removed ...
public SelectCompetenciesModel SelectCompetencies { get; set; }
// code removed ...
}
public class SelectCompetenciesModel
{
public int CareerPlanningFormID { get; set; }
public IList<CompetencyModel> Competencies { get; set; }
public byte MaximumCompetenciesAllowed { get; set; }
}
public class CompetencyModel
{
public int CompetencyID { get; set; }
public int? CompetencyOptionID { get; set; }
public string ActionPlan { get; set; }
public IDictionary<int, string> CompetencyOptions { get; set; }
}
The main view of the career planning form: /Views/CPF/CareerPlanningForm.cshtml
#model MyNamespace.Models.CareerPlanningForm.CareerPlanningFormViewModel
<link rel="stylesheet" href="#Url.Content("~/Content/CreateCPF.css")" />
#using (Html.BeginForm())
{
// other sections loaded here...
// code removed for brevity...
#Html.Partial("SelectCompetencies", Model.SelectCompetencies)
// other sections loaded here...
// code removed for brevity...
}
The SelectCompetencies partial: /Views/CPF/SelectCompetencies.cshtml
The user will fill in the new action plan text and click the add competency button.
That will post via ajax to CPFController/NewCompetencyTemplate
#model MyNamespace.Models.CareerPlanningForm.SelectCompetenciesModel
#Html.HiddenFor(m => m.CareerPlanningFormID)
<h3>Select Competencies</h3>
<p class="guidance">
Select up to #Model.MaximumCompetenciesAllowed competencies to focus on improving.
</p>
<table id="CompetenciesTable">
<thead>
<tr>
<th>Competency</th>
<th>Action Plan:</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.Competencies.Count(); i++)
{
#Html.EditorFor(m => m.Competencies[i])
}
</tbody>
<tfoot id="CompetenciesTableFooter" class="#(Model.Competencies.Count() < Model.MaximumCompetenciesAllowed ? "" : "hidden")">
<tr>
<td colspan="2">
#Html.TextArea("NewActionPlanText")
#Html.Button(ButtonType.Button, "Add Another Competency", "add", new { id = "AddCompetencyButton" })
</td>
</tr>
</tfoot>
</table>
#section script
{
<script>
jQuery(document).ready(function ($) {
var competenciesTableBody = $('#CompetenciesTable tbody'),
competenciesTableFooter = $('#CompetenciesTableFooter'),
addCompetencyButton = $('#AddCompetencyButton'),
newCompetencyTemplateUrl = '#Url.Content("~/CPF/NewCompetencyTemplate")',
count = competenciesTableBody.find('tr').length,
newActionPlanText = $('#NewActionPlanText'),
careerPlanningFormID = $('#CareerPlanningFormID');
addCompetencyButton.click(function () {
$.ajax({
url: newCompetencyTemplateUrl(),
type: 'POST',
data: {
careerPlanningFormID: careerPlanningFormID,
actionPlan: newActionPlanText,
itemCount: count
},
dataType: 'html',
success: function (data) {
var elements = $(data);
// other code removed here...
competenciesTableBody.append(elements);
// other code removed here...
}
});
});
});
</script>
}
Views/CPF/EditorTemplates/CompetencyModel.cshtml
#model MyNamespace.Models.CareerPlanningForm.CompetencyModel
<tr class="competency">
<td>
#Html.DropDownListFor(m => m.CompetencyOptionID, new SelectList(Model.CompetencyOptions, "Key", "Value"), "Select competency...")
</td>
<td>
#Html.TextAreaFor(m => m.ActionPlan, new { #class = "competencyActionPlan" })
#Html.HiddenFor(m => m.CompetencyID)
</td>
</tr>
The controller containing the action to add the new competency: /Controllers/CPFController.cs
This will call the CareerPlanningFormService to add the new competency and will return a partial view for NewCompetencyTemplate that will render out the new competency
public class CPFController : Controller
{
private readonly ICareerPlanningFormService careerPlanningFormService;
public CPFController(ICareerPlanningFormService careerPlanningFormService)
{
this.careerPlanningFormService = careerPlanningFormService;
}
[HttpPost]
public PartialViewResult NewCompetencyTemplate(int careerPlanningFormID, int itemCount, string newActionPlanText)
{
var count = itemCount + 1;
// Even though we're only rendering a single item template, we use a list
// to trick MVC into generating fields with correctly indexed name attributes
// i.e. Competencies[1].ActionPlan
var model = new SelectCompetenciesModel
{
Competencies = Enumerable.Repeat<CompetencyModel>(null, count).ToList()
};
model.Competencies[count - 1] = this.careerPlanningFormService.BuildNewCompetencyModel(careerPlanningFormID, newActionPlanText);
return this.PartialView(model);
}
}
My service class: CareerPlanningFormService.cs
This handles the business logic and makes the calls to the repository to add the item to the database and returns a new CompetencyModel
public class CareerPlanningFormService : ICareerPlanningFormService
{
private readonly IMyRenamedRepository repository;
private readonly IPrincipal currentUser;
public CareerPlanningFormService(
IMyRenamedRepository repository,
IPrincipal currentUser)
{
this.repository = repository;
this.currentUser = currentUser;
}
public CompetencyModel BuildNewCompetencyModel(int careerPlanningFormID, string newActionPlanText)
{
var competency = new Competency
{
CareerPlanningFormID = careerPlanningFormID,
CompetencyOptionID = null,
ActionPlan = newActionPlanText
};
this.repository.Add(competency);
this.repository.Commit();
return new CompetencyModel
{
CompetencyID = competency.CompetencyID,
CompetencyOptionID = competency.CompetencyOptionID,
ActionPlan = competency.ActionPlan,
CompetencyOptions = this.GetCompetencyOptionsForCareerPlanningFormID(careerPlanningFormID)
};
}
}
Now, the partial for NewCompetencyTemplate: Views/CPF/NewCompetencyTemplate.cshtml
This is very simple, it simply renders the same editor template as above, for the last competency in the collection (which we just added)
#model MyNamespace.Models.CareerPlanningForm.SelectCompetenciesViewModel
#Html.EditorFor(m => m.Competencies[Model.Competencies.Count() - 1])
When the ajax call succeeds, it will receive this partial back from the controller action method it called. It then takes the partial and appends it to the competencies table body
// snippet from ajax call above
competenciesTableBody.append(elements);
I hope this helps. Let me know if you have any additional questions.
While you're correct that you can do it just by returning a partial view containing the updated content, you may also consider using jQuery's load method.
Look here, in particular at the "loading page fragments" section. Basically you can just get the original page again and jQuery will "extract" the content you want as long as it can be targetted by a selector (such as a div id).
Note, this solution is not suitable in all cases as there will be redundant markup in the response from the server because you will be discarding the rest of the page content and just using the updated part.

Sending new order back to MVC controller

using the JQuery sortable, and trying to send the new order back to my controller, but not having a whole lot of luck. My view is:
using (Ajax.BeginForm("EditTickerOrder", new AjaxOptions { InsertionMode = InsertionMode.Replace, HttpMethod = "POST", }))
{
<div id="editableticker">
#Html.HiddenFor(m => m.ProjectGUID)
<ul id="sortablediv">
#foreach (DGI.CoBRA.Tools.BussinessObjects.CollabLibrary.TickerObjects.Ticker t in Model)
{
<li class="ui-state-default" id="#t.pKeyGuid.ToString()">
<p>#Html.CheckBox(t.pKeyGuid.ToString(), t.Display, new { #class = "activechk" })
<span style="font-weight: bold">
#t.Text
</span>
</p>
</li>
}
</ul>
<input type="submit" value="Save New Ticker Order" />
}
and my controller is:
[HttpPost]
public ActionResult EditTickerOrder(Guid ProjectGUID, List<string> items)
{
TickerCollectionModel TickerData = new TickerCollectionModel();
TickerData.ProjectGUID = ProjectGUID;
TickerData.ListAllBySession(ProjectGUID);
return PartialView("TickerList", TickerData);
}
yet the list<string> items is always null. Any ideas?
You are writing foreach loops, most definitely violating the naming conventions for your form input fields that the default model binder expects for working with collections. If you don't respect the established wire format, you cannot expect the default model binder to be able to rehydrate your models in the POST action.
In fact, why don't you use view models and editor templates? They make everything trivial in ASP.NET MVC.
So let's define a view model that will reflect your view requirements (or at least those shown in your question => you could of course enrich it with additional properties that you want to handle):
public class TickerViewModel
{
public Guid Id { get; set; }
public bool IsDisplay { get; set; }
public string Text { get; set; }
}
public class ProjectViewModel
{
public Guid ProjectGUID { get; set; }
public IEnumerable<TickerViewModel> Tickers { get; set; }
}
and then a controller whose responsibility is to query your DAL layer, retrieve a domain model, map the domain model into the view model we defined for this view and pass the view model to the view. Inversely, the POST action receives a view model from the view, maps the view model back into some domain model, passes the domain model to your DAL layer for processing and renders some view or redirects to a success action:
public class HomeController : Controller
{
public ActionResult Index()
{
// TODO: those values come from a data layer of course
var model = new ProjectViewModel
{
ProjectGUID = Guid.NewGuid(),
Tickers = new[]
{
new TickerViewModel { Id = Guid.NewGuid(), Text = "ticker 1" },
new TickerViewModel { Id = Guid.NewGuid(), Text = "ticker 2" },
new TickerViewModel { Id = Guid.NewGuid(), Text = "ticker 3" },
}
};
return View(model);
}
[HttpPost]
public ActionResult Index(ProjectViewModel model)
{
// Everything will be correctly bound here => map the view model
// back into your domain model and pass the domain model to
// your DAL layer for processing ...
return Content("Thanks for submitting");
}
}
a view (it is worth noting that in this example I have used a standard form instead of AJAX but it is trivial to convert it into an AJAX form):
#model ProjectViewModel
#using (Html.BeginForm())
{
#Html.HiddenFor(m => m.ProjectGUID)
<div id="editableticker">
<ul id="sortablediv">
#Html.EditorFor(x => x.Tickers)
</ul>
</div>
<button type="submit">OK</button>
}
and finally the corresponding editor template which will automatically be rendered for each element of the Tickers collection (~/Views/Home/EditorTemplates/TickerViewModel.cshtml):
#model TickerViewModel
<li class="ui-state-default">
<p>
#Html.CheckBoxFor(x => x.IsDisplay, new { #class = "activechk" })
#Html.LabelFor(x => x.IsDisplay, Model.Text)
#Html.HiddenFor(x => x.Text)
#Html.HiddenFor(x => x.Id)
</p>
</li>

Model State - What you see is not what you get - revisited

There are already several good discussions about how model properties set in the form post controller won't show up in view because of model state. To take the discussion a step further I have an example where items in the VIEW are updated but the model is not, again due to Model state. That's right, what your user sees will NOT match the model properties that are returned in the post!
say you have a model with a list
public class FruitBasket {
public string OwnersName { get; set; }
public IEnumerable<Fruit> Fruits { get; set; }
public string FruitColor { get; set; }
public FruitBasket() {
this.OwnersName = "Bob";
this.FruitColor = "Red";
}
}
public class Fruit {
public string Name { get; set; }
public string Color { get; set; }
}
And a data access class has methods that retrieves a list of fruits based on color
Data Access code
public IEnumerable<Fruit> GetFruits(string color) {
if (color == "Red") {
List<Fruit> fruits = new List<Fruit> {
new Fruit { Name = "Apple", Color = "Red" },
new Fruit { Name = "Grape", Color = "Red" }
};
return fruits;
} else {
List<Fruit> fruits = new List<Fruit> {
new Fruit { Name = "Banana", Color = "Yellow"},
new Fruit { Name = "Apple", Color = "Red" },
new Fruit { Name = "Grape", Color = "Red" }
};
return fruits;
}
}
Controller (where db is the data access class)
public ActionResult FruitBasket() {
FruitBasket basket = new FruitBasket();
basket.Fruits = db.GetFruits("Red");
return View(basket);
}
[HttpPost]
public ActionResult FruitBasket(FruitBasket basket) {
basket.Fruits = db.GetFruits("All");
return View(basket);
}
View
#model GEWeb.Models.FruitBasket
<!DOCTYPE html>
<html>
<head>
<title>FruitBasket</title>
</head>
<body>
#using (Html.BeginForm()) {
<p>#Html.DisplayFor(model => Model.OwnersName)</p>
<table>
#Html.EditorFor(model => model.Fruits)
</table>
<input type="submit" value="filter" />
}
</body>
</html>
Editor Template
(the template for editorfor is in views/shared/EditorTemplates)
the hiddenfor helpers are included so that the model will be populated with list data on post back
#model GEWeb.Models.Fruit
<tr>
<td>
#Html.DisplayFor(model => model.Name)
#Html.HiddenFor(model => model.Name)
</td>
<td>
#Html.DisplayFor(model => model.Color)
#Html.HiddenFor(model => model.Color)
</td>
<td>
#Html.CheckBoxFor(model => model.Remove)
</td>
</tr>
Scenario
- The user requests the page and sees the list of red fruits.
- The user clicks the filter button, the post controller loads all fruits, the user sees a list of all three fruits
- The user clicks the filter button a second time. Now inspect the basket properties as passed to the post controller, the list will not match what the user saw.
Now imagine that you wanted to use that checkbox to select fruits to remove from the basket. You could pass the basket.fruits model to a method in your data access class which would delete the fruits and then you could have your conroller reload the basket.fruits list. But it will not work because the list returned to the controller does not match what the user is seeing.
This "feature" can be subverted by putting the following into the Post controller
ModelState.Clear();
But realy, I shouldn't have to. Views and controllers should pass the model back and forth accurately. ModelState is an attempt to persist View properties between page loads and that doesn't seem to be in keeping with the MVC phylosophy where state is managed by the database, and not the view!

Problem with Edit View using ViewModel

I have a complex object that I want to use in an edit view. To simplify things I have created a ViewModel and have successfully created the edit view page, and everything renders correctly. When I hit save, everything falls apart.
The ViewModel is as follows:
public class ClosureEditViewModel
{
public Model.Closure Closure { get; set; }
public Model.School School { get; set; }
public Model.ClosureDetail CurrentDetails { get; set; }
}
Some of the View is as follows:
<div class="display-label">School</div>
<div class="display-field">
#Html.DisplayFor(model => model.Closure.School.Name)
</div>
<div class="display-label">Closed</div>
<div class="display-field">
#Html.DisplayFor(model => model.Closure.Logged)
</div>
....
<div class="editor-label">
#Html.LabelFor(model => model.CurrentDetails.DateOpening, "Date Opening (dd/mm/yyyy)")
</div>
<div class="editor-field">
#Html.TextBox("DateOpening", Model.CurrentDetails.DateOpening.ToString("dd/MM/yyyy"))
#Html.ValidationMessageFor(model => model.CurrentDetails.DateOpening)
</div>
....
<tr>
<td>
#Html.CheckBoxFor(model => model.CurrentDetails.Nursery, (Model.School.Nursery ? null : new { #disabled = "disabled" }))
</td>
The important parts of the controller are as follows:
public ActionResult Edit(int id)
{
Data.IClosureReasonRepository reasonRepository = new Data.SqlServer.Repositories.ClosureReasonRepository(UnitOfWork);
IEnumerable<Model.ClosureReason> reasons = reasonRepository.GetAll();
Model.Closure closure = ClosureRepository.GetClosure(id);
Model.ClosureDetail currentDetail = closure.ClosureDetails.Last();
ViewModels.ClosureEditViewModel editClosure = new ViewModels.ClosureEditViewModel() { Closure = closure, School = closure.School, CurrentDetails = closure.ClosureDetails.Last() };
ViewBag.ReasonId = new SelectList(reasons, "Id", "Name", currentDetail.ReasonId);
return View(editClosure);
}
[HttpPost]
public ActionResult Edit(ViewModels.ClosureEditViewModel newDetail)
{
//if (ModelState.IsValid)
//{
//}
Data.IClosureReasonRepository reasonRepository = new Data.SqlServer.Repositories.ClosureReasonRepository(UnitOfWork);
IEnumerable<Model.ClosureReason> reasons = reasonRepository.GetAll();
ViewBag.ReasonId = new SelectList(reasons, "Id", "Name", newDetail.CurrentDetails.ReasonId);
return View(newDetail);
}
When I hit save the following message appears:
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error:
Line 94: </td>
Line 95: <td>
Line 96: #Html.CheckBoxFor(model => model.CurrentDetails.P1, (Model.School.P1 ? null : new { #disabled = "disabled" }))
Line 97: </td>
Line 98: <td>
I just can't figure out why it is having problems with the School property but neither of the other two.
James :-)
It seems that Model.School is null when you render the view once again in the POST action. Make sure that it isn't null because in your view you don't have a single input field bound to the School property => this property will be null inside your POST controller action.
[HttpPost]
public ActionResult Edit(ClosureEditViewModel viewModel)
{
... some operations
// Make sure that viewModel.School is not null
// Remember that the checkbox is bound to CurrentDetails.P1 so
// when you post to this action there is nothing that will initialize
// the School property => you should do whatever you did in your GET
// action in order to initialize this property before returning the view
return View(viewModel);
}

Resources