I've become a little lost trying to figure out the correct way to share some data between some partials. I'll do my best to explain the setup:
Layouts
_Layout.cshtml - this is the main layout for the site: top navigation, footer etc.
_SectionLayout.cshtml - this is a sub-layout of _Layout.cshtml and makes the main content area of the site into a header and two lower columns - the left column is a menu, the right loads various page content
Partials
_SectionMenu.cshtml - this is the mark-up for the left hand column menu, it builds the menu based on an ViewModel that it expects to be passed which contains an enumerable list of menu items
_SectionHeader.cshtml - this is the mark-up for the section header, it builds the header similarly to the menu above
All of the above are located in the Shared folder.
A series of controllers use the _SectionLayout style to have a consistent style for areas of the site with related pages. The layout is specified on these controllers via a _ViewStart.cshtml within the Views folder for each controller. Each controller implements an ActionResult method for both _SectionMenu() and _SectionHeader(), returning appropriately populated ViewModels for the section.
The individual pages that the menu loads have been setup to do something along the lines of the following:
if (Request.IsAjaxRequest())
return PartialView();
else
return View();
So that they can be loaded either directly via a standard GET request (which loads the header, menu and layout), or as a partial to be inserted into the right hand column of the layout. Now this is all seems to work well, until I come to deciding how to load data that is shared between actions and partials within the same controller while trying to adhere to DRY where I can and cut down on code re-use. The problem is as follows:
Within a typical controller of this type, a single full load of the page will invoke three ActionResult methods: _SectionHeader(), _SectionMenu() and whatever the main action was. Often, all three of them need to access data from the same, or similar database queries. For example, a ProductController may have an Index action which shows an overview of the product information. The header will need to show the name of the product, the menu will show statistics such as the number of images. All of this data comes from a single query which returns all of the product information from the database, but how am I best able to share this data between the three actions? Some ideas I've had:
Make all the ViewModels inherit from a base view model for that controller and pass it through to the partials after opening it in the main action
Store the data in the ViewBag or ViewData
Just open the database multiple times and hope that my ORM is clever enough to realise it doesn't need to fetch it from the server multiple times within the same request
The first one seems to me to be rather complicated for what is a fairly simple ask. The second always feels like a cheap easy way out and I try to avoid it. The third, I'm not confident enough that what I'm using (Dapper) will know not to go to the server again for the same query.
To make things just a little more complex, some of the Actions for a controller may not need to load the main database query. To extend the example from above, the Images option in the menu of the Product controller may need to run an additional query to load the list of associated images, but if it is being requested as an ajax call, it really doesn't need to load the main query for the header and menu sections.
I'm comfortable with the logic for that, but does every one of the actions within a controller of this type need to look something like the following, or can DRY be leveraged here?
public ActionResult Images(int? id)
{
if (!CheckExists(id))
return HttpNotFound();
// if this is an action that doesn't normally need the shared data,
// only load it if it isn't an ajax request, as then the menu/header will require it
if (!Request.IsAjaxRequest())
LoadSharedData(); // part of the question is how I handle this part
// load the particular model for this page
var model = GetImagesModel();
if (Request.IsAjaxRequest())
return PartialView(model);
else
return View(model);
}
In a section with say 10 similar actions in it, seeing all that extremely similar code repeated tells me that I'm doing something wrong. Thanks in advance for any help. If I haven't explained something well, please ask and I'll do my best to clarify.
Related
Hi I have spent hours trying to work out how to do this one.
So lets say I have two models:
model 1 - AccountInformation
model 2 - AccountHoldersImages
One account holder can have many images. I am trying to achieve on the accountInformation create view (using Scaffolding) to include the facility to add the images at the same time of creation of the user account. Is this possible?
One account holder can have many images
In which case, AccountHoldersImages must be a part of AccountInformation.
If in different views you are using AccountHoldersImages separately, then may be you can keep the view models separate.
However, for this particular view that you are talking about, you will have to wrap the view models into a single view model as MelancialUK commented above.
Another way is to pass the models as part of ViewData[]. If it is a form, then on post you can use FormCollection parameter of your [HttpPost] action to get your images.
Assuming a web page, where we want to display a list of movies, register movies and edit those movies.
You could create a controller, and within the controller treat the three views (Index, Create, Edit).
Or you could create two controllers, one for treating the Index and the other to address the Create and Edit. (Since Creating and Editing Model or ViewModel share)
Or you could create three controllers, one for each view.
It can be done in three ways.
But which one is right and why?
you'd want to create one controller for this, MoviesController, for example. The operations within the controller will then make sense since the URL will look like this...
Movies/Create
Movies/Edit
Movies //this will just list the movies.
The controller could then be passed a Movies repository for manipulating or listing the data.
Obviously, you can create three controllers, but this would be overkill and cause maintenance headache.
Because your business model is one , i think you have to use one controller and your required actions and views.
Controllers should be grouped logically around an object\functionality. If all of these three actions are related to Movies, then it should be MovieController with three actions\views.
Making new controller for each action will make your code a bit of mess.
You should create a controller with three actions and views (Index, Create, Edit).
I am working on ASP.NET MVC 4 Project of our Company. It is revenue based application and It has some Filters at every Controller and Action of the application.They are Year and Quarter like some filters. for these filter changes I used Create Base Model, (It contains these filter values) and every other Models inherit from this Base Model.Base Model is use at #layout view.
Every change of filter should update the view.
Base Model in layout view something like this
public class BaseModel
{
public string YearSelected{ get; set;}
public string QuarterSelected{ get; set;}
}
and other Models are inherit from this BaseModel
public class RevenueModel:BaseModel
{
// other stuff
}
For all these things I am sending through the parameters.Seems like now Parameters are increase requirements are changes to add more parameters
1.I want to know the method that handle is correct and If method is insufficient Suggest me best
way to handle this parameter changes.
2.In the UI(View),
When user change the view by checking radio button and click on apply filter button I am using jquery for handle this,
window.href='Url Action with new Parameters';
window.href='#Url.Action("SomeAction","Controller",new{ // those all parameters } ';
When button click window.href will automatically call and update the view I want to knowIs this method Robust? Suggest me best way to handle this scenario.
"Simply I need a answer for if i use jquery to call an action and use high numbers of parameters for the call controller action"
What you're doing is doable, but as #ps2goat points out, you can run into issues if you have too many GET parameters in the URL.
But the right answer depends on how the filters will be used. Will the user often change the filters or will he rarely change them? If the user is expected to browse through your app and only rarely change the filters, I would suggest you to use another approach than including the parameters as GET parameters in the URL because:
You could run into problems if the total length of your URL becomes too long, as #ps2goat points out.
You could run into user experience problems. If a user bookmarks a page, and then later changes his filters, and uses the bookmark to return to the earlier page, his filters would be reverted, which is probably not what he would have expected.
It wouldn't look very pretty. All your urls on your site would look like /controller/action/?YearSelected=2014&QuarterSelected=1&Parameter3=2&Parameter4=8, which could also create SEO issues you would need to take care of.
In that case, I would recommend you to consider using a cookie or saving the user's filters on the server instead. (But preferably not in a Session, as that can create scalability problems for your application). If you used a cookie, the user's filters would be available to your Controller Action on each request automatically, as the cookie would be sent along with every request. (This is of course also something to have in mind when considering which strategy to use. If you have alot of cookie data, this will slow down the perceived responsiveness of your application, as the cookie data has to be sent along with every request to your server. So keep your cookie data as small as possible)
On the other hand, if you expect the user to change the filters often and maybe even several times on the same page, you could consider using jQuery to do an asynchronous POST to your MVC controller, retrieve the neccessary data using JSON, and update the view. This is actually not as difficult as it might sound.
What you would need to do to implement it, is to create a Javascript function on your page that submits your parameters to your controller action. You can send the data as JSON to the controller action also. Something like this could work: (Untested code)
<script>
function submitFilters() {
var parameters = {
parameter1: $('#parameter1').val(),
parameter2: $('#parameter2').val(),
...
};
$.ajax('#Url.Action("SomeController", "SomeAction")', {
contentType: 'application/json',
data: JSON.stringify(parameters),
success: function(data) {
alert('Received data back from server. Ready to update the view.');
}
};
}
</script>
Then you would hook up the filters (Radio buttons, drop downs etc) to call the method submitFilters.
MVC automatically converts the JSON data it receives from the client into your C# ViewModel as long as the property names match. Then you can do whatever querying and filtering you need to on the server, and then send the data back as the result of the action. (Pseudo code)
public ActionResult SomeAction(MyViewModel vm)
{
var data = SomeRepository.GetData(vm.Parameter1, vm.Parameter2, ...);
return Json(data);
}
Then you just need to update the view when the data is received. You would preferably use a template engine for that, but it's not required of course.
This is just a crude example of how you could do it. Normally I would create a Knockout View Model to encapsulate it all. But this should get you started.
I am building a cms, on the edit screen for a section you can edit multiple types of pages, the urls need to remain nutral, like this:
foobar.com/edit/section/my-content-page-name
foobar.com/edit/section/my-gallery-page-name
foobar.com/edit/section/my-blog-page-name
In this scenario the Index action is used for both gets and posts.
At the moment I have one massive ViewModel, that encompasses all the data required across all page types.
I feel this is quite wrong and, makes an ugly solution for deciding what type of page update on the post.
How can I keep the Action the same but use it with different strongly type ViewModels?
Is this even possible?
public ActionResult Index(string page)
{
var model = _pageManager.GetSection(page, SelectedSite);
return View(model.PageType, model);
// renders appropriate View based on page type.
}
[Transaction]
[HttpPost]
[ValidateInput(false)]
public ActionResult Index(SectionIndexViewModel model)
{
// all page types post back to same action to update content etc.
// at this point SectionIndexViewModel is getting bloated with properties because it must cater for ALL page types data.
var action = Request["action"] ?? "";
// currently use this to determine what event has been triggered
switch (action.ToLower())
{
// then goes to update the appropriate page, blog or gallery
// etc.
all page types post back to same action to update content etc.
There is your problem. The same action should not handle all post backs. Create one controller per feature (content, gallery, blog). It's how MVC is intended to be used.
Single Responsibility Principle do also apply to controllers.
You can even move the controllers to class libraries to get plugin like architecture for your CMS. I've described how here: http://blog.gauffin.org/2012/05/griffin-mvccontrib-the-plugin-system/
I managed to achieve this with some MVC basics that I had forgotten about.
The routing remains as per the defaults.
For each ViewModel type I delivered an extra hidden field in the form, with the type of the page/content/ViewModel eg: Content Page, or Blog Page etc.
In the Post action, I check the type of the page from this hidden field.
Then use TryUpdateModel using the expected ViewModel type for that page type.
And the rest is straight forward.
Pretty basic stuff really.
Ok, I'm still getting the hang of asp.net and the MVC framework and converting my knowledge over from classic ASP and VB - so please be gentle.
I've got my first view (/home/details/X) functioning well thanks to previous help pointing me in the right direction, now I need to add data from multiple tables and queries/views to the MVC view (I hate that SQL and MVC both use the word view for different meanings).
I'm not looking for someone to write the answer for me (unless they're feeling really energetic), more so for someone to point me in the right direction of what I should be looking at and reading up on to understand it and do this myself.
My problem
There are multiple datasets which I need to display in this view, and each different data set has a proper PK/FK 1-M relationship established, and the resultant records would need to be looped through.
How I would have done this previously
In my classic ASP days, I would have just defined the SQL query at the head of the page where the data was to be used, with a select statement along the lines of:
SELECT * FROM query_name
WHERE query_uniquecolumnname = Request.QueryString("value")
Once that was done, you'd set the do while query_name NOT BOF/EOF up, then drop in the field names you wanted from that query and it was all done.
How do I acheive this now?
So, fast forwarding from my classic ASP knowledge, how do I acheive the same outcome with MVC?
The tables/views I wish to use are already defined within my data model (and the relationships are showing up in there which I would assume is a plus), I just need to work out how I could call these within the page and use the ID of the record being displayed in the Details view to ensure only related data is displayed.
Thanks in advance
The concept you are looking for is called a ViewModel. Essentially this is a custom class that you write that contains all the data that would be used in your view. So it is responsible for amalgamating all the data from the different tables and exposing it as properties. If you're using a data access layer, this is often as simple as bringing a few entities together. If you're using raw SQL to do it, then you would execute your queries when the properties were accessed.
Then you would make your View inherit from the ViewModel, like so:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Models.MyViewModel>" %>
Now in your View, you can access all the different properties of your object simply by writing statements like:
<%= Html.TextBox("MyProperty", Model.MyProperty) %>
To construct your view from your controller, create a new instance of your class (MyViewModel), pass it the ID of the details record that you need, and the logic in your class will take care of getting the right data. Then return your view from your controller like normal.
var myDetailsModel = new MyViewModel(detailsID);
return View(myDetailsModel);
I would recommend reading this primer on ASP.NET MVC
http://weblogs.asp.net/scottgu/archive/2009/04/28/free-asp-net-mvc-nerddinner-tutorial-now-in-html.aspx
It covers most basic scenarios you'll need to get up and running.
If however you want to combine multiple resultsets into one, and then return it as a view, you should create a custom object, and map the resultset to it, then you can bind against your custom object in the view.
When I need to display multiple things like this on a web page, I use typically use RenderAction to do it.
RenderAction allows you to use a controller method dedicated to that particular part of the view (a subview, in effect), so you can pass a single data set of strongly-typed data to that "subview".
RenderAction is in the Microsoft.Web.Mvc ("futures") assembly.
If you are new at all of this, I apologize; this is a bit bleeding edge, but you're going to need to know it anyway. Be sure to check out the NerdDinner tutorial first.
http://www.andreas-schlapsi.com/2008/11/01/renderaction-and-subcontrollers-in-aspnet-mvc/
http://davidhayden.com/blog/dave/archive/2009/04/04/...