MVC3 Razor ViewBag.Model not making into the Page - asp.net-mvc

I have modified a Model recently for a system I've inherited, and for some reason the ViewBag.Model is not making into the page. I've reverted the code back to before I started tinkering, and still no luck.
The call into the view is as follows:
public virtual ActionResult Edit(long id)
{
var _news = _newsRepository.GetNewsById(id);
ViewBag.Model = AutoMapper.Mapper.Map<News, NewsModel>(_news);
ViewBag.Model.CurrentNewsImageFile = ConfigSettings.HostDomainName + ConfigSettings.NewsImageBasePath + _news.image_file;
return View();
}
The view has the following code:
#model MyModels.Models.NewsModel
#{
bool IsCreate = Model == null || Model.Id == 0;
ViewBag.Title = IsCreate ? "Add News" : "Edit News";
}
The problem is that "Model" is always null in the view code... what have I missed? Am I missing a fundamental here?
When tracing through the ActionResult code, right up until the return View() the debug inspector correctly shows the Model containing everything I would expect it too.

You need to simply return View(theModel). If you don't pass the model into View() it doesn't know what to do.
However, I'm confused about the code you have listed, you seem to be trying to put the model into the ViewBag? The ViewBag shouldn't contain the model.
I think the code you want is:
public virtual ActionResult Edit(long id)
{
var _news = _newsRepository.GetNewsById(id);
var model = AutoMapper.Mapper.Map<News, NewsModel>(_news);
model.CurrentNewsImageFile = ConfigSettings.HostDomainName + ConfigSettings.NewsImageBasePath + _news.image_file;
return View(model);
}

I think it is the AutoMapper line. Once you have created your mappings I believe you need to them call them using the following syntax:
var newsModel = Mapper.Map(_news);

Related

View/Model data isn't refreshing/changing after post/postback, even though I'm using the PRG pattern

