Display ViewComponent in shared _Layout, only on certain pages - c#

I have this ViewComponent in my shared layout:
#if (User.Identity.IsAuthenticated)
{
<vc:room></vc:room>
}
Is there a way to only have it render if the user is on a certain page, or exclude it from certain pages? Otherwise I'd just have to repeat it on every page that I need it to show?

There are multiple ways you could do this.
One way would be, in your _Layout.cshtml decide on a ViewBag property name and check if it exists and is set to true. I've called it ShowVC:
#if (User.Identity.IsAuthenticated && ViewBag.ShowVC is true)
{
<vc:room></vc:room>
}
In the views/pages you want to show the view component from the layout, in a code block, set ShowVC to true. For instance, if you want to show it in a view called Index.cshtml, do this:
Index.cshtml:
#model ModelClassName
#{
ViewBag.ShowVC = true;
}
Just skip this code block in the views where you do not want the view component to show.
This is exactly also the way the default application template uses(as of .NET 5) to display a different title for each view.
In _Layout.cshtml, you have:
<title>#ViewData["Title"] - AppName</title>
And the title for a specific view is set in a code block:
#model ModelClass
#{
ViewData["Title"] = "Title for the view"
}
For the most part, ViewBag and ViewData have only syntactic differences. For more on their differences, see this.

you can encapsulate the VC view component in partial views.
#if (User.Identity.IsAuthenticated && Model.HasAuthed)
{
<vc:room></vc:room>
}
when to call the partial view, pass the controlling model variable
<partial name="./AuthenticatedLayout.cshtml" model="#HasAuthed" view-data="ViewData"/>
there is a reference

Related

Sending Temporary data to multiple partial views from view MVC

I'm wondering if there is a good way to do this. I'm currently trying to send some temporary data to multiple partial views being called from the same view page in my MVC application.
I'm currently attempting to do this with TempData but I can see my understanding is limited as it is only going through for one partial request. What method do I need to use to filter out to all of my partials?
Main View Page:
#{
ViewBag.Title = "Main View Page";
TempData["ReturnUrl"] = Request.Url.OriginalString.ToString();
}
#Html.Partial("_StatusTable1")
#Html.Partial("_StatusTable2")
#Html.Partial("_StatusTable3")
#Html.Partial("_StatusTable4")
#Html.Partial("_StatusTable5")
Partial View Example:
#{
var temp = TempData["ReturnUrl"]; // temp is null on all partials except the first
}
// Partial View Code ...
Thanks in advance.
In your main view page call the partiel views like that
#Html.Partial("_SomePartial", TempData["ReturnUrl"])
I think that even this would work.
#Html.Partial("_SomePartial", TempData)
Get the value from TempData like this. ReturnUrl Value will retained across all the Partial Views
#{
var temp = TempData.Peek("ReturnUrl");
}
// Partial View Code

Determine what View is going to be rendered in #RenderBody()

In _Layout.cshtml is it possible to determine what View is going to be rendered in #RenderBody() ?
You can get the View (i.e. Index.cshtml) through ((RazorView)ViewContext.View).ViewPath
Example for your needs:
<script type="text/javascript" src="~/Content/Scripts/#(Path.GetFileNameWithoutExtension(Server.MapPath(((RazorView)ViewContext.View).ViewPath))).js"></script>
If you need your actual View (i.e. _Layout.cshtml), you can use VirtualPath instead.
Old answer
Reading your comments, you want to add a
<script ...>...</script>
depending on the view but outside of #RenderBody()?
Then put
#RenderSection("Scripts", required:false)
and in your view define the section like
#section Scripts {
<script ...>...</script>
}
So you don't need to maintain your _Layout.cshtml since every View defines their own scripts.
Here is an easy explanation: http://weblogs.asp.net/scottgu/asp-net-mvc-3-layouts-and-sections-with-razor
What you can do is check Html.ViewContext.RouteData.Values. That's a dictionary with controller, action, and id (as necessary).
Read this article and it will solve your problem.
Edit
RenderBody
What is RenderBody?
In layout pages, renders the portion of a content page that is not
within a named section. [MSDN]
How RenderBody works (graphical presentation)?
The #RenderBody() renders the view controlled by the controller. so if your controller is like this.
public class HomeController : Controller
{
public ActionResult Index() // Renders File /Views/Home/Index.cshtml
{
return View();
}
}
Then the public ActionResult Index() Index.cshtml will be the view it will render located int the /Views/Home folder.
You can add to the Index.cshtml or _Layout.cshtml view to render other Views or partialViews By adding #Html.Partial("_MyView") as shown below.
#Html.Partial("_LayoutHeaderHeader")
#Html.Partial("_LayoutHeaderNavbar")
Sometimes it is easy to setup a few layout pages to call from different Views.
If you want to call scripts to you View you should always create a _PartialView and place your scripts in the partial view and call that View at the bottom of your View like this #Html.Partial("_MyView") and the scripts will set properly.
Here is a good tutorial. http://www.codeproject.com/Articles/698246/ASP-NET-MVC-Special-Views-Partial-View-and-Layout
If you derive all your models from a base model then you could add a property to you base model that returns the controller name, which you can get using
this.RouteData.Values["controller"].ToString();
It would be even better if you had a BaseController class because you could put this in the constructor and never have to touch it again.
Since you would be returning a descendant of the base Model to your index page which has the controller name, now you could use some scheme base on #Model.ControllerName. If your controller services multiple views the property could be updated to indicate a certain view name.
I don't think you can get the name of a Partial inside the index unless you use jquery and by that point the page resources have already been loaded.
Edit: One other trick would be to create your own version of #Html.Partial() HtmlHelper class. So you have #Html.MyPartial("ViewName") and inside that method call the internal function that generates Html.Partial and then inject your dependencies.
EDIT: I just read your comments about the issue and think that the better way is using the code snipplet provided by #Matt in another answer.
You can use the #section razor statement inside your view to inform wich script should be loaded.
Layout template placeholder
#RenderSection("scripts", required: false)
View Code
#section scripts {
<script src="~/Scripts/custom-imgedit.js"></script>
}
The example above inform that the custom-imgedit.js will be loaded in the render section placeholder. Note: You can even use bundles like #Scripts.Render("~/bundles/myCustomScripts")

