Access model from shared partial view independent of controller - asp.net

I'm creating a simple webpage in asp.net mvc 3.
I have a sidebar that loads random citations, and the sidebar is on every page so it's a part of the layout, independent of the controller.
What is the correct way to access a datamodel from that view? Do I have to pass data from each controller?
My partial view file looks something like:
#model MvcApplication1.Models.CitationModel
#Model.Citation
But this results in a null reference.
The model is something like
public class CitationModel
{
public string Citation{ get { return "Test"; } }
}

I would do this with a child action. This way you can keep the view strongly typed (no viewbag or viewdata), without having to put it in a "master" viewmodel that gets sent to your layout:
<div id="sidebar">
#Html.Action("RandomCitations", "Citations")
</div>
In CitationsController:
[ChildActionOnly]
public PartialViewResult RandomCitations()
{
var model = new CitationModel();
// populate model
return PartialView(model);
}
Your view will stay the same, and will be injected into the sidebar div for every layout.

There are plenty of scenarios there.
For now for that cases I put model to view bag, and then getting it from viewbag on view.

I'd use a base controller class like this:
public class ApplicationController : Controller
{
public ApplicationController()
{
Citation c = getYourCitation();
ViewBag.Citation = c;
}
}
Get all your controllers to inherit from Application controller
public class HomeController : ApplicationController
{
//Controller code
}
Each view (including _Layout) will then be able to access the ViewBag
in _Layout.cshtml do this:
#Html.Partial("_CitationPartial", (Your.MidTier.Models.Citation)ViewBag.Citation)

Related

How to display view model validation for a view that has multiple forms on it?

I'm trying to better understand how to properly structure my ASP.NET MVC code to handle a situation where a single view contains multiple forms. I feel that it makes sense to submit the forms to their own action methods, so that each form can benefit from its own view model parameter binding and validation, and to avoid putting all form parameters into 1 larger, monolithic view model.
I'm trying to code this pattern, but I can't seem to tie the loose ends together.
I've written some example action methods below, along with example view model classes, that I think demonstrate what I'm trying to achieve. Lets say that I've got an Item Detail action method and view. On this Detail view, I've got two forms - one that creates a new Comment and another that creates a new Note. Both Comment and Note forms POST to their own action methods - DetailNewComment and DetailNewNote.
On success, these POST handler action methods work just fine. On an invalid model state though, I return View(model) so that I can display the issues on the original Detail view. This tries to render a view named Brief though, instead of Detail. If I use the overloaded View call that allows me to specify which view to render, then now I have issues with the different view model classes that I'm using. The specific view model classes now no longer work with the original DetailViewModel.
I get the feeling that I'm doing this completely wrong. How am I supposed to be handling this scenario with multiple forms? Thanks!
public ActionResult Detail(int id)
{
var model = new ItemDetailViewModel
{
Item = ItemRepository.Get(id)
};
return View(model);
}
[HttpPost]
public ActionResult DetailNewComment(int id, ItemDetailNewCommentViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var comment = CommentRepository.Insert(new Comment
{
Text = model.Text
});
return RedirecToAction("Detail", new { id = id; });
}
[HttpPost]
public ActionResult DetailNewNote(int id, ItemDetailNewNoteViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var note = NoteRepository.Insert(new Note
{
Text = model.Text
});
return RedirectToAction("Detail", new { id = id; });
}
... with view models something like ...
public class ItemDetailViewModel
{
public Item Item { get; set; }
}
public class ItemDetailNewCommentViewModel
{
public string Text { get; set; }
}
public class ItemDetailNewNoteViewModel
{
public string Text { get; set; }
}
For your case I'd recommend to have a master model for example your
ItemDetailViewModel class to which you'll add a property for each sub-model
public class ItemDetailViewModel
{
public Item Item { get; set; }
public ItemDetailNewCommentViewModel NewCommentModel {get;set;}
public ItemDetailNewNoteViewModel NoteModel {get;set;}
}
Your Detail view will be the master view and the other two will be partial views.
Master view will receive an instance of ItemDetailViewModel as model and inside view you will render your partials by passing Model.NewCommentModel and Model.NoteModel as their corresponding models. For being able to use separate actions for each form, instead of regular forms you can use ajax forms, thus you will send to the server only relevant information without altering the rest of the master view.
The chief problem here is what happens when the user messes up and their post doesn't pass validation server-side. If you choose to take them to a page where just the one form is presented, then you can post to a different action, but if you want both forms re-displayed, then they both should point to the same action.
Really, you just have to make a choice. I've seen sites handle it both ways. Personally, I prefer to re-display the original form, which means handling both forms in the same action. It can lead to bloat, but you can factor out a lot of logic from the action such that you end up with mostly just a branch depending on which form was submitted.

