In a Grails 3.3 application, I'm trying to create a menu configuration and I'd like to use application.groovy for that.
application.groovy:
mainmenuconfig {
menuitem_1 {
id = 'menuitem-1'
name='Home'
sub=null
}
menuitem_2 {
id ='menuitem-2'
name='Stammdaten'
sub = submenuitem_2 {
menuitem_2_1 {
id = 'menuitem-2-1'
name ='Stamm-A'
sub=null
}
}}
}
Retrieving the config via grailsApplication.config.get('mainmenuconfig') would give me the following:
[menuitem_1:[id:menuitem-1, name:Home, sub:null], menuitem_2:[id:menuitem-2, name:Stammdaten, submenuitem_2:[menuitem_2_1:[id:menuitem-2-1, name:Stamm-A]], sub:null]]
If I look at getClass() it says it's a org.grails.config.NavigableMap
Now, for my understanding, generating an <UL> ... <LI> tree should be done inside the View layer. For iterating through that structure, I will need recursion, because it can be n levels deep.
If I'm looking from main.gsp, I know the spot where I want to insert the menu tree, but how do I bring the data there and where do I do that recursion? Will I need a menu controller that is being called from the GSP? IMHO the GSP shouldn't do such calls. And on the other side, no controller should generate <UL> ... <LI> trees. I need the glue piece.
This sounds like a good use for an interceptor. You can have it add to your model after every action is finished. Then your view will have access to menu in its model and can build out the menu. It's fine to do recursion in your view if it's for display purposes.
class NavigationMenuInterceptor {
NavigationMenuInterceptor() {
matchAll()
}
boolean after() {
model.menu = grailsApplication.config.getProperty('mainmenuconfig', Map)
true
}
}
To render your menu, you can use a template that recursively renders itself. Depending on your desired HTML output it might be something like below.
In the main body:
<ul>
<g:each in="${menu}" var="menuitem">
<g:render template="menuitem" model="[menuitem: menuitem]"/>
</g:each>
</ul>
In your template _menuitem.gsp:
<li id="${menuitem.id}">
${menuitem.name}
</li>
<g:if test="${menuitem.sub}">
<li>
<ul>
<g:render template="menuitem" model="[menuitem: menuitem.sub]"/>
</ul>
</li>
</g:if>
Related
I am building a sidebar navigation dynamically and I have the following issue:
Option 1 // add class active
Option 1.1 // add class active
Option 1.1.1 // add class active
Option 1.1.1.1 // add class active
option 1.1.1.1.1 // selected link id = 7 (this is guid in real app)
Option 1.1.2
Option 1.2
Option 2
Option 3
Basically the navigation is in the following format:
<ul>
<li>
link
</li>
<li>
link
<ul>
<li>
link
</li>
</ul>
</li>
</ul>
And here is what I try to do recursively.
#BuildNavigation(Model)
#helper BuildNavigation(IEnumerable<Menu> menu)
{
foreach (Menu item in menu)
{
<li>
#item.Title
#if (item.Items.Any())
{
<ul class="nav sub-menu">
#BuildNavigation(item.Items)
</ul>
}
</li>
}
}
This works fine, the sidebar is built. But how can I add the active classes to the <li> based on the current selection?
I can add a property to the Menu class bool IsActive and then within the view I can check if that menu is active but how can I add the active class to all parents <li> of that selected link?
I am doing a two level menu and I want to show some entries if the user has certain permissions.
I dont want to show the first level of the menu if the user doesn't have permissions to any other submenu because it would display an empty list of results.
I'll put some code to explain it better. This could be an example of the menu:
<ul>
<shiro:hasPermission permission="foo">
<li> foo</li>
</shiro:hasPermission>
<shiro:hasPermission permission="bar">
<li> bar</li>
</shiro:hasPermission>
</ul>
Is it there a way to wrap the <ul> somehow to ask if the user has foo or bar permission?
I'm using Grails 2.4.4 and Shiro plugin 1.2.1
UPDATE WITH SOLUTION:
Finally I've created a new tagLib that extends the ShiroTagLib and has the desired function:
class ShiroTagLib extends org.apache.shiro.grails.ShiroTagLib {
/**
* This tag only writes its body to the output if the current user
* have any of the given permissions provided in a separated comma String.
*/
def hasAnyPermission = { attrs, body ->
def permissions = attrs["permissions"]
if (!permissions){
throwTagError("Tag [hasAnyPermission] must have [permissions] attribute")
}
def permissionsList = permissions.split(',')
for (permission in permissionsList) {
if (checkPermission([permission: permission], "hasPermission")) {
// Output the body text.
out << body()
return out
}
}
}
}
with this you can do <shiro:hasAnyPermission permissions="foo,bar">
There are several ways to approach this but the hasAnyRole tag from the Shiro plugin is likely the easiest way.
<shiro:hasAnyRole in="foo,bar">
<ul>
<shiro:hasPermission permission="foo">
<li> foo</li>
</shiro:hasPermission>
<shiro:hasPermission permission="bar">
<li> bar</li>
</shiro:hasPermission>
</ul>
</shiro:hasAnyRole>
You can take a look at the source code for the tag library for more information.
Update
Based on the comment provided there is another option which is based on permissions.
<g:set var="hasSomePermission" value="${false}" />
<shiro:hasPermission permission="foo">
<g:set var="hasSomePermission" value="${true}" />
</shiro:hasPermission>
<shiro:hasPermission permission="bar">
<g:set var="hasSomePermission" value="${true}" />
</shiro:hasPermission>
...
<g:if test="${hasSomePermission}">
...
</g:if>
The Shiro plugin doesn't provide a logical OR tag for permissions like it does with roles, so the above is another approach. Using this same idea you could DRY it up with your own tag library.
Am trying to build a project using MVC 4 and Razor. Am having trouble understand partial views. I have a list of my domain objects that I iterate through and display it in a list box (each row being clickable). I split this into main view and a partial view which renders the domain object which works fine. Inside my partial view I want to make each item clickable and upon click, I want to create a new partial view displaying details about the domain object.
Here is what I have
My main view looks like this
<div class="panely">
<div class="list-group">
#{ Html.RenderPartial("DomainObjectsPartial");}
</div>
</div>
My partial view looks like this
<div class="list">
#foreach (var x in #Model)
{
<a href="#Html.Partial("DomainObjectPartial")" class="list-item">
<em>#x.Name</em>
</a>
}
</div>
I have a view named DomainObjectPartial, which has nothing but a small div with hello.
When the user clicks on a domain object, I expect the partial view to be rendered within the main view, but instead I get a error saying
A potentially dangerous Request.Path value was detected from the
client (<).
And when I look at my URL, the contents of the partial view are contained within it like
http://localhost/<div>hello</div>
I dont want to be redirected to another URL. I just want the partial view to be displayed below the list. Can anyone explain to me what am i missing or not understanding?
I guess you wanted to use AJAX:
<div class="list">
#foreach (var x in Model)
{
<a href="#Url.Action("Index", "Items", new { id = x.Id })" class="ajax-link">
<em>#x.Name</em>
</a>
}
</div>
and then you will obviously have a controller action which will render this partial:
public class ItemsController: Controller
{
public ActionResult Index(string id)
{
// go get the specific item from your database using the id and pass it to
// the partial view
var viewModel = ...
return Partialview("DomainObjectPartial", viewModel);
}
}
and the last part is to AJAXify this anchor:
$(function() {
$('.ajax-link').on('click', function() {
// Send an AJAX call to the server endpoint pointed by the href attribute
// of the anchor and inject the results of this endpoint execution inside a
// DOM element with id="result"
$('#result').load(this.href);
// By returning false you are canceling the default action of the
// anchor click and prevent the browser to redirect to the url pointed
// by the href property. This would leave enough time for your AJAX request
// to execute and return the results.
return false;
});
});
and you will obviously need a DOM element with id="result" somewhere on your page to harbor the results of the AJAX call:
<div id="result"></div>
I've got a controller, which is getting a list of objects associated with a user and passing it to the view using the render method, like so:
class HomeController {
def index() {
def postList = user.posts
postList.each{ it ->
println "Title: " + it.title
}
render(view: "/index", model: [returnPosts: postList])
}
In my view I have the following code:
<h2>Recent Posts:</h2>
<ul>
<g:each var="post" in="${returnPosts}">
<li><g:link controller="post" action="show" id="${post.id}">${post.title}</g:link></li>
</g:each>
</ul>
Now, in my controller, I've put in the 'println' statements to ensure that the list is not null, and sure enough it's not. However, when I open the page, the g:each tag doesn't run even once as if the "returnPosts" variable is null, even though in the controller the println statments show that it's not.
I've been going crazy trying to figure this out, can anyone see any reason why the view wouldn't have access to this variable?
You seem to have a controller attribute called 'post' and also your g.each var is 'post'. Potential problem there?
There are couple of things you could do to fix this problem. Firstly, if your view is called index.gsp then you don't need to explicitly call render, returning a list of model objects would do. Your code will look like this:
def index() {
def postList = user.posts
postList.each{ it ->
println "Title: " + it.title
}
[returnPosts: postList]
}
Another thing you could also do is to explicitly import the model object that you are accessing in the view. Not that it matters, but you could try using a name other than post, given that it is a common word. So your gsp will look like this :
<%# page import="com.blah.post" %>
<h2>Recent Posts:</h2>
<ul>
<g:each in="${returnPosts}" var="post">
<li><g:link controller="post" action="show" id="${post.id}">${post.title}</g:link></li>
</g:each>
</ul>
Ok I have a menu system with a menu (Dynamicly Generated from a datavbase field) I want to include this menu system on several views - All of which use differant controllers and models.
<ul>
<li>All</li>
<%
foreach (var curCat in Model.CategoryList)
{
%>
<li><%= Html.Encode(curCat.Category1)%></li>
<%
}
%>
</ul>
Whast the best way to achieve this? Do i need to just pass the Categories model with every other model so that I can do the RenderPartial("Name",Model) synatx?
There are two different ways to accomplish this. You can include the Categories in every model or you can store the Categories in the ViewDataDictionary and retrieve them from there. Normally, I would want to extend the model with data so that I can use it in a strongly-typed way, but in this case -- since the data is ubiquitous -- I would probably go with ViewData and use a base controller to populate it (likely in OnActionExecuted) so that it is always available. In my partial view I would cast the ViewData item to a strongly-typed object and use it from there.
The reason I would do this is to keep my models clean for the actual view, which doesn't need to know about the data for the menu. To me this seems like a reasonable exception to the usual route of creating a view-specific model.
<% var categories = ViewData["categories"] as IEnumerable<Category>; %>
<ul>
<li>All</li>
<%
foreach (var curCat in categories)
{
%>
<li><%= Html.Encode(curCat.Category1)%></li>
<%
}
%>
</ul>
Third way--check out the MVC Futures on codeplex, specifically the Html.RenderAction method. Then you can create a controller that just ouputs the menu and let it handle it's business. No need to pollute ViewData.