Is it bad practice for views to create partial(child) views? - c#

I'm creating a little view framework. I'm not trying to stick to strict MVC adherence, but I am definitely trying to not get in the way of MVC practices.
Anyway, one of my questions is this: Is it bad for a view to create it's own child views?
For instance, in pseudo-ish C# code:
/*BlogEntryView*/
<h1>my blog entry...</h1>
<div class="Comments">
{# //code
foreach(var comment in Comments){
Write(new CommentView(comment));
}
#}
</div>
Is this bad practice for an MVC style? Would the proper thing to do be to provide a "placeholder" in BlogEntryView where the model populates it with CommentViews?
(also, please do not tag asp.net-mvc, this is similar, but in no way uses ASP.Net MVC technologies)
For comparison, the opposite of this, would be adding views with some placeholder in the model code:
/*BlogEntryView*/
<h1>my blog entry...</h1>
<div class="Comments">
{# SomePlaceholder #}
</div>
/*In the model code for BlogEntry*/
//v is a BlogEntryView
foreach(var comment in Comments){
v.PlaceHolder.Add(new CommentView(comment));
}

Both ASP.NET MVC and Ruby on Rails facilitate the approach I think your referring to through the use of partial views.
Using your example would typically result in a view that called a partial for comment record. In ASP.NET MVC C# this would look like the following: -
<h1>my blog entry...</h1>
<div class="Comments">
<% foreach (var comment in Model.Comments) { %>
<% Html.RenderPartial("Comment", comment); %>
<% } %>
</div>
Following current MVC philosophies and design principals this sort of decomposition into small "atomic" portions of view code is actively encouraged in many circles. However, there is always a balance to be sought between this decomposition and the maintainability.

No. This is actually how the ASP.NET MVC Templates features work in MVC. However, a potential pitfall in ASP.NET MVC is a slight performance cost to searching the file structure for the views. This can be avoided by specifying the full view path explicitly.
http://vishalswami.blogspot.com/2007/11/design-patterns-in-mvc_30.html discusses MVC architecture. The Gang of Four also advises that one of MVC's greatest advantages is that it facilitates a Composite UI (which is what you are describing).

In traditional MVC, there's one view to each controller and model, which is call the "MVC Triad". I think what you what is the view's template to be able to embed other templates for re-usability (think partials).
One piece of tech that gets this correct with mustache. It uses a view model, coupled with a template. The template can request other partials to reuse chunks of other templates.
The problem with many web MVC frameworks is that they treat the view as a template, which is the wrong way to view it (no pun intended). Once you have a class representing the view, this all becomes much easier.
Personally, I think the specific example you posted is bad form, because a template should never have that sort of access to objects and instantiating them like that. Templates should get their data from outside sources (the view model), which cane make those instantiations cleaner.

Is it bad for a view to create it's own child views? My answer is "NO". In face creating partial views gives you more power to change the UI contents in a modular way.
There are always multiple ways of achieving results. I personally think ascx files are a good clean way of creating reusable modules which can inherit from customized user controls. Its modularized approach keeps things very organized for me.
Just my 2 cents...

I understand that there are communities both pro and against views rendering child views.
Personally I consider the use of RenderPartial to still be a view concern. I don't have an issue with view depending on another view, providing it assumes the same model offered by the controller action's model.
RenderAction on the other hand is less of a view concern because it winds up invoking a controller action, which then renders a view. It's an entire request lifecycle unto itself. However, it has a lot of benefits, particularly for cross-cutting concerns such as site navigation, user account state, ads, or other page features that are completely independent of the page's primary goal.

Related

ASP.NET Core 6 MVC : how to implement layout logic?

I need to implement some logic to all views globally. I have a _layout view. How can I run logic (via a controller) for the shared layout?
I'm looking to implement the following logic:
Check if the user can perform an action of controller (if not, return to the previous page)
Check if the user account is active (after login, a user account can be disabled) (if not, log out and return to the login page)
Update the user log (update a table to save the IP and the time of the last user request to the server)
Hide controls for the user (prevent the user from seeing a control or changing any data)
In ASP.NET Web Forms 4, I created a class called Users, and this class was called inside of the master page's .cs code behind.
How can I accomplish something similar in ASP.NET Core MVC?
Welcome to MVC! The transition from ASP.NET Web Forms can be a bit frustrating as the programming models are so different. But once you get past the conceptual hurdles, you'll find a lot of tasks are much cleaner and easier to maintain.
I struggled with a similar challenge myself when migrating from ASP.NET Web Forms to, first, the ASP.NET MVC Framework and, later, ASP.NET Core MVC. There are a few common strategies for addressing problems like this, which I'll summarize below.
Custom Filter
For most of what you're talking about, the textbook solution is to create an custom filter, which is a piece of code that can be applied to multiple actions or controllers, and will be called at various stages of execution (e.g., before or after an action). You can also wrap filters in attributes, which makes it easy to apply them as an annotation to any action or controller. You can also register filters globally so they apply to all actions on all controllers. Filters are especially useful when you want to validate a request based on the current request—including potentially the current user.
View Component
Another approach, introduced with ASP.NET Core, is a View Component, which is a bit like a partial view, except that it has code behind it that operates a lot like a controller. This is useful if you need reusable components that share their own model on every page of your site, as it allows that to be constructed independently of any one action, while being shared across multiple views.
The textbook example of a view component is a login control, which receives a view model with information about the currently authenticated user, and uses that to conditionally display e.g., a sign-in or sign-out link.
Aside: I often use view components for centralizing my navigation, so I don't need to relay the data for the navigation down to every single view model. That's beyond what you're asking about here, but may help further conceptualize when a view component is useful.
Business Object
From a code organization perspective, you can still maintain much of the logic inside a custom User class, or even as extension methods off of e.g. the ASP.NET Core ClaimsPrincipal class (which is returned from the HttpContext.User property). Determining what the current state of the user is for the purposes of e.g. authorization makes sense in a separate class; handling the ASP.NET Core response to that (such as redirection to a previous page) belongs in the filter or view component.
And, of course, you can also relay (properties from) this class to your view via your action's or view component's view model if you need to rely upon them to customize the user interface.
Conclusion
There are obviously a number of approaches here—as well as some I haven't mentioned, such as implementing a base controller—but this should help get you started in thinking about how to centralize site-wide business logic without needing to repeat it in every action of every controller, or polluting your _Layout.cshtml with logic that would normally reside in a controller.
First I wouldn't recommend a Controller for the _layout page as this is not how the MVC architecture works.
You could create a view using the layout page, like UserView.cshtml.
Then you can create a UserController.cs and your actions in it.
I hope I could help.
Check if the user can perform an action of controller (if not, return
to the previous page) Check if the user account is active (after
login, a user account can be disabled) (if not, log out and return to
the login page) Hide controls for the user (prevent the user from
seeing a control or changing any data)
You can use #if(User.IsInRole("Admin")) to determine the identity of the user in the layout, and then if the user can't perform an action of controller, use class="disabled" to disable the action.
If is the user you want to show a control , you can contains this control after it is judged to be true. So hide controls for the user.
For example, if it is admin, you can add a role or view the current role.
Demo as below:
_layout:
#inject SignInManager<AppUser> SignInManager
#inject UserManager<AppUser> UserManager
#using Microsoft.AspNetCore.Identity
#using Identity.Models
<!DOCTYPE html>
<style>
.disabled {
pointer-events: none;
color: grey;
text-decoration: none;
}
</style>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>#ViewBag.Title</title>
<link href="~/twitter-bootstrap/css/bootstrap.css" rel="stylesheet" />
<p>Login</p>
#if(User.IsInRole("Admin"))
{
<p>Admin</p>
<p>Role</p>
<p>Claims</p>
<li >
<a asp-area="" asp-controller="Role" asp-action="Create" >Hello #UserManager.GetUserName(User)! Create a Role</a>
</li>
}
else
{
<p><a class="disabled"href="/Admin" target="_blank" >Admin</a></p>
<p><a class="disabled" href="/Role" target="_blank" >Role</a></p>
<li >
<a asp-area="" asp-controller="Role" class="disabled"asp-action="Create" >Hello #UserManager.GetUserName(User)! Create a Role</a>
</li>
}
</head>
<body class="m-1 p-1">
#RenderBody()
</body>
</html>
result:
Update the user log (update a table to save the IP and the time of the
last user request to the server)
Use javascript to get the URL of the current request and the time of the current request, and then use ajax to call the action of the back-end controller, and record the time of the obtained request into the database.

Best way to separate C# and HTML

I've been working with a team on a ASP.NET project, a project management tool.
Most of the pages is basically writing stuff from the database to the page and store the changes which where made.
This is our first project in ASP.NET (We currently use ASP Classic for all our other projects), our senior told us not to use MVC.
The problem now is that our Designer/Front-end developer is pretty much clueless if it comes to ASP.NET, he also does not want to use VisualStudio if possible (He uses OSX).
For example:
What would for example be the best way to write a dynamic list?
This is what we currently do:
<div id="tasks" overflow-y="scroll" style="height:400px;">
<ul id='taskList'>
<% PrintTasks(); %>
</ul>
</div>
In the code-behind:
public void PrintTasks()
{
foreach (var task in _tasks)
{
Response.Write("<li rel='#' id='task_"+task.TaskID+"'>" +
...
"</span>" +
"</li>");
}
}
In this case, its not possible for the designer to edit the li tags without going into the code behind.
Thanks.
I'm afraid your front-end developer will have to suck it up and learn how to use VS. If your entire team is working on ASP.Net, there is no way to escape the wrath of VS.
MVC is a good for separation of C# and HTML. Razor engine allows a nice way to separate code and markup.
For you list you would probably do something like that in Razor:
#foreach (var item in items)
{
<li rel="#" id ="blah_#item.id">#item.text</li>
}
You should absolutely raise the question why MVC is not an option. It would be a much better fit for what you're doing, and the code quality would therefore also be much better, which helps the project overall. Not to speak of team productivity (and the ability for your designer to "escape" VS).
Doing some CSHTML with Razor you and your team would almost feel "at home".
According to your context, the main friction point is your front end developper.
So, decide on using a javascript framework (Knockout.js, angular.js, whatever).
Let your front-end developer design the UI that will get all needed info from JSON provided by your web stack of choice (ASP.NET MVC, Web Api, whatever).
This will ensure that he can develop his UI with the tool he wants. And you can work on the backend without worrying about UI. You just need to agree on the JSON contracts and stick to them.
This way, you will keep a clean separation of concerns with :
- everyone developping with the tools they're most at ease with
- no layer mix and match (ie having some HTML "generated" in your code behind)
And, your list will look like (using knockout) (syntax may not be accurate):
<ul data-bind="foreach: myList">
<li><span data-bind="text: Text, attr: { id: Id }" /></li>
</ul>
Code behind (using asp.net mvc) (syntax may not be accurate)
public JsonResult GetList()
{
var res = myServiceLayer
.GetListItems()
.Select(
x => new { Text = x.Val, Id = x.Id }
);
return JsonResult( res );
}
When using ASP.NET WebForms, I would give you the following suggestions:
Take advantage of CSS. Accept that your designer will not be able to edit the HTML produced by your code-behind, but if you use CSS intelligently you can probably overcome that challenge. In your example, you have a div and a ul element with id's, and the designer can then change the CSS to style these elements and child elements using the cascading properties of CSS. (Ex: #taskList > li)
Consider client side templating when appropriate. When presenting data for the user, fx. lists, consider using a templating framework. (jQuery fx) These templates can be placed in separate files that your designer can edit without VS. (Reference: load jQuery-Templates from external file?)
Spend some time learning the different controls available in ASP.NET WebForms, instead of using Response.Write to build HTML. Using Response.Write in the code-behind is generally a really bad practice and should be avoided, because your code will quickly turn into spaghetti and be unmaintainable. And if you're hoping to use ASP.NET WebForms (Or MVC) on later projects and want your senior and management to see the advantages of ASP.NET, then you and your team should spend a little time learning the framework. When you do that, you will be rewarded with more structured code that is easier to maintain. But if you use your current Classic ASP practices in ASP.NET, chances are that neither your senior or your management will see the benefits of using ASP.NET. And that would be a shame, because the benefits are really there. :-)
Good luck!
In the specific case you mentionned, in 'classical' asp.net, you're supposed to use a repeater: http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.repeater(v=vs.80).aspx
<asp:Repeater id="Repeater1" runat="server">
<ItemTemplate>
<li rel='#' id='task_<%# DataBinder.Eval(Container.DataItem, "TaskID") %>'>
<span>Whatever</span>
</li>
</ItemTemplate>
</asp:Repeater>
Basically, learn which webcontrols are available, create new ones every time it's necessary, and try to get as much as you can from asp.net webform's limited binding/templating system.
use Repeater here:
<asp:Repeater runat="server" ID="r1">
<ItemTemplate>
<li rel='#' id='<%# Eval("id","prefix{0}") %>'><span>
<asp:Label runat="server" ID="t1"><%# Eval("Caption") %></asp:Label>
</span></li>
</ItemTemplate>
</asp:Repeater>
and in cs file:
r1.DataSource=list;
r1.DataBind();

Is there a way to prevent a plain text section of an MVC view from rendering?

I have a web application I'm working on, and I'd like to avoid the clutter of if/elseif/else in the view, and of having way too many separate ascx files just for conditional inclusion in a view.
What I want to do is create some sort of simple class that works like so (an example will demonstrate what I'd like to achieve)
<% using(RequiresAccessTo.Feature(Features.FancyStuff)) { %>
Special content for users
<% } %>
If the user does not have access to the feature, the class would render a sign up link instead. I know I could simply use if/else, but the content of the else block could be one of 2-3 different standard responses depending on access level, and this mechanism would be used in countless places around the website.
If there a way to simply prevent Special content for users from rendering altogether, it'll mean I can make the templates around the website really easy to maintain.
Another option you might try would be to create a custom server control. Properties on the control could include the feature set you'd want to check permission for. If that permission wasn't met, the control would render the sign up link appropriate for that access level or permission. Your view would end up looking something like:
<controls:SignUpWrapper runat="server" id="signup" access="FancyStuff">
<div>
Approved user contents.
</div>
</controls:SignUpWrapper>
In your control, you would first check permission then render either the appropriate link or the provided HTML. The trickiest bit here might be getting the routing information to your server control code. Not something I've tried. Worst case scenario I imagine you should be able to pass the necessary information or even the entire sign up link through properties. No wait, worse would be bypassing routing altogether and forcing the URL in through a configuration value, erm... yeah. Either way it's a bit wordier than your desired example, but not far off.
I suppose some folk might see even the server control idea as a bit wonky. But as long as you stay away from view state, post back behavior and maybe a few other classic ASP.NET features, there's nothing preventing using server controls. We all use masters and content containers already. Sorry to preach if you're already in the choir. =)
For the time being, this is stretching my imagination and maybe even common sense a bit depending on the difficulty of generating that link. I'll check back if I think of anything else.
I can think of one other decent option to keeping your if/else logic in a partial view.
You could create an HtmlHelper extension method. HtmlHelper is the object used when calling things like Html.ActionLink in a view. You can write your own method that produces whatever HTML you want. The conditionals all take place in the extension method and your view code is reduced to:
<%= Html.MyControl(param1, param2) %>
The rule of thumb I follow when deciding when to create an HtmlHelper extension method and when to create a partial view is generally how much HTML is going to be generated. If I end up with more than a few lines of rendered HTML, a partial control is generally your best bet as it is generally easier to understand and maintain the HTML.
If you're worried about organizing numerous partial views, you can create subfolders under your Shared view directory. The views can then be referenced like this:
<% Html.RenderPartial("Subfolder/PartialView") %>
I just thought of an alternative solution:
<% if(!BlockContentAndRenderPlaceHolder(Feature.Whatever)) { %>
whatever
<% } %>
I know it looks a bit obtuse, but if you saw the content on these pages, you'd understand.

ASP.NET MVC ViewPage rendered in a ViewPage

I'm wondering if it is possible to render a ViewPage inside of a ViewPage.
Normally, you'd have this:
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<my_object>"%>
and then in the page you can render a user control:
<%Html.RenderPartial("~/Views/Shared/mycontrol.ascx", this.ViewData);%>
However I'd like to render a ViewPage in a ViewPage. The reason? I'm working on a very large project with 10+ developers and a lot of code already written. A large piece is already written which dynamically renders the UI however there are 15+ methods which pass around a ViewPage.
In other words, I'm calling this in the ViewPage:
<%this.RenderUI();
And the method stub is this:
public static void RenderUI(this ViewPage viewPage)
So in order to reuse the most code, I'd like to pull the call to this.RenderUI() into it's own ViewPage but then include that on some ViewPages. It's a hairy situation and it sounds unnecessary, but there's a lot of code rework that would need to be done otherwise. Does anyone know if this can be achieved?
Render your subview in the main view using RenderAction() instead of RenderPartial(). This allows you to keep your controller and view for each subview, and "inject" the output of each controller/subview combination into any main view, in a location of your choosing within the main view.
This arrangement should ease some of the complexity of managing each of the views and subviews. It is an approach reminiscent of the "web parts" in traditional ASP.NET or SharePoint. It is a more modular approach that provides for better reusability.

Passing ViewData to ViewPage in ASP.NET MVC

I'm trying to build a custom control in ASP.NET MVC. The thing that I want to avoid is mixing the view (html) and the code (C#) in these controls.
After hours of googling and struggling with this problem I've finally thought of creating helper methods that use the ASP view Engine thought the ViewPage class.
I'm able to create an instance of the class and even load an ascx template using LoadControl and LoadTemplate methods but the problem is that when I pass a ViewData object to an instance of ViewPage class I cannot see the ViewData in the ViewPage and I'm getting this error:
The ViewUserControl '~/Components/Controls/EditableLabel/Views/EditableLabel.ascx' cannot find an IViewDataContainer. The ViewUserControl must be inside a ViewPage, ViewMasterPage, or another ViewUserControl.
Generally the final effect that I want to achieve is loading controls with some parameters, these parameters should be readable in the template (ascx).
example:
<% Html.EditableLabel(Writer, "memberName", "Name", "Antonio", "/identity/updateProperty", "memberName"); %>
Is there a way to pass the ViewData to the ViewPage instance that I create?
Is there a smarter way to create custom controls/widgets in ASP.NET MVC?
Thanks in advance
You can pass VD to the VP from controller via "return View(...)" or via "ViewData[name] = value".
It's better to use ascx controls which are inherited from ViewUserControl in order to create a custom control. In this way you easily can pass data to it:
<% Html.RenderPartial("FooControl", new FooViewData(){ Name="aa", Type="awesome", Id = 2}); %>
<% Html.RenderPartial("FooControl", new FooViewData(){ Name="aa", Type="awesome", Id = 2}, ViewData); %>
And all necessary parameters you can define in your FooViewData class.
If you really want to write an extension to the HtmlHelper which will load some ascx controls you should look at a Repeater class in MvcFutures (sources of MVC are here). But I'm sure that in a common case you'll not need this.
I am tracking with zihotki on this one. With #2 I will add a caveat, however. In the default implementation, which uses neutered ASPX and ASCX as views, creating MVC controls (use the template to ensure your dog is neutered) is the best option.
Now for the caveat. ASPX and ASCX do not have to be the views. If you look at Phil Haack's blog, you will see some other view engines mentioned, like the Spark View Engine (ASP.NET MVC Northwind Demo Using the Spark View Engine). In fact, Phil has set up a sample of using alternative view engines in this entry: Rendering A Single View Using Multiple ViewEngines.
I am not suggesting that you move to other view engines, only that there is an option, if you find one that better fits your problem domain. Sometimes it is better to stick with the out of the box implementation. In this case, keep things simple with ASCX controls.
By the way, if you are seriously getting into MVC as your UI story, you should look at the codeplex projects for MVC. The most important here is probably MVC.Contrib, but there are a few other gems in the list.

Categories