Passing a model with a Partial View

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

Convert Views to Partial Views

I created some views in visual studio by clicking right click=> add => view.
I select in the selection: "use a layout or master page".
Now I want to turn these views to partial views should I delete it and create new? or I can somehow turning it to partial view without deleting the views?
In Razor there is no much difference in View and partial view
Only difference is
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
If there is no layout specified they could be considered as partial views.
From your controller action you return PartialView(); instead of return View(); this layout will not be applied.

How to handle a strongly typed modal in a strongly typed view with different types

I'm using modals for logging in and registering a user. Each modal is strongly typed to make use of the ASP.NET built-in account classes (RegisterModel and LoginModel). However, since the two buttons to call these modals are located on the navbar and the navbar is placed on every page I receive errors because most views are strongly typed and thus cannot handle a partialview (the modal) to be using a different strongly typed model.
How are strongly typed modals handled in a strongly typed environment?
_Layout:
<body>
<div class="navbar">
#Html.Partial("_LoginPartial") // contains buttons to call login/register modals
</div>
<div>
#Html.Partial("_LoginModal")
#Html.Partial("_RegisterModal")
</div>
<div class="container">
#Html.RenderBody()
</div>
</body>
/News/Index:
#model List<NewsBulletinViewModel>
LoginModal:
#model NoName.Models.LoginModel
On a related note:
Since I have forms inside my modals, how can I refer back to those modals when validation errors occur? Ideally the modal should popup again (or never be closed) with the validation errors shown.
There is an overload in #Html.Partial that takes an object, used for the partial page's Model. If you include the Partial in your layout, in every page, you need a logic to keep that data. For example, if you take LoginModel and RegisterModel, you could do this:
#Html.Partial("_LoginPartial", ViewBag.LoginModel ?? new LoginModel())
#Html.Partial("_RegisterPartial", ViewBag.RegisterModel ?? new RegisterModel())
And leave to the executing controller the role to put a LoginModel (or RegisterModel). If there isn't any in the ViewBag, it will fallback to creating an empty one.
Edit: Based on the additional information, I'd do this for the LoginPartial (RegisterPartial would be the same logic):
public class AccountController : Controller
{
public ActionResult LoginPartial()
{
return PartialView("_LoginPartial", (Session["Login"] as LoginModel) ?? new LoginModel());
}
[HttpPost]
public HttpStatusCodeResult SaveLoginModel(LoginModel model)
{
Session["Login"] = model;
return new HttpStatusCodeResult(200);
}
}
And then, in _LoginPartial, do as you would, but add a javascript code to send an ajax post request to the SaveLoginModel action of your controller when the value change to keep your model in sync (there is plenty of information around on how to do that).
Now, instead of doing:
#Html.Partial("_LoginPartial", ViewBag.LoginModel ?? new LoginModel())
#Html.Partial("_RegisterPartial", ViewBag.RegisterModel ?? new RegisterModel())
You would do:
#Html.Action("LoginPartial", "AccountController");
#Html.Action("RegisterPartial", "AccountController");

Categories