Update I have saved my problem a long time ago. The problem was that I was trying to call the view model on the wrong view method! I was calling the base view method (Document), instead of one of it's derived method (like NewDocument, PDFDocument, etc.) Thus it was only giving me the Documents data, which didn't change. I was looking and using the wrong view method all the time... Stephen, when you asked me
"Why do you create derived classes in a method but then return only the base class"
I couldn't answer the question at the time because I didn't even know myself, until I remember that originally, the method wasn't returning the base class. I only changed it so that it can work with the base view method, which was wrong in the first place!
That's what I get for only getting 3-4 hours of sleep in 3 days. Everything works right now. Thanks.
I'm having a hard time trying to figure out why the data in my view isn't changing after I do a post. Originally I was doing it via return View() and it worked, but since it was a partial view, the page didn't look great, so I was reading up and saw that it was better to do it by Post-Redirect-Get pattern (PRG) and to use an id value to retrieve the values instead of sending the entire model via Tempdata. I even used ModelState.Clear() and that didn't even work. When I debugged the code, the model only has the values from when I first called it.
Here's part of my Get controller:
NewDocument Get Controller
[DocumentAuthenticationFilter]
public ActionResult NewDocument(int? id = null)
{
// This doesn't work. The view keeps on showing the data from View(Services.CreateNewDocument()).
if (id != null)
{
return View(Services.GetdocumentViewModelData(DocEnum.Section.NEW_DOC_INDEX, (int)id));
}
// This works fine
return View(Services.CreateNewDocument());
}
And here's the post that calls the redirect:
NewDocument Post controller
[HttpPost]
[ValidateAntiForgeryToken]
[MultipleButton(Name = "action", Argument = "AddDocuments")]
//[OutputCache(Duration = 30, VaryByParam = "*")]
public ActionResult AddDocumentViewModel(FormCollection frm, DocumentViewModel dvm)
{
try
{
if (ModelState.IsValid)
{
int? DocID = Services.AddingNewDocument(dvm);
// See, I even tried to clear it.
ModelState.Clear();
return base.RedirectToAction("NewDocument", new { id = DocID });
}
else
{
// Display errors in the modal
}
return base.RedirectToAction("NewDocument");
}
And here's the old way I did it:
NewDocument Post controller
[HttpPost]
[ValidateAntiForgeryToken]
[MultipleButton(Name = "action", Argument = "AddDocuments")]
//[OutputCache(Duration = 30, VaryByParam = "*")]
public ActionResult AddDocumentViewModel(FormCollection frm, DocumentViewModel dvm)
{
try
{
if (ModelState.IsValid)
{
Services.AddingNewDocument(ref dvm);
dvm.NewRecordMode = DocEnum.Action.UPDATE;
// It worked, but only the partial view showed, and not the entire view.
return PartialView("_NewDocument", dvm);
}
else
{
// Display errors in the model
}
return base.RedirectToAction("NewDocument");
}
Could it be because I'm using a custom model binding?
My Custom Model Binding
public class BaseClassModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var modelType = bindingContext.ModelType;
var modelTypeValue = controllerContext.Controller.ValueProvider.GetValue("ViewModel");
if (modelTypeValue == null)
throw new Exception("View does not contain the needed derived model type name");
var modelTypeName = modelTypeValue.AttemptedValue;
var type = modelType.Assembly.GetTypes().SingleOrDefault(x => x.IsSubclassOf(modelType) && x.Name == modelTypeName);
if (type == null)
{
throw new Exception(String.Format("Derived model type {0} not found", modelTypeName));
}
var instance = bindingContext.Model ?? base.CreateModel(controllerContext, bindingContext, type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, type);
return base.BindModel(controllerContext, bindingContext);
}
}
EDIT: And here's the GetDocumentViewModelData code:
GetDocumentFromViewModelData
public static DocumentViewModel GetDocumentViewModelData(DocEnum.Section docType, int id)
{
switch (docType)
{
case DocEnum.Section.NEW_DOCUMENT_INDEX:
// NewDocumentTypeViewModel is a child to DocumentTypeViewModel
DocumentTypeViewModel nd = NewDocumentService.GetViewModelByID(id);
return nd;
case DocEnum.Section.PDF_DOCUMENT:
DocumentTypeViewModel pdfvm = PDFDocumentService.GetViewModelByID(id);
return pdfvm;
case DocEnum.Section.XLS_DOCUMENT:
DocumentTypeViewModel xlsvm = XLSDocumentService.GetViewModelByID(id);
return xlsvm;
}
return null;
}
Edit: Also adding the GetViewModelByID function
GetViewModelByID
public static DocumentTypeViewModel GetViewModelByID(int id)
{
docEntities db = new docEntities();
NewDocumentTypeViewModel vm = new NewDocumentTypeViewModel();
// Calls a stored procedure called Select_Documents_ByID(id) to get the note entry
// that was submitted.
List<Select_Documents_ByID_Result> prevNotes = db.Select_Documents_ByID(id).ToList();
StringBuilder sNotes = new StringBuilder();
foreach (var note in prevNotes)
{
sNotes.AppendFormat("{0} - {1}: {2}\n\n", note.CreatedDate.ToString("yyyy-MM-dd HH:mm"), note.username, note.Entry);
}
vm.PreviousNotes = sNotes.ToString();
return vm;
}
Edit: I did a direct creation of the view model inside the Get controller, and it's the same result. when i debugged the view itself, the values from the new view model don't show up. Instead, the values from the initial view model, View(Services.CreateNewDocument()), shows.
[DocumentAuthenticationFilter]
public ActionResult NewDocument(int? id = null)
{
// Right here I created the view model to test thing, but I'm getting the same results. Nothing has changed.
if (id != null)
{
var d = new NewDocumentTypeViewModel(1, "Help!");
// This property is from the base class, DocumentTypeViewModel
d.DocumentTitle = "Testing!";
return View(d);
// Inside the view itself, none of the values in the view model, including the one
// belonging to the base class. It still shows the initial values.
}
// This works fine
// Or maybe not...
return View(Services.CreateNewDocument());
}
Edit: I wanted to see if it was also doing the same thing for the initial call to the view return View(Services.CreateNewDocument()), and decided to change the value for documentTitle in the base class from New Document to a randomly-generated number, after the object has been created.
Here's the code for DocumentTypeViewModel's default constructor:
public DocumentTypeViewModel()
{
DocumentTitle = "New Document";
NewRecordMode = DocEnum.Action.ADD;
DocumentID = 0;
}
And here's the Services.CreateNewDocument() code where I change the DocumentTitle after the View Model has been created.
public DocumentTypeViewModel CreateNewDocument()
{
DocumentTypeViewModel dtvm = new DocumentTypeViewModel();
Random r = new Random();
dtvm.DocumentTitle = r.Next(5, Int32.MaxValue).ToString();
return dtvm;
}
Now in the View, when I call DocumentTitle:
<div class="label-text-group">
#Html.LabelFor(model => model.DocumentTitle)
#Html.EditorFor(model => model.DocumentTitle)
</div>
You would expect to see a randomly-generated number every time the View gets called. Nope, what you would see is "New Document". Weird.
It's seems that Services.GetDocumentViewModelData() is not exactly working correctly. It only carries the values created by the base class' constructor when a view is created, not any values that have been added or changed within GetDocumentViewModelData() itself. Why is that? What's going on? Please help anybody!
I have solved it. Look at the Update section on top. Thanks Stephen.

