spring-boot-starter-parent 2.4.1
spring-boot-starter-thymeleaf
I have a bunch of constants that I want to be accessible everywhere.
For example I have organized some constants:
public class Constants {
public final static String LANG = "lang";
}
Here I have a param which I already use in two places: in LocaleResolver and in LocaleChangeInterceptor.
And I need on every page where I include this fragment:
<th:block th:fragment="top" !doctype html>
<div>
French
Английский
</div>
</th:block>
As we can see, this hardcoded twice. I'd like to avoid such cases.
Is it possible somehow? If a class with static methods is not a good practice, I can use whatever you suggest. Even if such class is preferrable for me, any elegant solution would be highly appreciated.
You can add a model attribute on a #ControllerAdvice so it becomes available automatically in all templates:
#ControllerAdvice
public class GlobalControllerAdvice {
#ModelAttribute("test")
public String getTest() {
return "some-value";
}
}
You can use test now in any template:
<div th:text="${test}"></div>
You can access static variable in Thymeleaf expressions.
<th:block th:fragment="top" !doctype html>
<div>
<a th:href="|?${T(Constants).LANG}=fr|">French</a>
<a th:href="|?${T(Constants).LANG}=en|">Английский</a>
</div>
</th:block>
(Where Constants is the full path to your class)
Related
We're working with ASP.Net MVC and Google Publisher Tags (GPT).
GPT requires that you create javascript "slots" in the <head> and some html+script that goes into the <body>. The key dependency here is that in both places is an id that must match. So the head will contain some Javascript that includes something like:
<head>
<script>
...
DefineSlot('div-gpt-ad-123456789-0', 'foo', 'bar')
...
</script></head>
and
<body>
...
<div id='div-gpt-ad-123456789-0'>
<script>
...
Display('div-gpt-ad-123456789-0')
...
</script>
</div>
...
How can we manage the dependencies between these 2 pieces code? The critical piece is that the id of both parts must match.
We want to use MVC to create these pieces of code dynamically. So in any view, partial or layout, I will be able to add a helper call that might look like:
#Html.CreateAd(size, "foo", "bar")
#Html.CreateAd can be called anywhere in a view, partial view, layout, or nested layout.
How do you use ASP.Net MVC to program the code that goes into <head>?
Any suggestions are appreciated. I'm just looking for direction, not a full blown solution.
Many Thanks.
You have a few different ways to do this.
You can add the id's to the ViewData or a base viewmodel.
Then OnActionExecuting or OnActionExecuted in a base controller or via actionfilters, you can add your data to whichever place you prefer to. If you need examples for this, please leave a comment on this answer.
Then, your helpers (one for each section) you can read from one of the 2 sources you decided on. I have gone both routes. If all of your pages are going to have an ad, then I would lean towards the base ViewModel. If it's more of a rare occurrence ViewData would be more appropriate.
To access the viewdata within an htmlHelper Extension method:
public static class HtmlExtension
{
public static MvcHtmlString RenderAdHead(this HtmlHelper h)
{
h.ViewContext.ViewData.Model // a test and cast here
h.ViewContext.ViewData["AdIdentifier"] // test for null and cast here
string tags = String.Empty;
//build up string to resemble your script/html tags using either of the
//sources above, so long as either source is not empty.
return new HtmlMvcString(tags);
}
}
And some code for a filter:
public class AdvertisingFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
List<String> idList = null; // list of string might not be adequate. depends on your implementation
//read advertising ids from your datastore or wherever you have.
filterContext.Controller.ViewData["advertisingFilter"] = idList;
}
}
The basecontroller is pretty much the same, instead you have the controllercontext directly. You just have to be sure all your controllers inherit from them.
I'm an experienced .NET programmer, but I'm new to this whole web programming thing. My ASP.NET MVC website has a global layout that includes some content (menu links at the top of the page) that I want to hide under conditions that are detected dynamically by controller code.
My inclination -- the simple approach that uses the tools I've learned about thus far -- is to shove a Boolean HideGlobal value into the ViewBag, and to put the global markup in _Layout.cshtml that I want to hide inside of an #if (ViewBag.HideGlobal){} block.
I just want to know if this is the "proper" way to do it, or is there some Razor magic that I should be using for reasons that are not yet evident to me?
I dislike using the view model of the action outside of the view returned by the action. Using base view model for this scenario feels very clunky.
I believe it's cleaner and more obvious to just use a separate (child) action that contains the logic for specifying how the global menu should be displayed. This action returns the global menu view. Call that action from your layout page.
Or you can create an action for the entire header where the menu's state is determined -- or do an if/else to render a partial view of the global menu.
The example below encapsulates the needs of a header/global menu and offers a future proof way of changing your header/menu with minimal effect on your code infrastructure (base view model).
~/Controllers/LayoutController.cs
public class LayoutController : Controller
{
[ChildActionOnly]
public ActionResult Header()
{
var model = new HeaderViewModel();
model.ShowGlobalMenu = ShowGobalMenu();
return View(model);
}
}
~/Views/Layout/Header.cshtml
#model HeaderViewModel
#{
Layout = "";
}
<header>
Home
#if(Model.ShowGlobalMenu)
{
<ul>
<li>Link</li>
<li>Link</li>
<li>Link</li>
<li>Link</li>
</ul>
}
</header>
~/Views/Shared/_Layout.cshtml
<html>
<body>
#Html.Action("Header", "Layout")
<p>Stuff</p>
</body>
</body>
What you've described (putting a bool into the ViewBag) will work fine. But personally, I like the strongly-typed model experience, so when I want to have UI logic like what you're describing in the master/layout page (which is pretty much always), I prefer to put those flags into a base model that my other models inherit from.
public class BaseModel
{
public bool HideGlobal { get; set; }
}
And at the top of the _Layout.cshtml page, I specify that I'm expecting a BaseModel:
#model Company.Project.BaseModel
Other views can, of course, require other model types, but if they're using this layout, those model types should derive from BaseModel.
Finally, when I want to check the flag, rather than having a mysterious, undocumented field in the ViewBag, I've got the lovely intellisense and feel-good strongly-typed member of the model:
#if (!Model.HideGlobal)
{
<div>...</div>
}
Edit: I should probably add that it's often nice to also have a base controller whose job it is to populate the fields in BaseModel. Mine usually look like this:
public class BaseController : Controller
{
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
var result = filterContext.Result as ViewResultBase;
if (result != null)
{
var baseModel = result.Model as BaseModel;
if (baseModel != null)
{
//Set HideGlobal and other BaseModel properties here
}
}
}
}
First answer here, be gentle :-)
This type of thing is usually based on user permissions which would be much better in an Action Filter instead of the base controller.
Yes you would have to use the ViewData or ViewBag but it's really not part of the view/model it's higher up in your _layout.cshtml
Action Filter
public class UserAccessAttribute : ActionFilterAttribute
{
public override void OnActionExecuting( ActionExecutingContext filterContext ) {
var hasAccessToMenu = /* Code */;
filterContext.Controller.ViewData["GlobalVisible"] = hasAccessToMenu;
}
}
View (_layout.cshtml)
#if(ViewData["GlobalVisible"])
{
<ul>
<li>Link</li>
<li>Link</li>
<li>Link</li>
<li>Link</li>
</ul>
}
This gives you the advantage of separating this logic out of the controller and because it's global that's important.
Also I've used ViewData in my example but using ViewBag is just as good.
Creating a site in MVC 3 and I have this code snippet that I use on several parts on the design. It's a designcode that creates a head for modules on the site.
Now wondering if this is the best way to call this code snippet? Should i use Helpers or is there a better way?
Today I do like this:
public static IHtmlString FrameModuleHeader(this HtmlHelper helper, int Type, string Headline)
{
StringBuilder html = new StringBuilder();
html.Append("<div class='module_top'>");
html.Append("<div class='module_top_left'></div>");
html.Append("<div class='module_top_middle_" + Type + "'><div class='module_top_headline'><h4>" + Headline + "</h4></div></div>");
html.Append("<div class='module_top_right'></div>");
html.Append("</div>");
return MvcHtmlString.Create(html.ToString());
}
And then call my HTML helpers in the view through:
#Html.FrameModuleHeader(1,"My headline")
Thanks!
/Mike
I would probably use a partial view or a display template that I would include instead of HTML helper because stuffing so much HTML in a C# code looks ugly.
So for example I would have a view model:
public class SomeViewModel
{
public int Type { get; set; }
public string Headline { get; set; }
}
and a partial:
#model AppName.Models.SomeViewModel
<div class="module_top">
<div class="module_top_left"></div>
<div class="module_top_middle_#(Model.Type)">
<div class="module_top_headline">
<h4>#Model.Headline</h4>
</div>
</div>
<div class="module_top_right"></div>
</div>
and then:
#Html.Partial("Foo", new SomeViewModel { Type = 1, Headline = "My headline" })
This of course doesn't mean that your HTML helper wouldn't work. It's just that normally HTML helpers should be used to generate small fragments of HTML and in this particular example this doesn't seem to be the case. Also you get HTML intellisense if used in a view which might aid you identify unclosed tags, not properly formatted HTML, ... whereas inside C# everything is one big magic string.
And one final remark about your HTML helper if you decide to use it: Make sure you HTML encode this Headline string before putting it inside the string builder or you might get bad XSS surprises.
There is really no recommended way. The way that you are doing it is very clean. So I see no reason to change it.
Another option is to use a Partial View. You can put it in /Views/Shared so it is available to everything. One reason this is nice, is it can be more easily edited without having to push out a new code base. If this output never changes, then perhaps that's not necessary.
You could of course cure the code bloat caused by divitus, by removing the unnecessary html tags.
public static IHtmlString FrameModuleHeader(this HtmlHelper helper, int Type, string Headline)
{
StringBuilder html = new StringBuilder();
html.Append("<div class='module_top'>");
html.Append("<h4 class='module_top_middle_" + Type + "'>" + HtmlEncode(Headline) + "</h4>");
html.Append("</div>");
return MvcHtmlString.Create(html.ToString());
}
Suppose I have a Site.Master page.
I want to display something like the version number of the application (lets assume it is available in the the BaseController as a string) in the Site.Master.
What would be the best way to do that? I know one way would be to have a Base view model class which would contain the version element. But any better way?
Hope the question is valid.
Thnx,
Karan
I would write a helper method for this:
public static class HtmlExtensions
{
public static MvcHtmlString Version(this HtmlHelper htmlHelper)
{
string version = FetchVersionFromSomewhere();
return MvcHtmlString.Create(version);
}
}
And then in your master:
<%: Html.Version() %>
For something like an assembly version number it might be Ok to have it as a static property on the BaseController, in which case you could reference it directly from any code that needed it.
<%# Import Namespace="ControllerNamespace"%>
<%=BaseController.MyProperty %>
Im a newbie to MVC and trying to use the web navigator class in my MVC application. http://polymorphicpodcast.com/shows/webnavigator/
The class enables us to have stongly typed urls in a central class file.
I have 3 questions:
Is this the best way of storing
strongly typed urls in MVC or does
MVC has some special helper methods
which does the same thing.
What is the best place for the class
file to go in e.g VIEW/MODEL or
CONTROLLER
Below is a sample of the Navigation
class which returns a proper url for
an image file.
Instead of passing the UrlHelper object to the I want to
run this
class in the System.Web.Mvc.ViewPage
context (current context). What is
the best way of doing this.
Sample navigator class for images:
using System.Web;
using System.Web.Mvc;
public class IMG
{
public string AjaxLoading(UrlHelper urlHelper)
{
return urlHelper.Content("~/Images/loading2.gif");
}
}
To answer your questions:
There are many ways to create navigation links for you ASP.NET MVC, whatever works for you is the best.
Most would answer that class files should be placed in Model folder. I found it more meaningful to place ViewModel classes in a separate folder and classes used throughout the application (business logic/meat of the application) in a separate file.
What you're trying to accomplish seems like a job for extension methods. Here is a good tutorial: http://www.dotnetcurry.com/ShowArticle.aspx?ID=406&AspxAutoDetectCookieSupport=1
What you're doing is on the right track, however, you need to create static classes and static functions/methods for this to work properly.
http://msdn.microsoft.com/en-us/library/bb383977.aspx has some general info on extension methods.
One quick note: To allow usage of all the extension methods you create, you'll need to reference the class/namespace that you placed them in.
There are two methods of doing this:
Assuming you've placed your extension methods in MvcApplication1.MyExtensionMethods, Add the following after the
<page>
<namespaces>
tag in your application's web.config (Not the View's web.config file)
<add namespace="MvcApplication1.MyExtensionMethods"/>
Doing so will allow the extension methods to be used in all of your views (.aspx/.ascx) files.
Place
<%# Import Namespace="MvcApplication1.MyExtensionMethods" %>
at the top of your .aspx/.ascx files. You would need to do this for every file you need to use the extension methods on (not efficient)
The following is what I implement and it has served me well thus far.
NavigationLink.cs
public class NavigationLink
{
string Text {get; set;}
RouteValueDictionary Routes {get; set;}
string RouteName {get; set;}
}
NavigationLink.ascx (Place in shared folder for easy access in entire application)
(Note: I wrap the links in < li> < /li> tags because I use lists for all my navigation controls. You can then place these links in any type of list, allowing freedom with the list's class/style.)
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<NavigationLink>>">
<% foreach(NavigationLink n in Model){ %>
<li>
<a href="<%= string.IsNullOrEmpty(n.RouteName) ? Url.RouteUrl(Routes) : Url.RouteUrl(RouteName) %>">
<%= n.Text %>
</a>
</li>
<% } %>
Menus.cs (Some examples)
public static Menus
{
public static List<NavigationLink> MainMenu()
{
List<NavigationLink> links = new List<NavigationLink>();
links.Add(new NavigationLink{
Text = "Home",
Routes = new RouteValueDictionary(new { action="Index", controller="Home" })
});
return links;
}
public static List<NavigationLink> UserMenu()
{
List<NavigationLink> links = new List<NavigationLink>();
links.Add(new NavigationLink{
Text = "Messages",
Routes = new RouteValueDictionary(new { action="Messages", controller="UserCP" })
});
links.Add(new NavigationLink{
Text = "Account",
Routes = new RouteValueDictionary(new { action="Account", controller="UserCP" })
});
return links;
}
}
Now that you have everything setup, calling these functions is simple:
In your view file (.aspx/.ascx)
<ul class="horizontal_menu">
<% Html.RenderPartial("NavigationLink", MyMvcApplication1.Menus.MainMenu()) %>
</ul>
Having it setup like this allows for different partial views to be created to render out lists of navigation links in different ways and would require that you only build the partial and call it.