93

I need to hide an element if certain values are present in the JSP

The values are stored in a List so I tried:

<c:if test="${  mylist.contains( myValue ) }">style='display:none;'</c:if>

But, it doesn't work.

How can I evaluate if a list contains a value in JSTL, the list and the values are strings.

OscarRyz
  • 196,001
  • 113
  • 385
  • 569
  • 18
    Note that the given syntax works as intented since EL 2.2 (which is brought as part of Servlet 3.0 / JSP 2.2 which was released Dec 2009). – BalusC Jan 10 '12 at 04:28
  • possible duplicate of [JSTL Sets and Lists - checking if item exists in a Set](http://stackoverflow.com/questions/1076679/jstl-sets-and-lists-checking-if-item-exists-in-a-set) – Matt Ball Jun 18 '12 at 13:32

11 Answers11

101

there is no built-in feature to check that - what you would do is write your own tld function which takes a list and an item, and calls the list's contains() method. e.g.

//in your own WEB-INF/custom-functions.tld file add this
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
        PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
        "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib
        xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
        version="2.0"
        >
    <tlib-version>1.0</tlib-version>
    <function>
        <name>contains</name>
        <function-class>com.Yourclass</function-class>
        <function-signature>boolean contains(java.util.List,java.lang.Object)
        </function-signature>
    </function>
</taglib>

Then create a class called Yourclass, and add a static method called contains with the above signature. I m sure the implementation of that method is pretty self explanatory:

package com; // just to illustrate how to represent the package in the tld
public class Yourclass {
   public static boolean contains(List list, Object o) {
      return list.contains(o);
   }
}

Then you can use it in your jsp:

<%@ taglib uri="/WEB-INF/custom-functions.tld" prefix="fn" %>
<c:if test="${  fn:contains( mylist, myValue ) }">style='display:none;'</c:if>

The tag can be used from any JSP in the site.

edit: more info regarding the tld file - more info here

Guy Bouallet
  • 2,099
  • 11
  • 16
Chii
  • 14,540
  • 3
  • 37
  • 44
  • 4
    I recommend to use `Collection` instead of `List` in the taglib - it work the same but support more collection types like `Set`s – Ralph Dec 16 '15 at 11:53
75

Sadly, I think that JSTL doesn't support anything but an iteration through all elements to figure this out. In the past, I've used the forEach method in the core tag library:

<c:set var="contains" value="false" />
<c:forEach var="item" items="${myList}">
  <c:if test="${item eq myValue}">
    <c:set var="contains" value="true" />
  </c:if>
</c:forEach>

After this runs, ${contains} will be equal to "true" if myList contained myValue.

Community
  • 1
  • 1
Kaleb Brasee
  • 51,193
  • 8
  • 108
  • 113
  • 10
    works nicely if the list is small. Just realize there is a performance cost to doing it this way. – Chii Sep 29 '09 at 01:55
  • 1
    Yeah, there would be if you get high enough. I've used it for collections of 10-20 things and have not experienced any performance issues. The thing I think is worse is the number of lines of JSTL. Still, I think this is the only way without setting up your own TLD (which isn't too difficult and may very well be worth it). – Kaleb Brasee Sep 29 '09 at 02:01
29

Another way of doing this is using a Map (HashMap) with Key, Value pairs representing your object.

Map<Long, Object> map = new HashMap<Long, Object>();
map.put(new Long(1), "one");
map.put(new Long(2), "two");

In JSTL

<c:if test="${not empty map[1]}">

This should return true if the pair exist in the map

tamersalama
  • 4,093
  • 1
  • 32
  • 35
  • 2
    This is my preferred way too, set up hashmaps in the viewmodel for everything I'll need in a view. They integrate nicely with EL syntax and are lightning-fast when searching. – Boris B. Aug 03 '12 at 14:21
  • Just one more thing, if key exist but value is null or empty then it would return false. – Zai May 09 '14 at 11:01
  • 1
    Or if the Map holds booleans, `test="${isPresentByValue[myValue]}"`. – Noumenon Feb 07 '17 at 04:13
17

You need to use the fn:contains() or fn:containsIgnoreCase() function.

<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>

...

 <c:if test="${not fn:containsIgnoreCase(mylist, 'apple')}">
        <p>Doesn't contain 'apple'</p>
    </c:if>

or

<c:if test="${not fn:contains(mylist, 'Apple')}">
            <p>Contains 'Apple'</p>
        </c:if>

