How to pass a current loop value to class method in Tapestry? - loops

I want to dynamically show available menus of links to pages depending on which type of user is logged in using Tapestry.
Part of my code in Layout.tml looks like this:
<div class="header">
<t:if t:test="userLoggedIn">
<div class="menu">
<ul>
<t:loop t:type="loop" source="pageNames" value="pageName" class="prop:classForPageName">
<t:if t:test="isUserAllowedOnPage('pageName')">
<li>
<t:pagelink page="prop:pageName.name">${pageName.displayName}</t:pagelink>
</li>
</t:if>
</t:loop>
</ul>
</div>
</t:if>
<div style="clear:both;"></div>
</div>
In my Layout.java I have a following method:
public boolean isUserAllowedOnPage(String pageName) {
// My logic here, returns either true or false
}
The problem is, I do not know how to pass the actual page name parameter to isUserAllowedOnPage(String pageName) method, because with the following line of tml code
"isUserAllowedOnPage('pageName')"
I pass an actual string, "pageName" instead of one of the desired values (for example, "Index", "About", "Contacts"...).

Your loop specifies value="pageName" which means that tapestry will update the pageName property in your page each time it iterates through the loop. So, you don't need to pass it to a method since it's already set each time you invoke the method.
You could just do the following:
TML
<t:loop source="pageNames" value="pageName">
<t:if t:test="userAllowedOnPage">
...
</t:if>
</t:loop>
Java
#Property
private List<String> pageNames;
#Property
private String pageName;
...
public boolean isUserAllowedOnPage() {
// some calculation based on pageName
}

You can pass value to method without quotes as if you write this expression in java code:
<t:if t:test="isUserAllowedOnPage(pageName)">
</t:if>
Or:
<t:if t:test="isUserAllowedOnPage(getPageName())">
</t:if>

Related

ng-class not working with ng-repeat

I have a situation where I have to add class according to the condition and the ng-class is working according to it even the condition in the ng-class is true.
<ul id="" class="clowd_wall" dnd-list="vm.cardData[columns.id].data"
dnd-drop="vm.callback(item,{targetList: vm.cardData[columns.id].data, targetIndex: index, event: event,item:item,type:'folder',eventType:'sort','root':'folder',current_parent:'folder'})" ng-model="vm.cardData[columns.id].data">
<div class="emptyCol" ng-if="vm.cardData[columns.id].data.length==0">Empty</div>
<li class="dndPlaceholder"></li>
<li class="cont____item" ng-repeat="card in vm.cardData[columns.id].data | orderBy:vm.sort" dnd-draggable="card"
dnd-effect-allowed="move"
dnd-allowed-types="card.allowType"
dnd-moved="vm.cardData[columns.id].data.splice($index, 1)"
dnd-selected="vm.tree.selected = card" ng-class="{emptyCard:card.data.length==0,zoomin:vm.zoomin=='zoomin',emptyCard:!card.data}">
<div class="item" style="height:79%">
<ng-include ng-init = "root = columns.id" src="'app/partials/card.html'"></ng-include>
</div>
</li>
</ul>
ng-class="{'emptyCard': (!card.data || !vm.cardData[columns.id].data.length),'zoomin':(vm.zoomin=='zoomin')}">
Seems like you want to use vm.cardData[columns.id].data.length instead of card.data.length
Your question is not clear as don't know what card.data will contain and ".data" will be present for each iteration
If it is array then this will work "card.data.length" and if there is no "data" key in "card" then ".length" will through error i.e. if card.data itself undefined then it will not have "length" property.
Try to add condition in ng-class one by one then you will be able to figure out which condition is causing problem.
Made some small change
ng-class="{emptyCard: card.data.length==0 || !card.data,zoomin: vm.zoomin=='zoomin'}"
If you have multiple expression, try old fashioned, if it looks best:
Controller:
$scope.getcardClass = function (objCard, strZoomin) {
if (!card.data) {
return 'emptyCard';
} else if (strZoomin =='zoomin') {
return 'zoomin';
} else if (card.data.length == 0) {
return 'emptyCard';
}
};
HTML:
ng-class="vm.getcardClass(card, vm.zoomin)"
NOTE: Replace vm with your controller object.

Dynamic checkbox from list with Thymeleaf

I try to save the values from dynamically created checkboxes:
<div>
<ul>
<li th:each="item, stat : *{users}">
<input type="checkbox" th:field="*{users[__${stat.index}__]}" th:value="${item}" />
<label th:text="${item}"></label>
</li>
</ul>
</div>
The controller provides the String items as follwing:
public List<String> getUsers() {
return users;
}
And the setter for the Strings is:
public void setUsers(final String[] users) {
for (final String string : users) {
System.out.println(string);
}
}
The values are correct shown in the html page. But when i click save button, and the setter is called, the values are empty. What can i do, where is the problem?
Any help would appreciate.
Please check out section about handlin multi-value checkboxes in Tutorial: Thymeleaf + Spring.
You should provide some model attribute (of type List<String>) containing all users possible to select. Let's call it selectableUsers.
Then it can collaborate with your form-backing bean (that one containing users) in a following manner:
<div>
<ul>
<li th:each="item : ${selectableUsers}">
<input type="checkbox" th:field="*{users}" th:value="${item}" />
<label th:for="${#ids.prev('users')}" th:text="${item}"></label>
</li>
</ul>
</div>
Note I think that getter and setter for a field should handle the same type, but they don't (getter returns List<String> however setter consumes String[])
What you are trying to do looks logical, but it does not work that way.
If you did not get it resolved you can do this instead:
In relevant method of your controller you can add list of titles for your checkboxes:
List<String> allUsers = Arrays.asList("abc","xyz"); // OR generate list dynamically
model.addAttribute("selectableUsers", allUsers);
Or add it to ModelAndView if that is what you are using.
Change your html to what was suggested by #Jakub Ch.
Change your getter and setter methods as follows:
private String users;
...
public String getUsers() {
return this.users;
}
public void setUsers(String users) {
this.users = users;
}
Then 'users' field will contain comma separated String values or their id numbers ( depending on how you set it up) indicating selected checkboxes. Then you can convert String values to array using code like below or if id numbers are stored get String values from your ArrayList.
public List<String> getStrings() {
return Arrays.asList(strings.split(","));
}
Hope it helps.

