In my view (asp.net mvc razor) I would like to display a description (from my model) in french or dutch based on current thread culture. Below is my actual implementation.
<td>#item.Title</td>
<td>#item.SubTitle</td>
#if (Thread.CurrentThread.CurrentCulture.Name == "fr-BE") {
<td>#item.MaterialPacking.DescriptionFr</td>
} else {
<td>#item.MaterialPacking.DescriptionNl</td>
}
<td>#item.Quantity</td>
...
I think the code clarity is not optimal but I don't think creating a helper specific for this is necessary. Are there any other possibilities?
Thanks.
UPDATE
Here is an extract of data I retrieve from my repository.
As you can see I have 2 possibilities: ...fr or ...nl
I need a specific item based on the current culture.
here is the linq:
var request = requestRepository.Find(x => x.RequestID == requestID)
.MyInclude(x => x.TransportedMaterials.Select(y => y.MaterialPacking)).FirstOrDefault();
return request.TransportedMaterials;
If you use a custom ViewModel, you can make it the controller's responsibility to populate the MaterialPacking property with the message in the correct language. That moves the code out of the view.
However, the if/else statement is still bad practice. What happens if you decide to support Spanish? Do you want to modify every one of these if statements throughout the code? You should create a service where you can pass it the key for a message and it will give you back the actual message in the current language.
So your controller code would end up saying something like this:
item.MaterialPackingDescription =
_languageService.GetDescription(item.MaterialPacking);
And your view code:
<td>#item.MaterialPackingDescription</td>
You don't need special casing (it wouldn't scale: if you were to add one more language, you'd have to go in an add a new else block everywhere you do this). The way to get localized strings is to use the built-in resource manager. Visual Studio makes this very easy... Look it up (or look up localization) in MSDN.
So your code would become:
<td>#item.Title</td>
<td>#item.SubTitle</td>
<td>#Resources.MaterialPackingDescription</td>
<td>#item.Quantity</td>
...
Note that you are also using the wrong property: For resources you should use Thread.CurrentThread.CurrentUICulture rather than CurrentCulture.
Why not perform this logic in your controller or view model. Then you wouldn't need to do it in your view.
Take a look at this blogpost about Localization.
With resource files you'll be able to keep your view cleaner because there's no need for if constructions to display messages in the correct language.
You must store your string values in App_GlobalResources, and then use it from there.
Check this article for more help
Related
Most of the good references for MVC on web strongly suggest "views must be dumb". It should not contain any logic. This does not seems valid when I try to implement it.
Some of my views display only first name of student and some of them display only last name and some of them display full name. My database (and POCO and DTO) store name in separate components. I see best place to format the name is view.
My view changes on some actions on client side without hitting server. Example, on clicking some button, it hides some part of view and shows other and disable some controls. Or another button opens new window and takes some inputs and validates it. This input is never sent to server; it is only useful for some client side activities.
I have validations on server side. But just to save the hit to server, I also validate on client side.
Data binding with KnockoutJS.
Based on data (start and end date) received from server, I generate table on client side to display sections of that period slicing 1 hour each. This is something scheduling like GUI. I need to do date and time calculations in view to achieve this. When I was in stone age (ASP.NET WebForms), I was generating this table on server side; I know you are shocked. I shifted it to JS for performance.
In SPA, view hold most of the logic by fetching only necessary data from server through AJAX.
I can put many other similar examples here those force me to put some logic in view. Considering that view still holds logic and use of JS is increasing day by day, can we still say "views must be dumb" is a correct statement?
Some details explaining the same with respect to points above will help.
Notes:
Though my question is based on ASP.NET, I am expecting answers referencing to ONLY MVC as a design pattern; no matter what technology I use. So please do not suggest what is another way to do validations. Points above are just to note some of the cases where logic is necessary in view.
There might be something wrong in how I am implementing above points. But My only point is that, use of JS (and putting logic in views as a result) is increasing.
Everyone is welcome to contradict above points in case I am implementing it wrong way; just do not redirect entire discussion that way.
Edit 1:
#kayess: Yes, I have Models as well as ViewModels and this is obvious in MVC. Server logic that is strongly related to specific view and cannot be reused is put in ViewModels. Major part of domain logic which is reusable is put in Models. Even after having ViewModels, there are many things those I will prefer to do on client side. About narrowing the question, the basic answer to this question will be "Yes" or "No". Other details will be just to support the answer. I do not think this will attract opinions as there must be something about MVC views that I have not fully understood. The one answering the question just need to point that out to me.
I think generally by "views must be dumb" means specifically the server side part of the views. Having TypeScript/JS in a view is perfectly normal. I wouldn't however expect to have dozens of lines of C# in the view that is fetching records from a database.
However, having some very simple logic such as the following is pretty common:
#{
if(user.IsLoggedIn)
{
<span>You have new messages!</span>
}
else
{
<span>You need to login to view messages.</span>
}
}
However server side view code shouldn't get much more complicated than that because that breaks down the whole point of separation concerns and having appropriate abstractions and design patterns etc, and just becomes unmaintainable.
See also: Microsoft documentation on "Adding a View"
May it be any technology there are set of theories supporting different concepts, like you said view should be dumb there are advocates of model should be dumb.
Idea here is let your view model take care of the manipulation if any needed, and let your view refer to view model. So that change is centric.
And I believe you are alredy doing that.
Although this is an old question and has an accepted answer, I want to add some points not covered in any answers.
I think you are overlooking cases where more logic can be pushed to your domain model. For example, you mention the concept of "Name" and that you must use conditional logic in your views to decide whether to show first, last or full name. I have a class called Name in my domain model. It incorporates the logic of FirstName, LastName and FullName like so:
public class Name
{
public Name(string firstName, string lastName)
{
this.FirstName = firstName;
this.LastName = lastName;
}
public string FirstName { get; }
public string LastName { get; }
public string FullName
{
get { return $"{this.FirstName} {this.LastName}"; }
}
}
In my UI I have have ViewModels with the whatever name property I need. When I convert my domain models to view models my conversion logic tells the ViewModel which property to use. If I need FullName I use Name.FullName. If I just need FirstName, I use Name.Firstname. At no point do I need to put conditional logic about names in my views.
In addition, you can use custom helpers, formatters and extensions to encapsulate reusable logic for views. For example, I have utilities to format phone numbers, addresses etc, so that I do not have to put this logic in every view that needs it. I also have a paging module in my UI that encapsulates paging so that I do not have to have paging logic in my views other than to call the pager.
Partial views are also helpful. For example, from the accepted answer, I would put the following in a partial view and just call it in each view, rather than repeating the conditional logic it in every view:
#{
if(user.IsLoggedIn)
{
<span>You have new messages!</span>
}
else
{
<span>You need to login to view messages.</span>
}
}
My point is there is a lot you can do to move logic out of views to keep them dumb.
What is the point in using a viewmodel when all the relevant data is already available through the parameter in the signature of the controller? I've seen a lot of examples similar to this:
public ActionResult Index(BasicPage currentPage)
{
var model = new BasicViewModel { Heading = currentPage.Heading, Body = currentPage.MainBody, MyBlock = currentPage.MyBlock };
return View(model);
}
Why not just send in the "currentpage" in this example directly to the View? Is there something that is considered bad practise by doing like that?
The above was a general question for asp.net-mvc. I'll add a question about Episerver as well here as well and if I'm lucky someone can answer that as well.
When looking through the Alloy site done with MVC I also saw similar behavior like above where a viewmodel is created in all controllers, why not just send in the Page that is sent into the controller directly to the View? It seems like an unnecessary step to create the viewmodel? Most likely I'm missing some important point here =)
The idea behind viewmodels is that they are optimised for presentation. If you use domain objects you end up having to put logic into the view itself to control the way things are displayed. Putting logic into the view is poor design so the viewmodel gives you some flex so that you have this logic managed in a more appropriate place.
In addition, you can make your viewmodel simpler, only including the fields that are required for the specific view.
Your code example is not a good idea I agree. But having a view model is a good pattern. You might not need it right away but having a view model available and in place is a good option to have for upcoming additions. I would go with Joel's concept from his MVC templates: http://world.episerver.com/Download/Items/EPiServer-CMS/EPiServer-7---CMS/EPiServer-7-MVC-Templates/
I am attempting to construct an asp.net mvc app which will use the urls like:
/Controller/[Number]/Action/Id
I have got it to always call my controller and pass it the Number and the Id fine...
However I now want to return a different view depending on the Number
I could have options like:
if([Number] == 1) { return View("ViewName");}
if([Number] == 2) { return View("ViewName2");}
however I instead was wondering if there was a way to change the core so that instead of searching at ~/Views/controller/action.aspx I could have my own method which did some checking on the Number then passed to the virtual file provider is a different path
Hope this makes sense!
Decide which view to load, depending on input parameters is a controller task. You could write your own view engine.
But it is easier to return the full path to the view you want to return.
return View("~/myviews/ViewName3.aspx");
This will render ViewName3 from given directory.
You might want to look at decorating your controller method with Action Filter Attributes.
Then, you could do something special inside the Action Filter Attribute.
Or, you could pass Number to a Model object, then have the model Object return the right View path.
Either way, your instinct of trying to keep too much logic out of the Controller is sound, especially if [Number] is somehow a business concern and not a view concern.
You need to look into / google creating a custom view engine.
By the sounds of things you probably just want to extend the built-in WebFormViewEngine and just override the locations and the .FindView() method.
HTHs,
Charles
I have a view to display a list of items.
The user can edit, delete or create new items, but according to their authorizations they may or may not be allowed to do some of this actions.
I have the requirement to display only the actions which the current user is allowed to do, but I don't want to clutter the views with authorization if-else's
Despise of being a very common requirement, I can't find a real satisfactory way of doing it.
My best approach so far is to provide an overload to the Html.ActionLink extension method that takes the permission to ask for, but there are going to be more complex scenarios, like hiding entire blocks of html or switching a textbox for a label+hidden.
Is there a better way to do this?
One example I can think of would be to invoke Html.RenderAction (link: http://msdn.microsoft.com/en-us/library/ee703490.aspx), and then pass the link you wish to use as a route value to the action method (it will appear as a parameter on your action). Because it's a RenderAction, you get to go back through the controller process and thus you can have it render whatever view or data you want depending on the user's state.
Example:
<% Html.RenderAction("Permissions" /* Controller */, "PermissionLink", new { Url = "Admin.aspx" }); %>
And your controller would have something like:
public ActionResult PermissionsLink (string url)
{
// Do Whatever kind of Authentication you want here, Session is available, etc
if(authenticated)
return View("Link");
else
return View("Blank");
}
I personally don't see anything wrong with this kind of conditional logic within the view. The logic is still all about presentation. You decide whether to show or hide, enable or disable, highlight etc. This is the view's job, not the controller's. As long as the view does not need to actually compute anything to come to its decision. The controller should be agnostic of the view's implementation as much as the other way around.
My approach would be this:
Make the controller do the actual logic of deciding the level of access that the user has
Pass this information to the view using (ie via the ViewModel) but in a way that is neutral to implementation details (ie "user is admin", "user is an author", etc)
Let the view draw itself appropriately using only the pre-compiled data that is provided by the controller.
This also has the benefit that the view itself can be removed and replaced without any effect on the controller.
We had this same issue. We ended up writing a lot of helper methods and in cases where lots of html output was required, we put them in partial views.
Would it not be simpler to create several views with various controls based on what values the user can change, then return the right view based on the user's access rights?
Views should be for presenting information only, there really shouldn't be any conditional logic contained in them, or at least a bare minimum.
I've always found when i get to the point where i'm finding it hard to grok a situation like yours, the best solution is always to go back to the controller and e-assess what i'm passing to the view in the first place.
By the time the View is called to do its job, all of the important 'decisions' should have already been made.
In complicated situations where are many conditions and rules I'm doing this that way:
ViewModel
public class ModelView
{
private IAuthorisationService { get; set; }
public bool CanShow
{
...
}
}
View:
<% if(Model.CanShow) { %>
<html>
<% } %>
what are your top lessons learned when starting asp.net mvc that you would highlight to someone starting out so they can avoid these mistakes?
Use Html.Encode() everywhere you print data, unless you have a very good reason to not do so, so you don't have to worry about XSS
Don't hardcode routes into your views or javascripts - they're going to change at some point, use Url.Action() instead
Don't be afraid of using partial views
MVC is no silver bullet, first evaluate if it's indeed the best tool of choice for solving your problem.
Don't forget the "Unit Tests" part of the pattern.
Try to always use a ViewModel to pass data between the Controller and the View.
You may think you don't need one, you can just pass your model around, but suddenly you need a list box with several options for editing a model, or displaying a message (not validation message) and you start adding items to the ViewData, with magic strings as keys, making the app harder to maintain.
There are also some security issues that you solve with a ViewModel.
For instance:
class user:
int id
string name
string email
string username
string password
Your view let's the user change his name and email and posts to the action
public ActionResult Edit(User user)
{
--persist data
}
Someone could tamper your form and post a new password and username and you will need to be very careful with the DefaultBinder behavior.
Now, if you use a ViewModel like:
class userEditViewModel:
int id
string name
string email
The problem is gone.
Whenever it is possible make your view typed
Avoid logic in your views
stay away from the HttpContext
Get Steve Sandersons Pro ASP.NET MVC Framework
Debug into the Sourcecode
If you make a Controller method with a different parameter name from id for a single parameter method, you have to make a new route. Just bite the bullet and use id (it doesn't care about the type) and explain it in the comments.
Makes sure you name your parameters with RedirectToAction :
return RedirectToAction("DonateToCharity", new { id = 1000 });
You lose your ViewData when you RedirectToAction.
Put javascript in seperate files, not into the view page
name of the controller :)
unit test Pattern
Don't use the Forms collection, use model binding.
Try not to use ViewData, create a ViewModel.
If you have a loop or an if in your View, write an HTML helper.
Kindness,
Dan
Don't let your controller become a fat one and do too much work. I've seen 1000+ line controllers in the past and it just becomes an absolute nightmare to understand what's going.
Utilise unit testing for your controllers to ensure that dependencies are kept under control and that your code is testable.
Don't get drawn into letting jQuery and fancy clientscript define the behaviour of your application, try and use it as sparingly as you can and let it enhance your application instead.
Use partial views and HTML helpers whenever possible to ensure that your Views do not become unwieldy and a maintenance nightmare.
Use a ViewModel whenever possible.
Use a dependency injection framework to handle your dependencies (MvcContrib has several controller factories, though it's simple enough to roll your own).
Use a different controller for every section of your site (e.g., Home, Account)
Learn how to use ViewData and TempData
Learn what's the use of RenderPartial