Note: This will work like mylist.toString().contains("apple") and if this is not what you are looking for better use a other approach.

tk_
  • 16,415
  • 8
  • 80
  • 90
  • 3
    This is not the correct approach. It will basically behave like `mylist.toString().contains("apple")` which is absolutely not what you would initially expect. – BalusC Nov 04 '19 at 10:08
  • ohh.. yeah this is not what I expect. Anyway I will update my answer with this fining. – tk_ Nov 05 '19 at 03:26
2

The following is more of a workaround than an answer to your question but it may be what you are looking for. If you can put your values in a map instead of a list, that would solve your problem. Just map your values to a non null value and do this <c:if test="${mymap.myValue ne null}">style='display:none;'</c:if> or you can even map to style='display:none; and simply output ${mymap.myValue}

svachon
  • 7,666
  • 1
  • 18
  • 16
  • I guess the syntax should be style='display:none;' Otherwise the variable "myValue" is not evaluated. – Andreas Jun 08 '10 at 11:46
2
${fn:contains({1,2,4,8}, 2)}

OR

  <c:if test = "${fn:contains(theString, 'test')}">
     <p>Found test string<p>
  </c:if>

  <c:if test = "${fn:contains(theString, 'TEST')}">
     <p>Found TEST string<p>
  </c:if>
Tony Hung
  • 54
  • 5
0

If you are using EL 3.0+, the best approach in this case is as this other answer explained in another topic:

For a Collection it's easy, just use the Colleciton#contains() method in EL.

<h:panelGroup id="p1" rendered="#{bean.panels.contains('p1')}">...</h:panelGroup>
<h:panelGroup id="p2" rendered="#{bean.panels.contains('p2')}">...</h:panelGroup>
<h:panelGroup id="p3" rendered="#{bean.panels.contains('p3')}">...</h:panelGroup>

For an Object[] (array), you'd need a minimum of EL 3.0 and utilize its new Lambda support.

<h:panelGroup id="p1" rendered="#{bean.panels.stream().anyMatch(v -> v == 'p1').get()}">...</h:panelGroup>
<h:panelGroup id="p2" rendered="#{bean.panels.stream().anyMatch(v -> v == 'p2').get()}">...</h:panelGroup>
<h:panelGroup id="p3" rendered="#{bean.panels.stream().anyMatch(v -> v == 'p3').get()}">...</h:panelGroup>

If you're not on EL 3.0 yet, you'd need to create a custom EL function. [...]

Lucas Basquerotto
  • 7,260
  • 2
  • 47
  • 61
0

you must not use fn:contains(), because it is a string comparison. So suppose your list contains 11,12,13 and you have written fn:contains(list,'1'), it will give you the 'true' result. But I am hoping you are expecting false as 1 is not on the list. You should create your own custom tag to use list.contains(Object o).

-1

If you are using Spring Framework, you can use Spring TagLib and SpEL:

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
---
<spring:eval var="containsValue" expression="mylist.contains(myValue)" />
<c:if test="${containsValue}">style='display:none;'</c:if>
xxg
  • 2,048
  • 1
  • 11
  • 14
-1

I found this solution amazing.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%
   ArrayList list = new ArrayList();
   list.add("one");
   list.add("two");
   list.add("three");
%>
<c:set var="list" value="<%=list%>" />
<html>
<body>
        My list is ${list}<br/>
<c:if test='${fn:contains(list, "two")}'>
        My list contains two <br/>
</c:if>
<c:if test='${fn:contains(list, ",")}'>
        My list contains , 
</c:if>
</body>
</html>

The output for the code above is

My list is [one, two, three]

My list contains two

My list contains ,

I hope it helps someone.

oOXAam
  • 237
  • 1
  • 6
  • 20
-2
<c:if test="${fn:contains(task.subscribers, customer)}">

This works fine for me.

Vladislav
  • 41
  • 1
  • 18
    This is doing the check after converting both elements to string. The list is converted to a string and not checked on a per element basis. If task.subscribers is a list [ "one", "two", "twentyone" ] it will be: true for customer = "one" (matching twice) false for customer = "three" (no matching) true for customer = "twenty" (which is not what you are looking) – Ricardo Marimon Nov 10 '13 at 17:28
  • 2
    Being aware of the rmarimon's warning, this answer matched my exact use case. – CodeReaper Oct 07 '14 at 22:55