Error when using two models in index page

I generated a simple MVC5 application (code first with scaffold for the views) and it generates for me the views for every action index, create, edit etc
I want to copy the code from the create view to the index view but in the index the model is declared like below :
#model IEnumerable<Ro01.Models.Ro>
and when I copy it to the view and run it I get an error. I can use only one model, how should I overcome this?
since the code of the create (razor code) having errors...
#model Role.Models.Ro
You need to create a ViewModel. Create a ViewModels folder in your project and add a class like this:
public class FooViewModel
{
public IEnumerable<Ro01.Models.Ro> Model1 {get;set;}
public FooClass Model2 {get;set;}
}
Controller Action:
public ActionResult SomeAction()
{
FooViewModel model = new FooModel();
model.Model1 = new Ro01.Models.Ro();
mode.Model2 = new FooClass();
return View(model);
}
and then use it your View like this:
#model Ro01.ViewModels.FooViewModel
#Html.HiddenFor(m=>m.Model1.SomeProperty)
#Html.HiddenFor(m=>m.Model2.SomeProperty)

Pass subclass of generic model to razor view

The Big Picture:
I have found what seems like a limitation of Razor and I am having trouble coming up with a good way around it.
The Players:
Let's say I have a model like this:
public abstract class BaseFooModel<T>
where T : BaseBarType
{
public abstract string Title { get; } // ACCESSED BY VIEW
public abstract Table<T> BuildTable();
protected Table<T> _Table;
public Table<T> Table // ACCESSED BY VIEW
{
get
{
if (_Table == null)
{
_Table = BuildTable();
}
return _Table;
}
}
}
And a subclass like this:
public class MyFooModel : BaseFooModel<MyBarType>
{
// ...
}
public class MyBarType : BaseBarType
{
// ...
}
I want to be able to pass MyFooModel into a razor view that is defined like this:
// FooView.cshtml
#model BaseFooModel<BaseBarType>
But, that doesn't work. I get a run-time error saying that FooView expects BaseFooModel<BaseBarType> but gets MyFooModel. Recall that MyFooModel in herits from BaseFooModel<MyBarType> and MyBarType inherits from BaseBarType.
What I have tried:
I tried this out in non-razor land to see if the same is true, which it is. I had to use a template param in the View to get it to work. Here is that non-razor view:
public class FooView<T>
where T : BaseBarType
{
BaseFooModel<T> Model;
public FooView(BaseFooModel<T> model)
{
Model = model;
}
}
With that structure, the following does work:
new FooView<MyBarType>(new MyFooModel());
My Question:
How can I do that with Razor? How can I pass in a type like I am doing with FooView?
I can't, but is there any way around this? Can I achieve the same architecture somehow?
Let me know if I can provide more info. I'm using .NET 4 and MVC 3.
EDIT:
For now, I am just adding a razor view for each subclass of BaseFooModel<BaseBarType>. I'm not psyched about that because I don't want to have to create a new view every time I add a new model.
The other option is to just take advantage of the fact that I am able to get this working in regular c# classes without razor. I could just have my razor view #inherits the c# view and then call some render method. I dislike that option because I don't like having two ways of rendering html.
Any other ideas? I know its hard to understand the context of the problem when I'm giving class names with Foo and Bar, but I can't provide too much info since it is a bit sensitive. My apologies about that.
What I have so far, using Benjamin's answer:
public interface IFooModel<out T>
where T : BaseBarModel
{
string Title { get; }
Table<T> Table { get; } // this causes an error:
// Invalid variance: The type parameter 'T' must be
// invariantly valid on IFooModel<T>.Table.
// 'T' is covariant.
}
public abstract class BaseFooModel<T> : IFooModel<T>
where T : BaseBarModel
{
// ...
}
What ended up working:
public interface IFooModel<out T>
where T : BaseBarModel
{
string Title { get; }
BaseModule Table { get; } // Table<T> inherits from BaseModule
// And I only need methods from BaseModule
// in my view.
}
public abstract class BaseFooModel<T> : IFooModel<T>
where T : BaseBarModel
{
// ...
}
You need to introduce an interface with a covariant generic type parameter into your class hierarchy:
public interface IFooModel<out T> where T : BaseBarType
{
}
And derive your BaseFooModel from the above interface.
public abstract class BaseFooModel<T> : IFooModel<T> where T : BaseBarType
{
}
In your controller:
[HttpGet]
public ActionResult Index()
{
return View(new MyFooModel());
}
Finally, update your view's model parameter to be:
#model IFooModel<BaseBarType>
Using interfaces-based models was deliberately changed between ASP.NET MVC 2 and MVC 3.
You can see here
MVC Team:
Having interface-based models is not something we encourage (nor, given the limitations imposed by the bug fix, can realistically support). Switching to abstract base classes would fix the issue.
"Scott Hanselman"
The problem you are experiencing is not a Razor error, but a C# error. Try to do that with classes, and you'll get the same error. This is because the model is not BaseFooModel<BaseBarType>, but BaseFooModel<MyFooModel>, and an implicit conversion cannot happen between the two. Normally, in a program you'd have to do a conversion to do that.
However, with .NET 4, introduced was contravariance and covariance, which sounds like the ability of what you are looking for. This is a .NET 4 feature only, and I honestly don't know if Razor in .NET 4 makes use of it or not.

