Tuesday Feb 25, 2014

Stretching an inputText inside of a Table Column

A quick trick to note if you have a table that includes a column containing an <af:inputText>. If the user is able to re-size the columns within the table, then it's not unreasonable to have the inputText stretch along with the column to take advantage of the full width available. 

The first thing to do is to set the simple property of the inputText to "true".  This almost does the trick, but you'll find that as you stretch the column there is a small trailing gap between the end of the input field and the column which grows as the column is stretched. So although the input is stretched as well it does not fill the cell which looks a little messy.

The second trick then is to set the contentStyle property on the inputText to "min-width:100%;" Do this and you will eliminate that gap and the input will now fill the column cell completely as you resize. 

Friday Jan 20, 2012

Break Group Formatting in a Table - Part 1

From time to time, interesting questions come my way from the community which warrent a little investigation time and in turn capturing of that information in a posting like this. Just such a problem arrived in my in-tray yesterday. Simply put, how do you create break group style formatting in an ADF table? By break group formatting I mean the supression of repeating values in a column, something which is a very common reporting requirement although not so common in a transactional UI. However, if the data is read-only and pre-sorted, why not?

To make it clear here's an example:

Example of Break Group formatting in a table

This is one of those problems that you can address in several ways. you could of course manipulate the data set, say for example, your View Object. if using ADF BC, to have a calculated column that you pre-process to contain the grouping text or not, however, this has some drawbacks. Firstly I dislike the idea of manipulating the data model for the sake of a UI vanity, secondly you have to find the right point in time to run through the transient attribute to populate it correctly, and finally if you filter the data in any way then you'll have to re-do that calculation. 

So is there a way you can do this in the JSF layer? Well yes, that's how I created the screenshot above. I'll reveal a couple of ways to do it, but first, let's get into what the logical process needs to be. Basically when we go to render say the department name we want to look at the previous value for the same attribute in the collection and if the value matches we supress display, otherwise we print it. So that's simple enough but let's review the tools we have to play with. Within the node-stamp of a table we have access to some contextual information in the form of var (the row information we're processing) and varstatus (which gives access to the row which can be very useful in some circumstances). We could perhaps see, that if our data model backing the table was some sort of indexed List, we could use a value of (<VarStatus>.count-1) to reach into the list and get the previous value. Indeed that might be an approach worth having a play with now I come to think about it, however, lets think about another way we could look at the previously stamped value. Logically all we have to do is to store the last value we printed and not print again if this row has the same value. If this row has a different value then we do print, and also we replace the stored value with the new one, and so the process continues. Great, so how do we do it?  

Everybody likes to cheat if they (think that they) can get away with it!

In this approach I've resorted to one of the great JSF "cheats". In out of the box JSF you can't execute arbitrary method calls as part of page rendering (however, more on that in the next article where I will discuss creating a custom EL function to do this as well), but there is one way in which you can make a call that passes a value. This is when you access a Map in an expression. e.g. #{mybean['foo']}. Behind the scenes this is calling the get() method on the Map interface and passing the supplied key - "foo" in this case, as the argument. So you see, we do have a technique for calling functions in-line. This technique has been covered before by me and by others, for example my good buddy Lucas Jellema.

So the basic approach here involves creating a managed bean which supplies a fake Map, where the get() method actually proxies for the check we want to make. Here's an extract from that class. I've not shown all of the code - you do have to implement the compelete Map interface in terms of creating the empty methods, but fear not, JDeveloper's Source -> Implement Interface option will do all the work for you. The important bit is the get() method shown in the extract here:

public class UIManager {
    //Holds the DepartmentId of the last one we rendered
    Integer _breakGroupLastKey = new Integer(-1);
    
    //The fake Map
    Map _breakGroupMap = new BreakGroupFakeMap();

    //Getter for the face map. No setter required
    public Map getBreakGroupMap() {
        return _breakGroupMap;
    }

    //Implementation of the fake map as an inner class
    public class BreakGroupFakeMap implements Map{

        @Override
        public Object get(Object key) {
            Boolean retValue = false;
            if (key instanceOf Integer){
              if (((Integer)key).equals(_breakGroupLastKey)){
                  retValue = true;
              }
              else {
                  retValue = false;
                  _breakGroupLastKey = (Integer)key;
              }
            }
            return retValue;
        }
...

And then we wire that into the column that we want to break on, in this case I'm displaying the DepartmentName and using DepartmentId as the key, so DepartmentId is also defined in the underlying binding. You can see here how the current DepartmentId is passed in using the Map style expression

<af:column headerText="Department" id="c1">
  <af:outputText value="#{row.DepartmentName}" 
                 visible="#{!uiManager.breakGroupMap[row.DepartmentId]}" 
                 id="ot1"/>                            
</af:column>       

You will of course need to think about the scope in which this bean lives, ViewScope is probably the most suitable. In the next posting I'll look at the slightly more advanced option of creating an custom EL function.  

See also Part 2 in this series

Friday Sep 02, 2011

Heatmap Styling in Tables

A question that has come up a couple of times recently is that of applying cell level styling, for example background color / colour within a conventional ADF Table.  In the Pivot table this is trivial, you just apply the styleclass to the component inside the <dvt:dataCell>. For the conventional table, however, you'll run into a slight problem with this approach in that the chances are that your styled inputText or outputText will not completely fill the cell and you'll end up with whitespace around the component and a ragged right margin depending on the length of the data in each. 

The solution is, however, simple.  Within the <af:column> wrap the display component inside of a <af:panelGroupLayout>. Ensure that you set the PGL to a vertical layout to ensure that it is stretched to fill the table cell.  Then just apply your styleclass expression to the PGL rather than the input or output text within.

So you might end up with something like this:

<af:column  headerText="#{bindings.Accounts.hints.AccountStatus.label}">
  <!-- stlyes with the same name as the Status value exist in the CSS --> 
  <af:panelGroupLayout layout="vertical" styleClass="#{row.bindings,Status.inputValue}">
    <af:outputText value="#{row.bindings.Status.inputValue}"/>
  </af:panelGroupLayout>
</af:column> 

About

Hawaii, Yes! Duncan has been around Oracle technology way too long but occasionally has interesting things to say. He works in the Development Tools Division at Oracle, but you guessed that right? In his spare time he contributes to the Hudson CI Server Project at Eclipse
Follow DuncanMills on Twitter

Note that comments on this blog are moderated so (1) There may be a delay before it gets published (2) I reserve the right to ignore silly questions and comment spam is not tolerated - it gets deleted so don't even bother, we all have better things to do with our lives.
However, don't be put off, I want to hear what you have to say!

Search

Archives
« April 2014
MonTueWedThuFriSatSun
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Today