passing value in partial view viewdatadictionary

#Html.Partial("~/Areas/WO/Views/PartialContent/_FirstPage.cshtml", new ViewDataDictionary { { "WOID", WOID } })
In my Page i am accessing Partial view in the above way.
I need to pass WOID(view data dictionary) value from query string, For that i am using following Code
#{
var desc = Html.ViewContext.HttpContext.Request.QueryString.Get("ID");
Uri referrer = HttpContext.Current.Request.UrlReferrer;
string[] query = referrer.Query.Split('=');
int WOID = Convert.ToInt32(query[1]);
}
But the issue is this code is working in all browsers except I.E. i Need to Solve this problem.
Please help me
Instead of this you can have this value as part of you model and use that.That is the standard and recommeded way .
In your action method you can have these as parameter.Your query string value will get bind to this parameter
public ActionResult ActionMethod(int ID)
{
Model.WOID = WOID;
// Other logic
return View(Model)
}
Next step you can add this as a property to your view model or add it to ViewData dictionary and then access it in your partial view.

Controller searching for view instead of returning a different view method

I have two controller actions outlined below:
public ViewResult TitleStorylines(int id)
{
var vm = Service.Get(id);
vm.IsEditable = User.HasPermission(SecurityPermissionType.ManageStorylines);
return View(vm);
}
public ViewResult TitleStorylinesCreate(TitleStorylineModel model)
{
var created = Service.Create(model);
return TitleStorylines(created.TitleId);
}
I only have one view in my project, called TitleStorylines, which the first controller action handles fine. But when I call the second method, it gives me an error saying that it can't find the view called TitleStorylinesCreate even though I'm explicitly calling the previous method. What gives?
Did you try ?
return View("TitleStorylines",created.TitleId);
EDIT: Based on your update : I guess you are posting your form back the TitleStorylinesCreate. So probably after saving, dont you want to redirect the user back to the Get action of same ?
[HttpPost]
public ViewResult TitleStorylinesCreate(TitleStorylineModel model)
{
var created = Service.Create(model);
return RedirectToAction("TitleStorylines",new { #id=created.TitleId});
}
In the above example we are doing the PRG pattern. Post -> Redirect -> Get
After saving, we are redirecting them back to the first method. It will be a HTTP GET method.
public ActionResult TitleStorylinesCreate(TitleStorylineModel model)
{
var created = Service.Create(model);
return RedirectToAction("TitleStorylines",new { #id=created.TitleId});
}

There is no ViewData item with the key

I have a problem with posting a multiselect, I get error:
"There is no ViewData item with the key 'NotificationUsergroups' of type 'IEnumerable'."
In the controller I have:
MultiSelectList NotificationUsergroups = new MultiSelectList(Usergroups, "UsergroupID", "UsergroupName", selectedNotificationUsergroupIDs);
ViewData["NotificationUsergroups"] = NotificationUsergroups;
In the view I have:
<%= Html.ListBox("NotificationUsergroups")%>
And in the post action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ObjectEdit([BindAttribute(Include = "BookingObjectID,BookingObjectName,Activated,ActivationStartDate,ActivationEndDate,AvalibleObjects,AvalibleObjectsPerBooking")]BookingObject bookingobject, int[] Objectcategories, int[] NotificationUsergroups, int[] CancellationUsergroups)
{
if (ModelState.IsValid)
{
try
{
_bs.SaveBookingObject(bookingobject);
if (NotificationUsergroups != null)
_bs.SaveNotificationUsergroups(bookingobject.BookingObjectID, NotificationUsergroups);
return View("CreateObject", new BookingObjectsAdminEditViewModel { BookingObject = bookingobject });
}
catch {
ModelState.AddModelError("SomeError", "errrrrrrrrror");
}
}
What might be wrong? I've checked the spelling and all, works if I dont run with the multiselect listing.
What makes the data "disappear"?
Thanks in advance
/M
You will need to set the viewdata in the POST accepting method as well because if it has an error it will return to the original view.
Kindness,
Dan
Or include them in your model :) You would need to alter view tho.
Your action must setup your ViewData.
If your action calls
ViewData["NotificationUsergroups"] = NotificationUsergroups;
everything should be alright.

How to get the Model from an ActionResult?

I'm writing a unit test and I call an action method like this
var result = controller.Action(123);
result is ActionResult and I need to get the model somehow, anybody knows how to do this?
In my version of ASP.NET MVC there is no Action method on Controller. However, if you meant the View method, here's how you can unit test that the result contains the correct model.
First of all, if you only return ViewResult from a particular Action, declare the method as returning ViewResult instead of ActionResult.
As an example, consider this Index action
public ViewResult Index()
{
return this.View(this.userViewModelService.GetUsers());
}
you can get to the model as easily as this
var result = sut.Index().ViewData.Model;
If your method signature's return type is ActionResult instead of ViewResult, you will need to cast it to ViewResult first.
We placed the following piece in a testsbase.cs allowing for typed models in the tests a la
ActionResult actionResult = ContextGet<ActionResult>();
var model = ModelFromActionResult<SomeViewModelClass>(actionResult);
ModelFromActionResult...
public T ModelFromActionResult<T>(ActionResult actionResult)
{
object model;
if (actionResult.GetType() == typeof(ViewResult))
{
ViewResult viewResult = (ViewResult)actionResult;
model = viewResult.Model;
}
else if (actionResult.GetType() == typeof(PartialViewResult))
{
PartialViewResult partialViewResult = (PartialViewResult)actionResult;
model = partialViewResult.Model;
}
else
{
throw new InvalidOperationException(string.Format("Actionresult of type {0} is not supported by ModelFromResult extractor.", actionResult.GetType()));
}
T typedModel = (T)model;
return typedModel;
}
An example using a Index page and List:
var actionResult = controller.Index();
var model = ModelFromActionResult<List<TheModel>>((ActionResult)actionResult.Result);
consider a = ActionResult;
ViewResult p = (ViewResult)a;
p.ViewData.Model
It's somewhat cheating but a very trivial way to do so in .NET4
dynamic result = controller.Action(123);
result.Model
Used this today in a unit test. Might be worth some sanity checks such as:
Assert.IsType<ViewResult>(result);
Assert.IsType<MyModel>(result.Model);
Assert.Equal(123, result.Model.Id);
You could skip the first one if the result is going to be a view or partial result depending on the input.

Resources