I'm looking for the equivalent of an
#if DEBUG
//view elements to show just for debug builds
#if
for views in MVC3/Razor. What's the idiomatic method for implementing this type of a setup?
That's too messy IMO. Views should be dumb, and focused on rendering HTML, not making build-based decisions.
Set properties in your view model if debug is configured, and render them out in the View.
If the properties are null (e.g non-debug), nothing will get rendered.
You can use HttpContext.Current.IsDebuggingEnabled, it checks debug value in the web.config file.
For instance:
#if(HttpContext.Current.IsDebuggingEnabled) {
//view elements to show just for debug builds
}
The other option is use write your own HttpHelper extension
public static class HtmlHelperExtensions
{
public static bool IsDebug(this HtmlHelper helper)
{
#if DEBUG
return true;
#else
return false;
#endif
}
}
Then in your Razor code you can use it as:
#if (Html.IsDebug())
{
//view elements to show just for debug builds
}
Don't think you can do that in Razor as it doesn't compile the same way as C# code does.
So I'd say the best way to do it would be to do it in your controller and add it to a value in your model.
Edit: Here's some more info. The person here is suggesting an extension method that loads the appropriate code whether it's in debug or not: asp.mvc view enteres #IF DEBUG in release configuration
Since you haven't told us what you'd like to do, i can't give you any 'code' answers.
Preprocessor directives (#if) are a language feature C#, which you can enter by using a razor code block (#{}) and then use a explicit delimited transition (<text></text>) or an explicit line transition (#:) to conditionally add html like this:
#{
#if DEBUG
{
#:<div>Debug Mode</div>
}
#else
{
<text>
<div>
Release Mode
</div>
</text>
}
#endif
}
See Also: Preprocessor directives in Razor
Related
I'm trying to set a master layout.cshtml page that works consistently for all page except for one or two (typically login and logout). In my layout I'd like to display some elements that I want to not display for these special pages.
I've seen partial views and sections and they all seem to work "backwards" to the way I want - in this case I want the default to be 'display all the elements', but for special pages I want to be able to turn an element off.
I've seen previous code that uses PageData to pass a variable to the layout (which seemed very useful as I could slap a bool in the relevant pages and check it in the layout) but this seems to have been removed. Are there any other ways that work without involving the controller or updating every page to display the bits I want hidden on just 1 page?
There's a number of different ways you can achieve this. If you want to simply "turn off" an area of the page, the simplest approach is probably with ViewData. Something like:
_Layout.cshtml
#if (ViewData["ShowThis"] as bool? ?? true)
{
<!-- HTML here -->
}
This will cause it to default to true (show the HTML) if the ViewData key is not defined, so then in your views where you want to disable it, you'd just need to define it as false:
SomeView.cshtml
#{
ViewData["ShowThis"] = false;
}
Alternatively, you can use sections. This would give you the ability to optionally replace the HTML with something else.
_Layout.cshtml
#if (!IsSectionDefined("Foo"))
{
<!-- HTML here -->
}
else
{
#RenderSection("Foo", required: false)
}
Then, in your view, you simply define the section. If you want to display nothing, you can just define it empty:
SomeView.cshtml
#section Foo {}
Or you can can actually put something in there to replace the area with:
#section Foo
{
<!-- alternate HTML here -->
}
You can try this to pass ViewData in _Layout page in asp.net mvc
public class DynamicMenuAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
var result = filterContext.Result as ViewResult;
result.ViewData["key"] = "Hello Bangladesh";
}
}
Add Dependency Injection into Startup.cs file
services.AddMvc(
config =>
{
config.Filters.Add(new DynamicMenuAttribute());
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
Now you can you ViewData["key"] in _Layout.cshtml
Could someone explain me why I get blank screen with printed string "#extends('layouts.default')" if I request page normally (not ajax)?
#if(!Request::ajax())
#extends('layouts.default')
#section('content')
#endif
Test
#if(!Request::ajax())
#stop
#endif
I'm trying to solve problem with Ajax, I don't want to create 2 templates for each request type and also I do want to use blade templates, so using controller layouts doesn't work for me. How can I do it in blade template? I was looking at this Laravel: how to render only one section of a template?
By the way. If I request it with ajax it works like it should.
Yes #extends has to be on line 1.
And I found solution for PJAX. At the beginning I was not sure this could solve my problem but it did. Don't know why I was afraid to lose blade functionality if you actually can't lose it this way. If someone is using PJAX and needs to use one template with and without layout this could be your solution:
protected $layout = 'layouts.default';
public function index()
{
if(Request::header('X-PJAX'))
{
return $view = View::make('home.index')
->with('title', 'index');
}
else
{
$this->layout->title = 'index';
$this->layout->content = View::make('home.index');
}
}
Try moving #extends to line 1 and you will see the blade template will render properly.
As for solving the ajax problem, I think it's better if you move the logic back to your controller.
Example:
…
if ( Request::ajax() )
{
return Response::eloquent($books);
} else {
return View::make('book.index')->with('books', $books);
}
…
Take a look at this thread for more info: http://forums.laravel.io/viewtopic.php?id=2508
You can still run your condition short handed in the fist line like so
#extends((Request::ajax())?"layout1":"layout2")
Is there any better way as compared to #if #else Just to changes few attributes (like disabled, value, class etc) of html element in cshtml page built with Razor view?
you can use inline conditional statements as well:
<input type="text" value="#(true == true ? "one value" : "another value")" />
MVC 4/Razor V2 will have some improvements:
Conditional attribute rendering
If you have an attribute that might be null, in the past you've needed to do a null check to avoid writing out an empty attribute, like this:
<div #{if (myClass != null) { <text>class="#myClass"</text> } }>Content</div>
Now Razor is able to handle that automatically, so you can just write out the attribute. If it's null, the attribute isn't written:
<div class="#myClass">Content</div>
So if #myClass is null, the output is just this:
<div>Content</div>
From Jon Galloway's blog.
You can pass them in the ViewModel as true/false values or string values.
For example -
anything that needs to be dynamically set to be enabled or disabled - you can create a bool
any html class that is a string set on runtime, can be passed as a string variable in your view model.
etc.
That would clean up your razor views of all the #if else statements
I don't know why you think there is any performance flaw with razor #if but you can use Your ViewModel or ViewBag for this:
<input type="text" value="#(ViewBag.TheValue)" />
But for best practice, Don't mix server side logic with the presentation. "Separate of concerns"
Update(What did I mean by "separate of concerns"):
It mean the View should contains as much as it can, HTML markups and HTML helpers(which just help you write markup) only. Don't put logic inside the View. don't reuse the same view if it makes you put code inside of it.
In my MVC project has 2 Areas which is Admin and Client and I need to dynamic config Layout for Client side, In _ViewStart (in client) file will set layout for all of client page.
Layout = "~/Views/Shared/_Layout.cshtml";
So if we need to change client layout we can change Layout path of cshtml file in _ViewStart file right? I cant find how to change inside ViewStart file or Is there another solution in this case.
Thanks for your Help :)
Remember that anything within the #{ ... } is treated as code. So, it should be a simple matter of placing a condition in there to change how it's inherited:
#{
Layout = "~/Views/Shared/_Layout.cshtml";
if (User.Current.IsAuthenticated) {
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
}
Though you're probaby better off looking at Themes (and have an admin/user theme). Alternatively, you can make your _Layout.cshtml smarter and have it handle the different views based on conditions as well.
See Also: MVC3 Razor - Is there a way to change the Layout depending on browser request?
Your question has not enough information to give you a complete code sample.
But basicly you can do this
if (InsertIsAdminLogicHere) {
Layout = "~/Views/Shared/_AdminLayout.cshtml";
} else {
Layout = "~/Views/Shared/_Layout.cshtml";
}
If you show us how you determine admin or not, we can provide more help.
hope this helps
You can take advantage of Nested Layouts. Create a base controller and drive all controllers from this one.
public class ControllerBase : Controller
{
public ControllerBase()
{
ViewBag.Theme = "~/Views/Shared/Default/Views/_Layout.cshtml";
}
}
public class HomeController : ControllerBase
{
public ActionResult Index()
{
return View();
}
}
_ViewStart.cshtml (don't make any changes in this file)
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
Views/Shared/_Layout.cshtml
This is default Layout file of Asp.NET Mvc. Empty this and replace these lines.
#{
Layout = ViewBag.Theme;
}
#RenderBody()
You can Modify this way for Areas. You can fetch active template info in BaseController from database or wherever you want.
Btw, if you want to put your views outside of ~/Views folder search for ThemeableRazorViewEngine
in Views/_ViewStart.cshtml
#{
object multiTenant;
if (!Request.GetOwinContext().Environment.TryGetValue("MultiTenant", out multiTenant))
{
throw new ApplicationException("Could not find tenant");
}
Layout = "~/Views/"+ ((Tenant)multiTenant).Name + "/Shared/_Layout.cshtml";
}
<ul>
#{int i=0;}
#foreach (var entry in Model.PhoneNumberEntries)
{
<li>
<span>#entry.PhoneNumber<span>
#Html.TextBoxFor(m=>Model.PhoneNumberEntries[i].PhoneNumber)</li>
i++;
}
</ul>
This seems a little too verbose for me... is there way to get around creating the counter with having to resort to the standard for loop?
This seems a little too verbose for me... is there way to get around
creating the counter with having to resort to the standard for loop?
Yeah it seems too verbose to me as well. Even the loop seems too verbose as you don't need it if you use editor templates.
<ul>
#Html.EditorFor(x => x.PhoneNumberEntries)
</ul>
and then obviously you would define a custom editor template that will automatically be rendered for each element of the PhoneNumberEntries collection (~/Views/Shared/EditorTemplates/PhoneNumberEntry.cshtml):
#model PhoneNumberEntry
<li>
<span>#Model.PhoneNumber</span>
#Html.EditorFor(m => m.PhoneNumber)
</li>
You don't even need to write loops as templates work by convention.
Notice that the name and the location of the editor template is important. It should be located either inside ~/Views/Shared/EditorTemplates if you want to share this template between views belonging to different controllers in your application and it is the location where ASP.NET MVC will first look for it. Or you could also put it inside ~/Views/XXX/EditorTemplates where XXX is the name of the current controller. Then name of the editor template must be the name of the type used as agrument for the collection property.
So if you had no your main view model:
public IEnumerable<FooBarViewModel> FooBars { get; set; }
the name of the corresponding template would be FooBarViewModel.cshtml and obviously it will be strongly typed to FooBarViewModel.
Try this:
<ul>
#for (int i=0; i++; i < Model.PhoneNumberEntries.Count)
{
<li>#Html.TextBoxFor(m=>Model.PhoneNumberEntries[i].PhoneNumber)</li>
}
</ul>