Can't pass ViewBag data to AngularJS

I have a controller:
public ActionResult Edit(int id) {
ViewBag.IsComplete = false;
return View(dbContext.Users.Where(user => user.Id == id))
}
and the corresponding view:
#model User
#{
ViewBag.Title = "Edit User";
Layout = "~/Views/Shared/_PopupLayout.cshtml";
}
<p>#ViewBag.IsComplete</p>
<div ng-init="init(isComplete: #ViewBag.IsComplete)" />
The first instance of ViewBag.IsComplete (in the <p> tag) emits the correct value. The second emits null. Can anyone tell me what is going on here and how to fix it so the second #ViewBag.IsComplete emits the same as the first?
Use #Html.Raw(ViewBag.IsComplete) in your ng-init binding.
Use #Html.Raw which returns html markup which is not encoded.
<div ng-init="init(isComplete: #Html.Raw(ViewBag.IsComplete))" />
You need to convert the bool value to string, you can either use #Html.Raw() as suggested in other answers, or just do #ViewBag.IsComplete.ToString().
But, when you convert a bool value to string in C#, it will be converted to either "True" or "False", but Javascript doesn't recognize those as bool values. So, you'll have to convert it to lowercase as well. Here's what you need to do:
<div ng-init="init(isComplete: #ViewBag.IsComplete.ToString().ToLower())" />
In concept:
<div ng-init="init(isComplete: '#ViewBag.IsString')" />
Note the ViewBag.IsString, is replaced directly in the Html,
So if it's a string ViewBag.IsString= "Mystring"
html is:
<div ng-init="init(isComplete: 'Mystring')" />
in your case :
<div ng-init="init(isComplete: true)" /> //might give error
try : <div ng-init="init(#ViewBag.IsComplete)" />
might not work since it replaces it with true, else pass string or json

ng-repeat doesn't seem to be working

I am trying to pull all items from my array called 'collections'. When I input the call in my html I see only the complete code for the first item in the array on my page. I am trying to only pull in the 'edm.preview' which is the image of the item. I am using Angular Firebase and an api called Europeana. My app allows the user to search and pick which images they like and save them to that specific user.
here is the js:
$scope.users = fbutil.syncArray('users');
$scope.users.currentUser.collections = fbutil.syncArray('collections');
$scope.addSomething = function (something) {
var ref = fbutil.ref('users');
ref.child($rootScope.currentUser.uid).child('collections').push(something);
}
$scope.addSomething = function(item) {
if( newColItem ) {
// push a message to the end of the array
$scope.collections.$add(newColItem)
// display any errors
.catch(alert);
}
};
and the html:
<ul id="collections" ng-repeat="item in collections">
<li ng-repeat="item in collections">{{item.edmPreview}}</li>
</ul>
First, remove the outer ng-repeat. You want to only add the ng-repeat directive to the element which is being repeated, in this case <li>.
Second, from your AngularJS code, it looks like you want to loop over users.currentUser.collections and not just collections:
<ul id="collections">
<li ng-repeat="item in users.currentUser.collections">{{item.edmPreview}}</li>
</ul>
And third, you're defining the function $scope.addSomething twice in your JavaScript code. Right now, the second function definition (which, incidentally, should be changed to update $scope.users.currentUser.collections as well) will completely replace the first.

Mustache - How to detect array is not empty?

I want to implement the following logic with Mustache:
{{#if users.length > 0}}
<ul>
{{#users}}
<li>{{.}}</li>
{{/users}}
</ul>
{{/if}}
// eg. data = { users: ['Tom', 'Jerry'] }
Should I modify the users structure to meet the need? For example:
{{#hasUsers}}
<ul>
{{#users}}
<li>{{.}}</li>
{{/users}}
</ul>
{{/hasUsers}}
// eg. data = { hasUsers: true, users: ['Tom', 'Jerry'] }
Sorry, this may be too late. But I had similar requirement and found a better way to do this:
{{#users.length}}
<ul>
{{#users}}
<li>{{.}}</li>
{{/users}}
</ul>
{{/users.length}}
{{^users.length}}
<p>No Users</p>
{{/users.length}}
Working sample here: http://jsfiddle.net/eSvdb/
Using {{#users.length}} works great if you want the inner statement to repeat for every element of the array, but if you only want a statement to only run once, you can use:
{{#users.0}}
...
{{/users.0}}
{{^users.0}}
...
{{/users.0}}
I'm using chevron for Python. Since {{#users.length}} is implementation-dependent as described in previous answers, and doesn't work for me in Chevron, I return an object in my code which only contains the list if the list is non-empty. Presumably this technique would work for other languages too.
users = ["user1", "user2", "userN"]
user_obj = {"user_list": users} if len(users) > 0 else {}
template = """
{{#users}}
<ul>
{{#user_list}}
<li>{{.}}</li>
{{/user_list}}
</ul>
{{/users}}
"""
chevron.render(template, {"users": user_obj})

Resources