Passing a model with a Partial View - c#

I want to show a view on some of my forms, which shows a list of alerts, read from a database table. I think I need to use a partial view - but haven't used one.
So far, I created a partial view in my shared views folder called "_Alerts.cshtml".
In that file, at the moment, I simply have:
#{
Layout = null;
}
This is a shared view.
This is just me trying to display something.
And then, on my existing page, on which I want to display the alerts, I have this section of code:
#if (User.Identity.IsAuthenticated)
{
<div class="row">
#Html.Partial("~/Views/Shared/_Alerts.cshtml", null)
</div>
}
This works. However, my understanding is not right. At the moment, I pass no model to it. Is there no controller for the partial view? At the moment, I need to create a controller method - somewhere - that gets me a list of alerts from my data service, and then I want to format that and present it in the partial view. But I am unsure where the controller methods go. If this view is called from 8 different screens, would the 8 controllers for these screens have a call to get my alerts, and format them?
Seems like a lot of duplication.

They need not be duplication.
You can define the action you want inside a controller and call #Html.Action instead of #Html.Partial
Inside you action you can return a partial view.
public class AlertsController : Controller
{
public ActionResult Show()
{
var model = GetModel();//decide where this will come from.
return PartialView("~/Views/Shared/_Alerts.cshtml",model);
}
}
In your layout view or wherever you need to use it. you can simply call it as below.
#Html.Action("Show","Alerts")

If you have all the data you need to pass into the partial, then you can use the #Html.Partial and pass in the model.
If on the other hand, you want the view you are embedding to get the data itself, then you would use Html.RenderAction

Related

How to use one razor view for two action methods but with slightly different contents in some portions only? [duplicate]

I have set up a menu-controller to drive the top menu links based on which other controller is being used. Each other controller has a separate nested master page for each of its views.
so, i have a menu-controller with several methods that return viewresults, one per each controller, or "section" of the site. So currently each of these methods has its own view to render the menu. but each view to render the menu is the same code, the only thing that changes is the logic in the controller methods based on which links to render.
is there any way to have all of these controller actions target the same view? since the view is the same for all?
thanks
Yes, that is a common practice.
return View("Menu");
Create a strongly typed view that takes a container specifying your menu content. Pass this as a parameter on your return statement.
var thisMenu = CreateMenuForThisRequest();
return View ("Menu", thisMenu);
it depends on what version of ASP MVC you're using; with MVC 2, you can create an ascx control and use RenderAction
in your view you'll put something like
Html.RenderAction("Menu", "Navigation");
and have a navigation controller with a Menu actionresult
public class NavigationController : Controller
{
[ChildActionOnly]
public ActionResult Menu()
{
Menu model;//your menu
return PartialView("YourMenuAscxControlName", model);
}
}
I think if you're using MVC 1, the MVC Future project has the RenderAction but i'm not sure.
For my menu I use the RenderAction method
I'm also using the ActionOutputCacheAttribute from Steve Sanderson
http://blog.stevensanderson.com/2008/10/15/partial-output-caching-in-aspnet-mvc/
you will greatly increase your site loading time with this caching

ASP.NET Core MVC - Partial view only renders on one page

