• Post Reply Bookmark Topic Watch Topic
  • New Topic

How to update rich:panel components in dataTable to affect only the one selected?  RSS feed

 
Boris Golman
Ranch Hand
Posts: 38
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I've been currently banging my head over one problem. I have a dataTable component, which has a command link and a rich:panel component for each column. Basically, all I want to do is to click the command link, which triggers bean's actionListener method and dynamically creates appropriate components, which are then added to the rich:panel. And it does, but instead of updating only the panel in selected row, it updates all of them. Here's how it looks like (in a rough):



#{myBacking.additionalItems} is HTMLDataTable object, that I have in my bean. The itemList is a list of an entity bean that I get from the database, it contains string variables, which I use to determine what components to put on my rich:panel. This is my processUpdate method:



The code mentions "findComponent()" method. I found this code elsewhere, here it is:



And that's really all, and, like I said, it seems like it can find the right component (I printed out the client id for each row I click, and it returns the right one), but it updates all of them. I tried binding the rich:panel and tried getting the component the way it's described in the code above, nothing worked. I know that something similar to this problem was posted on this forum, but, unfortunately, I didn't see anyone responding. Maybe someone had that problem before and found a solution. Can anyone help me, what am I doing wrong?

Thanks in advance
 
Tim Holloway
Bartender
Posts: 18531
61
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Rule #1 for JSF:

The more JSF-specific code you have, the greater the probability that you are doing it wrong. JSF was designed to do as much as possible using POJOs and the only javax.faces classes that most JSF beans should be employing are the model classes. Which, since JSF2 they aren't using enough, ironically.

Among the things that raise alarms are:

1. Using bindings
2. Using listeners
3. Accesses to the FacesContext

These are all things that have their uses, but there seem to be an awful lot of bad examples floating around and/or a lot of people who can't stand the idea that things can be simple and portable so they insist on doing things complex and non-portable.

A listener isn't something that's fired by a button/link click. It's something that listens to the action that is triggered by a button or link click. The actual action submits a form, the form is used as a data source to feed the JSF lifecycle process. One of the primary tenets of JSF is that if any control value in a submitted form is invalid all of the incoming request is rejected and that includes action listeners (which as I said you generally shouldn't use) as well as POJO action methods (which you generally should use).
 
Boris Golman
Ranch Hand
Posts: 38
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
So, what you're suggesting is to create an action method within a model class I'm using, instead of running an action listener in the backing bean. Tried that and still got the same result. It still updates all panels, not just the one I've selected, even though I'm running the action method that is specific to the item in the data table.
 
Tim Holloway
Bartender
Posts: 18531
61
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
A backing bean is a model. Don't confuse the UI model (backing beans) with the persistent Domain Model (JPA/Hibernate/EJB). With rare exceptions, they do not overlap.

The default behaviour in response to a click on a commandLink or commandButton is to send down all the data in the form containing the command control, run through the JSF lifecycle and return a new/updated web page (the Render Response phase of the JSF lifecycle) which then replaces the previously-displayed page in the user's browser.

To keep the current page and only update part of it (partial page refresh), you have to use AJAX. Using an actionListener instead of an action method won't help.
 
Boris Golman
Ranch Hand
Posts: 38
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I hope you don't mean "f:ajax", I'm not using JSF2, I'm using a previous version, this feature is not there yet. Also, I don't want to refresh parts of the page, I want to update part of the page. Technically, I do need to refresh the whole page, where only selected row is updated. If you create a button for each row to remove the entire selected row, it works, if the table is using the binding to HtmlDataTable object and its value is a list of POJOs (the backing bean removes the item from the list, refreshes the entire page, and selected row is gone). Same goes with adding a row. So I don't understand why the same thing can't work with adding specific components to the selected row, especially if the component where you add children components to is successfully retrieved. I do apologize for being slow on this, JSF, especially the binding, is still fairly new to me. I tried using action and actionListener in h:commandLink (which, as you've said, doesn't make too much of a difference), tried using a4j:commandLink, still to no avail. Is there a way to bind a specific row to a specific dynamically created HtmlPanel?
 
Tim Holloway
Bartender
Posts: 18531
61
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Don't feel bad. I am still slow in picking up what you mean.

While JSF1 doesn't support the f:ajax tag, you're using RichFaces a4j (AJAX for JSF), which is capable of doing the same things, if not even more.

If you are simply looking to add a new row to a tabular display, there's no need to use a lot of complex and arcane JSF-specific code. The JSF dataTable tag references a DataModel object which serves as the container for its row models plus some JSF support data. If you add a new row to the DataModel and then re-render the table (reRender, refresh, update all mean the same thing in this discussion) then the updated JSF dataTable component controller will automatically add the necessary UIcomponents needed to make the newly-added row display in the table.

