.

Friday, September 22, 2017

ADF (12.1.3) Table Row Selection: Dangerous Behavior

The ADF Table is one of those prolific components used in ADF web projects.

Over at the ADF project I worked on, there were cases where the selectionListener attribute was used to hook processing to the event. Of course, being a newbie that time, using the feature didn't go very smoothly.

This post will discuss things to watch our for when using the selection listener.



The following is a normal ADF table whose value is bound to some List whose values are irrelevant:


These tables are usually bound to a backing bean via the following code:

The generic type org.apache.myfaces.trinidad.util.ComponentReference is used to store component reference in a serializable and lightweight manner.

Row Selection and Activity

Selection Basics


By default, highlighting rows to mark them for selection is not enabled in a table. To augment this, simply modify the rowSelection property of the table. For example, if the rowSelection is set to "single", clicking on a row will yield the following visual output:


Although it has a little more added pleasant effects on the side, the highlighting is the most notable.

Behind the scenes, a table uses RowKeySets to keep track of the states of each row – like which rows are selected. The structure of a RowKeySet is quite close to a simple array of integers.

This allows the use of the table's getSelectedRowData() method to return the selected row, although a cast is needed as the return is of type java.lang.Object.

A problem with this is that this highlighting can be cached, even if the row is not actually selected! Furthermore, row selection cannot occur when a highlighted row is clicked on. This is very troublesome for pages that initially render with tables that have cached selection, while the tables have important row selection events – especially if there is only a single row, where the user can no longer trigger the event for the row whose selection is cached.

Of course, there are workarounds for this.

For pages with tables that initially render with cached selection, the solution can be quite a burden, but it is a solution nonetheless. It is as follows:
  1. Bind the table's selectedRowKeys property to the bean.
    • The bound bean property should be of type org.apache.myfaces.trinidad.model.RowKeySet; the property variable can be initialized using the concrete class org.apache.myfaces.trinidad.model.RowKeySetImpl. Initializing the variable with the empty constructors is sufficient.
    • Do not include the settermethod for this bean property so the table does not influence the state that the bean – in other words, the developer – should manage.
      • This means that for every process that might modify the table or its contents, the RowKeySet must be adjusted manually and constantly. Idioms exist for such cases:
        • adding an element to the table:
        • removing an item:
        • clearing selection:
  2. In the setter method for the table's binding, use the setRowIndex(int) method of the table during initialization, and pass it -1, as in "table.setRowIndex(-1)".
    • This is to make sure that the table has no initial back-end cached row selections (this does not make it safe from front-end selection caching, however)
    • Doing this disallows proper use of the varStatus property of the table for some reason – row indices are not displayed properly, but it does not negatively affect server-side processing.
  3. If the table resides in a reusable region, make sure the invoking page refreshes the region before it is displayed. The following snippet does just that:

On the other hand, when clearing the selection only has to happens during the lifecycle of the view, then clearing the table selection is sufficient.

 The Selection Event


Another main benefit of allowing selection is the ability to declare a selectionListener on the table. This property a allows a hook on which logic can be serviced upon the selection of a new row in the table. The following empty method snippet describes the signature of the method to bind:

Using this feature imposes new behavior upon its table. Without a selectionListener, row selection will not cause (partial) submission of table data; input components with validation will not trigger by merely selecting rows on the table. On the other hand, declaring a selectionListener the table submits its contents, tripping the validation of any enabled component within the table. Although this might sound very risky and could potentially produce vulnerabilities (this post discusses some of that), this is also a step taken to extend the operability of tables - also, because the events would usually reside on the server, it only makes sense that the table produces a request.

Of course, using more features introduces more considerations and maintenance requirements. It cannot be reiterated enough how invaluable awareness is throughout the construct of a page; couple it with knowledge of the problems discussed here, and, as with many other features, the output can be made robust.

Adding a selection event causes the table, and any partial targets to submit. This means that a selection will not succeed if the components queued for submission are not satisfied. Furthermore, the problem is aggravated by the inconsistency introduced regarding selection: even though the selection event is not fired, the selection changes anyway! This causes selection integrity efforts done during the selection event to go awry.

Here is a quick demonstration:
  1. Initially, the input components are not required so that the first selection can be made, and the selection event fired properly. A row is then selected (the text components below the table are partial targets of the table):

    • The value for "current selection via table" is directly bound in the JSF through the expression #{viewScope.backingBean.table.selectedRowData.name}, with the following properties:
      • backingBean refers to a viewScope bean that holds the table component
      • table is the bean property of the backing bean where the table is bound
      • selectedRowData is the table method that returns what is actually selected
      • name is a property of a row in the table that corresponds to the first column (output components)
    • The value for "current selection via selection listener" is bound to a viewScope bean property whose value is update during the table's selection event with code following from this snippet:
  2. The "make input required" button is clicked so that the input fields on the second column are now required. This is implemented as a simple toggle in the view-scope bean.
  3. The second row is selected, and then the page is manually refreshed (via f5 in most browsers) since partial rendering will not occur if there are unsatisfied fields during submission:

Notice that "current selection" text fields at the bottom are inconsistent. Again, "current selection via table" takes the value straight from the table, with the EL string "#{viewScope.backingBean.table.selectedRowData.name}". On the other hand, "current selection via selection listener" takes its value from a bean property that is managed by the table's selectionListener. It becomes clear that the selection event was not fired.

There is quick remedy for this problem: setting the table's immediate attribute to "true". Doing so will bypass errors from component validation as the table short-circuits its submission, if only to allow unhindered row changes. Take careful consideration of its implications to your desired program flow and guard your code well against problems (increasing such robustness may involve relocatin validation and manually queueing events, among others).

Use the immediate attribute for components responsibly. Normally, it is to be used with action components such as buttons or links, but finer requirements may merit the use of the attribute in input components as well. Be careful as using this attribute introduces delicacy in the lifecycle of a JSF request. This blog post provides a full explanation of what the attribute does.

Table Editing Mode

Table Editing Mode is another neat feature of ADF tables which appears as the editingMode property. It has two values: editAll (the default), which has the table leave the its input components to their own conditions for being disabled; and clickToEdit, which has the table disable the input components of a row that is not selected (hence, "click on the row to edit" its values).

As cool as this feature is, a selection listener combined with rows with components that use validation throws a spanner into its workings.

Let's observe such a case. Here is the previous table (still not made immediate), now modified to have the clickToEdit editingMode (notice the input text box only appears on the selected row):



The input text box components on the second row were left so that input is required. Since a selection listener causes table data submission upon row selection change, the requiredness should trip an error:



A truly frightening sight, even as we have not manually refreshed the page!

Now, let us refresh the page to find out of the selection listener was called:



It is now also clear that even the selection event was not fired, but the actual selection still changed.

Hold on, what about submission?

Let's satisfy the requiredness of the input field, and then click on the submit button:



Though the event is invisible (as the implementation is hollow), the submit actually proceeded. This spells bad news for inactive rows with invalid data – manual validation has to be done upon submit when using this feature.

Summary

Selection is pretty much commonplace when using tables, though the newbie ADF developer may find trouble when having to use the selectionListener.

When selection is enabled in a table, a selectionListener can further be declared to handle selection events as desired. However, doing so now forces the table to submit its contents as it now makes requests whenever row selection (of a different row from the previous) is done.

This can be troublesome when input components in the table have validation issues, as row selection now only "occurs" in the view (via highlight changes), as the selection event does not get called. A quick solution for this is to have the table's immediate attribute set to "true". Of course, be careful of its implications.

Another nice feature of ADF tables is the editingMode. Depending on the value, a table can have input components of non-selected rows disabled (by default, all input components of the table would be enabled).

Of course, this feature is not safe from what selection events and component validation impose. With both in commission, row selections that trip validation errors still have the highlight (only the visual element) move, while the actual selection remains, also without the selection event being called. Furthermore, "inactive" rows with invalid input do not trip their validations, so proper collective validation also has to be done deeper in server-side logic (as better practices also propose).


And that's all. Hope you find this helpful. Cheers!

3 comments:

  1. Password is the key that keeps your stuff locked safely. What if suddenly you forget your gemini account's password or your password doesn't allow you to login to your account. In such a situation do not panic as it can be resolved at an instant by Gemini Support Number advanced assistance of an executive at gemini. You can get in touch with them by dialing gemini customer service number and let the experts take over your issues and provide best possible solutions.

    ReplyDelete
  2. Bitcoin is one of the most renowned cryptocurrency and Binance is one of most renowned platform to transact in Bitcoin? On rare occasions users are unable to send and receive Bitcoins in Binance? Are you also fed of this precarious situation? If yes then dial Binance support number to avail the best possible service in the business. Binance Support Number The professionalism that the executives display is commendable and they are very handy. Connect with the talented team and enjoy hassle-free services. The professionals are always there to guide you at problematic stages.

    ReplyDelete
  3. Is phone verification not done in Gemini yet? Do you have the means and methods to fix the error or bugs immediately so that you get out of this error and focus on trading? If no, you get the best possible solutions from the team of well-experienced professionals who are always there to guide you. Dial Gemini support number which is the path to convey your queries as well as to ask for the solutions from the professionals. But, to get results in no time, call on our customer care number which is functional throughout the year to assist users.

    ReplyDelete