I have a partial view for ViewBag.count, which is defined in the ShoppingCartController. The problem is that the ViewBag will only show when you are on the ShoppingCart View. I want the ViewBag to be seen on all views. How do I fix this? I am currently rendering the partial like this:
#Html.Partial("_ShoppingCart", new List<bytme.Models.ShoppingCartModel>())
The partial view called _ShoppingCart:
<span class="badge">#ViewBag.count</span>
You should create a seperate action method which returns HTML markup needed to render the cart section of your page and include that in all your view using Html.Action method.
You may also decorate this action method with ChildActionOnly attribute so that users's cannot directly access this action method by requesting the url /ShoppingCart/Cart.
[ChildActionOnly]
public ActionResult Cart()
{
ViewBag.ItemCount = 2; // replace hard coded value with your actual value
return PartialView();
}
and in your partial view (~/Views/Shared/Cart.cshtml), you may write the HTML code which is needed for the cart segment of the page.
<span class="mycart">
Total items in cart #ViewBag.ItemCount
</span>
Here we are using ViewBag to pass the item count numeric value from the action method to it's partial view. But you may use a view model and use the strongly typed view approach to pass data from your action method to the partial view (this is my preferred approach).
Now in other views/layout file where you want to render the cart HTML, you can call the Html.Action method
<div>
#Html.Action("Cart","ShoppingCart")
</div>
<h1>Welcome to my site</h1>
When razor execute your view, it will see this Html.Action method and that will be executed and the output of that (the HTML markup generated fro the action method), will be included in the final output generated for the current view.
I am using the PartialView method, so that it will not try to execute the Layout code. (People make this mistake and gets an infinite calls to the Cart action method.
For Asp.Net Core projects
If you want to do the same thing in asp.net core projects, you may use View components to achieve the same results.
Create a view component to render the cart.
public class CartViewComponent : ViewComponent
{
public IViewComponentResult Invoke(string name)
{
var totalItemCount = 3;
return View(totalItemCount);
}
}
Create a razor view for this view component with the name Default.cshtml inside ~/Views/Shared/Components/Cart directory and you can have your razor code/HTML markup inside that to render the desired HTML. In this example, I am using a strongly typed approach where my view is stongly typed to int type and I am passing an int value from the the Invoke method when calling the View method.
#model int
<span>
Total items : #Model
</span>
Now you can invoke this view component in other views/ layout file by calling the Component.InvokeAsync method.
<div>
#await Component.InvokeAsync("Cart")
</div>
<h1>Welcome to my site</h1>

Can I Display User Specific Data throughout the site without adding to each view model

I have a site (MVC5) with a partial that is a header. This header displays the users name, and a logo of the organisation that they represent.
Each page also has a ViewModel of page specific data.
Is there any way I can have this Partial rendered on each page from a common model / object behind the scenes, or do I need to add my 'userheader' viewmodel to the viewmodel on each page?
You can get your requirement done through ChildActionOnly, lets say -
[ChildActionOnly]
public ActionResult LoggedIn()
{
// create your User View Model and pass it to Login Partial View
return PartialView("_LoginPartial", user);
}
Now create a Partial View with a stringly typed model what you are returning from the controller action above.
And in your Layout you can get the partial view like shown below -
#Html.Action("LoggedIn", "ControllerName")
In this way there is no need for you to include the same models across different views.

What is the right approach for a View with many forms and viewmodels in MVC?

I have a View to create an object and this object can be of three types. So I decided to create a tab panel with 3 tabs, and each tab represents a type of this object.
The user must choose which tab they will fill the data and press the submit. By the way, each tab has a FORM and a submit.
Until now, 1 View and 3 forms.
I want to know if there is a better approach to do that. Using render actions, partials, only one viewmodel, a lot of viewmodels.
My last try is using a big view model with all possibilities and a hidden field for determinate the type/tab panel is filled. When the user submit, I get only the fields this object type will use and save in the database.
Pro: Only one view model, only one controller. If the modelstate has errors, I can show the result in view with no much effort (using html validationmessagefor). Razor binds and Razor helpers.
Cons: I can't use dataannotations in all fields because some fields era required for a type and not for the other one and they have the same name. If a field of one form is changed, the field in another tab/form with the same name will change too. The object has a few properties that must change by the type choose, but another ones which is the same. Those ones show changes his value and the validation messages shows for every form.
This is the best approach
EDIT
I'm thinking about create a viewmodel as a container for the 3 viewmodels. So the fields will be separated and for each "submit" I'll use one of the three viewmodels.
Its a good approach?
Whether you use child actions, partials or just throw everything in one view makes no difference. In particular, once you've posted, whether you used child actions or partials to render the form in the first place, is inconsequential. You can't post to a child action.
I would suggest, first, that you create a wrapper view model to hold all your form-specific view models and use one view model per form on the page, and don't forget to instantiate those view models (the constructor of the main view model is a good place):
public class MainViewModel
{
public MainViewModel()
{
Form1 = new Form1ViewModel();
Form2 = new Form2ViewModel();
...
}
public Form1ViewModel Form1 { get; set; }
public Form2ViewModel Form2 { get; set; }
...
}
Next, since you are actually using separate submit buttons for each form, it makes this even easier, as you can just give a name to each submit button, and use that to determine which form the user submitted:
<button type="submit" name="_form1Submit">Submit</button>
And, then, in your action:
if (Request.Unvalidated["_form1Submit"] != null)
{
// form 1 was submitted
}
Finally, by default, the modelbinder will validate your entire view model (MainViewModel), and you'll get errors on any fields on the other forms that weren't filled out. However, you can use TryUpdateModel to re-run the model validation on whatever part of it you want:
if (Request.Unvalidated["_form1Submit"] != null)
{
TryUpdateModel(model.Form1);
if (ModelState.IsValid)
{
// do something interesting
}
}

Multiple calls to a controller on the same page

I have a Model that loads the Sidebar for my webpage, along with a Model that loads the main content. The content Model will be different for each page whilst the Sidebar model will remain constant. The content Model will change by the user clicking links:
~/Home/About
~/Home/Contact
What I ideally want to to put a line of code in _Layout.cshtml that loads a Controller that returns a PartialView displaying the Sidebar Model. So we might have:
<div id="sidebar">
#Html.Render("~/SidebarController/GetSidebar");
</div>
<div id="content">
#RenderBody()
</div>
But I know this won't work. How do I achieve this?
What I would do is to use #Html.Action("GetSidebar") in the _Layout.cshtml file, then you can have an action in your controller
public ActionResult GetSidebar()
{
//do stuff, populate menu items from database? etc
// Pass the data to the partial view
return PartialView("_Sidebar");
}
You would need this in each of your controllers unless you put this in a base controller, which you can then inherit on all your other controllers and add [ChildActionOnly] to the top of your action so that it can not be called directly.
I do the same thing you are trying to do. I use:
#Html.Action("GetSidebar", "SidebarController")
to draw my side bar and it works fine. I use ajax calls when changing views though so as to save on loading the sidebar over and over again and I have the ajax target the "content" div replacing its content with the partial view that represents each page.

Categories