Your table appears to have a single column with a fairly complex internal panel construction, but as long as it can be repeatedly mapped to individual rows in the DataModel, that's all that really matters. JSF will handle the rest.

When using a4j:commandLink this way, the proper coding is:


Technically, "ajaxSingle" shouldn't be meaningful if immediate= is specified, but RichFaces can get a little quirky. The reRender attribute specifies the id(s) of the components that need to be updated. The "immediate="true"" is a way to avoid passing down updated form control values when the request is submitted. You want that because otherwise any invalid values would block the update request even though they weren't directly related to the request. And to avoid what might be a premature update of the model (backing bean).

The DataModel object specified by the value= attribute of the dataTable bean can model a POJO List object using the setWrappedData() method or by wrapping it via a ListDataModel constructor. Once wrapped, updates to the list are automatically reflected when the View is updated. You don't need to re-wrap it.
 
Boris Golman
Ranch Hand
Posts: 38
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
All this makes perfect sense, which is why I'm surprised that why adding UIComponents to a specific row is not working, it's always updating all of them. Even after trying your code snippet (I always forget ajaxSingle with immediate, even though they saved me once ;)), it still updates the panel in every single row, rather than the one I've selected by clicking a command link. Could the problem be on the back-end? Eeach row technically should be bound to a row-specific rich:panel, so it shouldn't be hard to retrieve this specific rich:panel, right? And it looks like clicking commandLink deals with this specific row, so using this code snippet:
org.richfaces.component.html.HtmlPanel rowPanel = (org.richfaces.component.html.HtmlPanel)findComponent("outputRichPanel");
I'm, in fact, getting the right rich:panel, right? But still, all the rows are updated no matter what I do. If everything happens within one row, shouldn't the action affect only one row? I'm missing something very simple, I can feel it.
 
Tim Holloway
Bartender
Posts: 18531
61
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The sample I gave explicitly asked to re-render the entire table. That's usually the best way to do things. It's pretty much the only way to do it if you are actually adding or removing rows, since they do, in fact, affect the entire table display.