How to share a common object in each page request of a ASP.net MVC 4 webapp?

I come from "regular" asp.net so i'm a bit (totally) lost with MVC.
What I was doing with my own asp.net programmation pattern :
I have one custom class objet which represent the "page" and its properties (like mypage.loadJquery, mypage.isLogged, mypage.Title, custom cache logic, etc.)
This class is instanciate once on top of each ASHX page, I then manipulate a stringbuilder to produce HTML and spit it right at the browser at the end.
Having only one request on the ASHX page, i can use my page object instanciated at the top till the end when calling final response.write
Now i'm trying to go for MVC. I "kind of" understood the M/V/C model and the routing concept. I would like to keep my custom "page" object but I lost my page life cycle and I definitely don't know how to instanciate my page object ONCE in at the top of every call.
I need this instanciated ONCE shared object across every models, controllers, views, partial views, htmlhelper...
I realize MVC pattern might be confusing for me at this moment, bu how could I try to reproduce my need ?
(Very concrete exemple : On every request i need to check if the user is logged via his cookies. If it is I round trip the database to get user infos. Then I DO NEED THESE INFOS ON PRATICALLY EVERY model / controller / view of the app, but of course don't want to round back each time to security check and database querying, how can i have these info on the whole mvc cyle ?)
In my project I create interface IViewModel that contains all fields that I need in my layout/masterpage and set is as model of it so I can easily use them:
IViewModel.cs
public interface IViewModel
{
string Title { get; set; }
User User { get; set; }
}
Layout.cshtml
#model IViewModel
<html>
<head>
<title>#Model.Title</title>
</head>
<body>
#if (Model.User.IsAuthenticated) {
You are logged as #Model.User.Name
}
</body>
</html>
All my models implement that interface (or inherit from ViewModelBase that is default implementation of that class). Additionally I have custom action filter that check if returned ActionResult is (Partial)ViewResult and if Model of it implement my IViewModel interface and fill data in that interface.
public FillViewModelAttribute : ActionFilterAttribute
{
public override OnActionExecuted(ActionExecutedContext context)
{
var viewResult = context.Result as ViewResult;
if (viewResult != null && viewResult.Model is IViewModel)
{
var viewModel = (IViewModel)viewResult.Model;
// fill data
}
}
}
I created many projects like this. Basically, you can create a base controller class where all the other controllers inherit from it.
[Authorize]
public class BaseController : Controller
{
private Instructor _G_USER = null;
protected Instructor G_USER
{
get
{
if (_G_USER == null)
{
_G_USER = Your DB query here
ViewData["G_USER"] = _G_USER;
}
return _G_USER;
}
}
}
Then in your every child class, you can do
[Authorize]
public class YourController : BaseController
{
public ActionResult Index()
{
if(!G_USER.CAN_DO_THIS) throw new NoPermissionException();
return View();
}
}
To use the User in the view, create an extension method.
public static class ExtentionMethods
{
public static USER G_USER(this ViewPage page)
{
return (USER)page.ViewData["G_USER"];
}
}
Then use in the page like this
<%=this.G_USER().....%>

Fill ViewData on Every Action but not on renderaction

I have a viewData that is required to be available on each page. If I fill this ViewData using onActionExecuting. It gets filled for every action even for actionresult of partial pages. I want ViewData to be get filled only once for each Page Load.
Any suggestion
You could use the OnActionExecuting event and test if the result returned b the view is a normal view or a partial view and based on that decide whether or not to add the information to ViewData:
public class MyActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Result is ViewResult)
{
filterContext.Controller.ViewData["foo"] = "bar";
}
}
}
Another possibility to have some common data available to all views is to externalize it in a separate child action and use the Html.Action helper to include it in your layout for example.

Resources