The JSF dataTable does not explicitly define rows, so you cannot update an existing row by ID (there's no place to put the row ID). You can update a row component by requesting a reRender of that component by its ID. However that will only work if you use a DataModel object in your dataTable value. An undecorated POJO collection has no place to hold the necessary row-cursor data, but a DataModel does.
 
Boris Golman
Ranch Hand
Posts: 38
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks, I haven't used DataModel before, but this is something I've tried:
In my case I have to create something like this:



And initialize it in the constructor:



Then itemsDataModel is assigned to dataTable value attribute.

However, I'm still confused on the most important part - retrieving the HtmlDataPanel of my row, in order to add new components to it. DataModel returns me the same thing HtmlDataTable did - the row data, which is a MyEntityBean object. But I need to somehow access the actual rich:panel from my action method. Otherwise, I'm pretty much doing the same thing and result is exactly the same.
 
Tim Holloway
Bartender
Posts: 18531
61
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think that it's time to step back and tell us why you feel the need to alter the actual JSF UIComponent tree manually instead of letting JSF generate things automatically. Sure, I suppose I could actually print out your code and read it carefully, but that's too much like work.
 
Boris Golman
Ranch Hand
Posts: 38
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Well, I thought it shouldn't be that complicated, but then again, nothing is ever simple, especially when you're dealing with JSF pitfalls. The commandLink acts as a switcher between a rich:editor, where the user types certain data, and rich:panel, where the user gets results. Suppose, I have a list of records, that contain certain data. I display them in a dataTable, one rich:editor for each record. The data is, therefore changeable. When the user updates the data in rich:editor, he can add a <span> tag that represents a placeholder for a new input (doesn't matter how, I don't want to digress). Each rich:editor is accompanied by a commandLink. So, if the user enters a placeholder for, say a text editor, he should be able to click the commandLink and the rich:editor is replaced with rich:panel, where the text is now not changeable and the placeholder is replaced with the actual input (right now it's pretty much just text boxes, but there's a possibility to have other inputs). Thus, the user can change the text when he switches to rich:editor and to apply a value to an input when he switches to rich:panel. I once asked you if there's a way to have the text AND inputs placed in rich:editor, and you said no and gave me a good explanation why, so this is my alternative. Therefore, to make the switcher work, I have my backing bean read the value in rich:editor and turn them into UI components to place them on the rich:panel. I've done something like that before, but not in the datatable. I could only add it to a single panel, not to the one in a dataTable row. That's pretty much it. So, when you say "letting JSF generate things automatically", I'm a bit confused. I do let it generate things automatically, but it does so for every row, not the one I select.

I do hope it makes sense. Thanks for bearing with me, this seems to be the worst culprit I've encountered. ;)
 
Tim Holloway
Bartender
Posts: 18531
61
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ohhhhhh.

Well, it's true what I said earlier about the limitations of the Rich editor control, but sometimes things are simple when it comes to JSF:



I think I misread the rich editor component as a vanilla input component. It's not hard to do when reading from the screen. And I didn't realize that what you wanted to do was switch a pair of controls in and out.

The above example, suitably enriched for real-world use is all you need. You need an "edit" boolean property in the row model class to switch the display in and out of edit mode (the mode is set by the commandLink action method). It needs to be per-row because otherwise all rows in the table would switch. And the a4j:commandLink needs a reRender="txDisplay,txEdit" to ensure that the UI components are properly visually swapped.

No listeners, no UIComponent juggling, just plain old Java code. The only JSF-specific code at all is the table's DataModel object.
 
Boris Golman
Ranch Hand
Posts: 38
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That was, actually, my plan B (more like plan C) - just take the HTML in rich:editor and update it with HTML inputs. And, I guess, for the time being, it'll do. However, the reason I was fiddling with UIComponents is because I was planning to use rich:faces components, not the HTML components, like "rich:calendar" or "rich:inplaceInput". With outputText approach I don't think it's gonna work, but creating RichFaces inputs and placing them on a rich:panel actually did the trick ... but for all the rows, not just the one I clicked. I'm back to HTML inputs because there's a slight problem with JQuery, which I'll get resolved later, but I wonder how would the thing you've suggested work with RichFaces inputs.
 
Tim Holloway
Bartender
Posts: 18531
61
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I guess I'm still missing something. I thought you were looking to display a block of HTML-formatted text as fixed text with an option to edit it using the rich:editor. That's what the sample I just listed does, anyway. I did slip up and put "escape="true"". In order to render the text string's HTML tags graphically instead of in source form it should have been "escape="false"". Sorry about that.

I don't understand what "rich:faces" and "HTML components" are supposed to be or why they're supposed to differ. All of the RichFaces components are JSF (faces) components and they all ultimately generate HTML, even though the source code is JSF VDL.
 
Boris Golman
Ranch Hand
Posts: 38
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Not a problem, I've figured escape attribute already.
I actually wanted to replace the text in rich:editor with the fixed HTML text AND the inputs, replacing the certain "placeholders" in rich:editor. For example, if my rich:editor value would be something like:
"This is a fixed text. This is where you enter an additional value: <span id='someTextEditor'>[ENTER VALUE HERE]</span>", then when I click the command link, I expect to see fixed text and an actual text input, instead of a span. And this span can have an id like "calendar" and it would expect a date, which is why I was hoping to replace it with "rich:calendar", instead of a text input. I do understand that all RichFaces inputs are, essentially, HTML, but when they are compiled into HTML, they are also mixed with javascript, compiling them together in one string and placing in outputText won't necessarily make them work. Still, if there's no other way to do that, I might need to create my own version of those inputs, so that they behave similar to RichFaces inputs, but also be addable to an outputText as a string. I'm afraid, that's my only option. I was just hoping that RichFaces would be more flexible on creating its inputs dynamically.
 
Tim Holloway
Bartender
Posts: 18531
61
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Oh yeah. That problem. The "editor-within-an-editor". That requires 'way more than just jamming UIComponent controls in and out. Whether you do it statically or dynamically, neither JSF nor the rich:editor control can handle that. In fact, neither can the underlying javascript editor or even the HTML at the very bottom of it all. The rich:editor works in an HTML TEXTAREA control and you cannot nest one HTML construct (such as a span) inside a control primitive (such as a textarea) and expect it to work properly. Much less to be properly handled when it's complex JSF controls like the rich:calendar.

This is not a trivial job and you should demand a raise. And for that matter, I should send you a bill.

I think your best bet is to dig into the internals of TinyMCE, which is what implements the rich:editor at the html/javascript level. If you're lucky, its lexical scanner will allow some hooks so that you can detect when a span has been clicked on. That's the hard part. If there's no place to hook, you'll have to do things the hard way and customize TinyMCE itself.

You can pop up editors over the tinyMCE editor. That's not so hard. And they can be based on JSF controls, including RichFaces controls such as the rich:calendar. You need some CSS magic, that's all. Define the controls somewhere out of sight in the usual way. When you need them, set their visibility and x-y-z co-ordinates such that they appear in the desired place - over the span tag, for example. Define them as AJAX controls so that, for example when you hit "Enter", the JSF action converts to a TinyMCE API call. That's essentially what the RichFaces modal dialog does, and, in fact, you might even find it easiest to simple define these controls in little richfaces popup modal dialogs.

 
Boris Golman
Ranch Hand
Posts: 38
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I was afraid of that (the bill, the JSF/RichFaces pitfalls, everything). ;) Makes sense, though, it was more helpful than paid forums, I can tell you that. Well, for now I'll go with what I have. Thanks a lot for your help, really appreciate it.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!