| Author |
Message |
![[Post New]](/rsf/templates/default/images/icon_minipost_new.gif) 13/04/2006 01:19:19
|
stevegithens
Request-scope Wrangler
Joined: 04/04/2006 20:34:52
Messages: 84
Location: ooeepooee
Offline
|
I am trying to figure out the simplest way to create a POST form, without using flows or other things.
I have a simple form:
Code:
<form rsf:id="basic-form" method="post">
<input type="text" rsf:id="title" value="Biological Molecules" />
<input type="submit" class="active" rsf:id="saveedit" value="Save"/>
</form>
I have my little producer:
Code:
public class EdittopicProducer implements ViewComponentProducer {
public String getViewID() {
return "edittopic";
}
public void fillComponents(UIContainer arg0, ViewParameters arg1, ComponentChecker arg2) {
UIForm form = UIForm.make(arg0, "basic-form");
UICommand.make(form, "saveedit");
UIInput.make(form, "title", "#{topicdisplay.title}");
}
}
I'm guessing I should be able to do this 2 ways:
1. Just get the POST parameters when the form comes back to this producer. Would I need to create another custom ViewParameter class to do this?
2. Just use the 'topicdisplay' bean to take care of the updating and actions. I've registered it as a bean and added it to the 'copyPreservingBeans' list in applicationContext.xml. I know there's probably another step I'm missing but can't think of it. ( I'm also fairly new to the Spring world.
Thanks!
|
|
|
 |
![[Post New]](/rsf/templates/default/images/icon_minipost_new.gif) 13/04/2006 01:48:56
|
antranig
Request-scope Wrangler
Joined: 03/04/2006 13:29:55
Messages: 643
Offline
|
Actually only the 2nd way is supported in RSF - in general you may not just apply random fields to forms and expect to decode them in the raw on the next cycle. In general what is encoded in an RSF form is pure operations on the bean model and shouldn't be thought of as a set of parameters at all. This may seem a bit odd, but it is the natural result of separating URL state (held in ViewParameters objects) and application state (held in the model) which is correct given HTTP semantics as well as the way most portals (except for a certain one that we know well  expect to work.
So, that out of the way, in the example below, you have set up your "topicdisplay" bean which will have its "title" property set when the form is submitted. What you are missing is the "action" which the UICommand object will trigger, which needs to be on some kind of request bean, which, probably will need to be able to see the topicdisplay request bean. This sort of setup will be familiar to JSF people, although in RSF it is a bit cleaner because i) the request beans are managed by Spring (or something near it), and ii) the really messy "component" layer from JSF is gone completely. In RSF, "topicdisplay" is considered as part of YOUR model and the values are applied to it directly, and it's your job to figure out what to do with it.
So, you need to set up the bean that handles the action, and get it connected to the topicdisplay bean. There are (at least) two ways of doing this.
BASIC WAY:
One way, more similar to the JSF way, is to set up the association "statically" in your requestContext.xml file - e.g.
Code:
<bean id="edithandler" class="MyEditHandler">
<property name="editTarget" ref="topicdisplay"/>
</bean>
<bean id="topicdisplay" class="MyTopicDisplay"/>
So, when your POST response comes in, these two beans will be created in the request, wired together, the incoming request values will be applied, THEN your action is invoked.
In your case, you'd probably make a "saveEdit()" method on MyEditHandler, and add the method binding to the saveedit control:
UICommand.make(form, "saveedit", "#{edithandler.saveEdit}");
Cooler, more dynamic way
When you are in a more complicated situation, perhaps with an ORM solution involved which stops you from having just the one bean in scope that you are talking about, you can't set up the link between the action bean and the data bean in a static way as before.
To deal with this situation, RSF allows you to set up the association dynamically by encoding a special binding into the form. The syntax for this looks like this:
Code:
RSFUtil.addBasicFormParameter(parent,
new UIELBinding("#{messageEditBean.actionKey}",
viewparams.action));
So, the first argument to the UIELBinding constructor is the target of the binding, that is an EL path that will be written. The second argument is a value that will be written to it.
If the second argument is just a plain value, you can just think of this as a fancy model-aware way of encoding a hidden field - it's the basic way to get extra context into the handler of the POST on the other side. However, the second argument could also be another EL reference, which I'm sure you can see the cool potential of - the property for the first argument will be "wired" to the property for the second argument, letting you, for example, "inject" a Hibernate-managed bean into an action handler you have written, or indeed get out of any tight corners that you frequently find yourself in with webapps when you are trying to get your handlers to figure out what earth they are meant to be operating on...
OK, sorry to go on so long - the basic answer to your question at least is in the middle there
|
|
|
 |
![[Post New]](/rsf/templates/default/images/icon_minipost_new.gif) 13/04/2006 03:54:15
|
stevegithens
Request-scope Wrangler
Joined: 04/04/2006 20:34:52
Messages: 84
Location: ooeepooee
Offline
|
Thanks, that helped quite a bit. I was a bit confused about whether EntityViewParameters were only for when you are using Hibernate, but you can use them for any sort of dynamic activity.
For the meantime, I am using the basic setup, but I'm putting both my value bindings and method bindings in the same bean. ( Does putting them together break a design principle or paradigm? .
I think part of my learning curve is starting to think in Request scope, since I'm used to using so much Session scope from JSF. ( I must be doing ok though, as the forum says I'm a request scope wrangler.
|
|
|
 |
![[Post New]](/rsf/templates/default/images/icon_minipost_new.gif) 13/04/2006 17:12:18
|
antranig
Request-scope Wrangler
Joined: 03/04/2006 13:29:55
Messages: 643
Offline
|
Cool, glad you're getting with the "request-scope flow"
Yes, putting your value and method bindings onto the same bean is probably pretty bad, but don't worry about it for now This is the sort of design that you were pretty much forced to in "old" frameworks like JSF and the Spring Annotations stuff where it was difficult to deal with different beans in the request scope in a coordinated way. You'll have heard the recommendation from JSF folks to use "one bean - one page" which was pretty much the only sensible way to "get the framework out of your hair" as quickly as possible.
A current nasty "anti-pattern" IMO you can see with some of the recent 2.0 Spring annotations stuff, where people say things like "we set these values in this bean, and then we 'tell it to persist itself'". This is a mess, since naturally the service of persisting something is separate from the business function of your model (even if you insulate the details of what it actually does by creating a "DAO" type interface), and it's completely backward to inject your persistence service into the bean, rather than hand your bean to the persistence service.
Once your design gets a bit more complicated, or you start using some kind of persistence, you'll probably appreciate splitting up your logic and state a bit more - the idea behind RSF is that, for a fully "bean-ridden" design that it lets you work with the beans you already (want to) have rather than be forced to just create special "beans" for dealing with the UI (i.e. on a per-page basis). However, it doesn't stop you creating extra beans to form a JSF-like "controller" layer if you want/need to. And if you didn't have any beans to start with, you can't avoid making some new ones in any case
But no, beans that you "just made for the UI" just aren't beans in my book.
Yes, getting out of session scope produces apps that aren't just more efficient, but give a better user experience too. So great to hear you're getting up the curve so quickly
|
|
|
 |
![[Post New]](/rsf/templates/default/images/icon_minipost_new.gif) 01/05/2006 04:29:46
|
stevegithens
Request-scope Wrangler
Joined: 04/04/2006 20:34:52
Messages: 84
Location: ooeepooee
Offline
|
OK, still getting used to not having the monolithic JSF Session bean...
For the sake of writing Demo's and examples, I'm needing to create lots of very simple POST forms that just revolve around themselves, but keep the values with each revolution.
I'm finding myself trying to do the following
Code:
applicationContext.xml:
<bean id="simplepager" class="org.sakaiproject.rsfgallery.simplepager.SimplepagerProducer">
<property name="pagerAction" ref="pageraction" />
</bean>
requestContext.xml:
<bean id="pageraction" class="org.sakaiproject.rsfgallery.simplepager.PagerAction" />
My hopes are that I can just fill in the values in the Producer, and each time the form is submitted get the new results and fields from the PagerAction. etc.
It's not working right now because it can't initially inject the PagerAction that doesn't exist, but I'm assuming that when I find the part of the Spring documentation that tells me how to make it optional it will work.
Does this seem like a logical way to create simple looping forms just for the sake of widget demo's?
|
|
|
 |
![[Post New]](/rsf/templates/default/images/icon_minipost_new.gif) 01/05/2006 22:42:08
|
antranig
Request-scope Wrangler
Joined: 03/04/2006 13:29:55
Messages: 643
Offline
|
OK, you're a bit stuck here because you have dependencies which are "back to front" - you have an application scope bean, simplepager, whose dependency is something with a shorter lifetime than itself, the pageraction.
While this is actually technically possible (using the RSACBridgeProxy), this is something you have to be extremely careful with since it is in a proper sense logically invalid (although given virtually every access to the context will be within the scope of a valid RSAC request there is little chance that it will go wrong by "blowing up" by findiing nothing).
There are two answers to this - i) move simplepager into request scope, so you can wire the dependence properly. ii) make a proxy to the PagerAction in application scope - take a look at http://rsf.fluidproject.org/wiki/Wiki.jsp?page=RSACBridgeProxy for explanation.
This "pager" business is really quite knotty in terms of the scoping issues, and I'm looking forward to us coming up with some good solutions for it You may want to look at the ServerSide thread where this issue came up http://www.theserverside.com/news/thread.tss?thread_id=40127#207520 since it's really quite awkward whether you want to put this state in the URL, or in the "model". Right now you are opting for putting it in the model, which is fine, but we will need to think about how to make a smooth transition from one scheme to the other.
|
|
|
 |
![[Post New]](/rsf/templates/default/images/icon_minipost_new.gif) 12/11/2007 15:00:34
|
gbhatnag
Newcomer
Joined: 12/11/2007 14:02:24
Messages: 2
Location: Ann Arbor, MI
Offline
|
Hello,
I think I'm running into an issue that is highly related to this topic, but I am not quite sure (I am indeed very new to RSF and do not know anything about JSF and little about Spring/Hibernate).
Following is the scenario, with my major stumbling block bolded:
* I have a form handled by AProducer.
* The form's purpose is to create a new user, with the username field as the primary key.
* AProducer assigns the action of the form to BackingBean which takes care of creating the user.
* After the user is successfully created, I'd like to move to a page handled by BProducer that presents fields of the newly created user.
* AProducer is a NavigationCaseReporter and points to BProducer upon successful creation of the user in BackingBean.
* I'm not sure how to get the username of the newly created user in BProducer to be able to fetch it from the database and present fields from it in the UI.
Following is a little bit of code to illustrate:
AProducer.fillComponents():
Code:
UICommand.make( form, "submit", "Submit", "#{BackingBean.createUser}" );
AProducer.reportNavigationCases():
Code:
navCases.add( new NavigationCase( "created", new SimpleViewParameters( BProducer.VIEW_ID ) ) );
All this is happening in the same request, so I am assuming I'm fine with the SimpleViewParameters NavigationCase, but I know I need to somehow pass the username to BProducer (the reason I think the SimpleViewParameters NavigationCase is fine is because I don't actually know the username that needs to be sent to BProducer).
I thought the suggested
Code:
RSFUtil.addBasicFormParameter(parent,
new UIELBinding("#{messageEditBean.actionKey}",
viewparams.action));
might do the trick, but realized that I don't want to bind things to the BackingBean, but to BProducer. Another suggestion I was thinking might make sense would be to add BackingBean as a property of BProducer so that I could directly access the newly created user object (since that is where the form inputs are bound to), but this just doesn't seem right.
Any suggestions? Please let me know if there is further information that I can provide that may be of help.
Thanks,
Gaurav
|
|
|
 |
![[Post New]](/rsf/templates/default/images/icon_minipost_new.gif) 13/11/2007 16:40:19
|
gbhatnag
Newcomer
Joined: 12/11/2007 14:02:24
Messages: 2
Location: Ann Arbor, MI
Offline
|
In response to this issue, I've tried some different things like adding a custom ViewParameters object to both Producers that contains the parameter of interest, but that didn't work. After scouring the rsfwiki, I think I can do this using StatePreservationStrategies and TokenStateHolders (http://rsf.fluidproject.org/wiki/Wiki.jsp?page=Flows). This wiki page contains information on how to setup these objects, but I'm unclear as to how to use them in an application. Any ideas?
Thanks,
Gaurav
|
|
|
 |
![[Post New]](/rsf/templates/default/images/icon_minipost_new.gif) 13/11/2007 16:59:50
|
antranig
Request-scope Wrangler
Joined: 03/04/2006 13:29:55
Messages: 643
Offline
|
Terribly sorry for the delay in getting round to this. This is the classic case of "redirecting to a view after action with state depending on action results" that we have been devoting some time to streamlining in the last 2 releases.
Your navigationCase is correct, and should stay - apart from the fact that BProducer does need to have a different ViewParams which *does* mention the username, which you should leave blank in the navigationCase.
Then all you need to do is issue a "resulting view binding" in your form - you were nearly right with the "basic form parameter", but this is a special form of binding that takes effect *after* the form submission and not before.
Take a look at this recent thread for a discussion of a very similar problem:
http://ponder.org.uk/rsf/posts/list/167.page
In your case, I imagine the correct binding looks something like
Code:
RSFUtil.addResultingViewBinding(m_fForm, "username", "BackingBean.username");
|
|
|
 |
![[Post New]](/rsf/templates/default/images/icon_minipost_new.gif) 05/12/2007 18:26:22
|
bobacus
Request-scope Wrangler
Joined: 21/08/2007 12:00:00
Messages: 92
Location: UK
Offline
|
I'm trying to do the same thing right now, but am finding that the resulting view binding is being applied *before* the action is invoked. Is there any way of controlling the order?
Rob
|
|
|
 |
![[Post New]](/rsf/templates/default/images/icon_minipost_new.gif) 05/12/2007 19:29:47
|
antranig
Request-scope Wrangler
Joined: 03/04/2006 13:29:55
Messages: 643
Offline
|
Since the binding is applied by an ARI2 there would seem to be no way it could be applied before the action.... so if you are seeing this behaviour it would have to be a bug. Can you supply some more details?
|
|
|
 |
![[Post New]](/rsf/templates/default/images/icon_minipost_new.gif) 06/12/2007 13:13:35
|
bobacus
Request-scope Wrangler
Joined: 21/08/2007 12:00:00
Messages: 92
Location: UK
Offline
|
I'm attaching a section of the log output.
What I expected was that after processing a POST to SignUpProducer, it would redirect as a GET to YourProfileProducer, which uses a subclass of ECVP, with the entity set to Profile.<id> where <id> is the id of the newly-created Profile (from action-signup).
This is the view binding:
Code:
RSFUtil.addResultingViewBinding(form, "entity.ID", "action-signup.profile.id");
The logs show that getProfile() is being called *before* fire(), which is causing the redirect to go without the entity id set.
Rob
| Filename |
rsflog.txt |
Download
|
| Description |
log output |
| Filesize |
6 Kbytes
|
| Downloaded: |
170 time(s) |
|
|
|
 |
![[Post New]](/rsf/templates/default/images/icon_minipost_new.gif) 10/12/2007 18:51:30
|
antranig
Request-scope Wrangler
Joined: 03/04/2006 13:29:55
Messages: 643
Offline
|
Sorry for the delay on this issue - my feeling is that it is really a bug, but the best way to tackle it is probably to beef up RSF's JUnit cycle and build a test case... I have done most of the work for this but am travelling this week which has reduced the time I have had free. I should have something in a day or two.
|
|
|
 |
![[Post New]](/rsf/templates/default/images/icon_minipost_new.gif) 11/12/2007 10:49:08
|
bobacus
Request-scope Wrangler
Joined: 21/08/2007 12:00:00
Messages: 92
Location: UK
Offline
|
Thanks for looking into this.
In the meantime I am implementing ActionResultInterceptor instead.
Rob
|
|
|
 |
![[Post New]](/rsf/templates/default/images/icon_minipost_new.gif) 10/01/2008 17:45:59
|
antranig
Request-scope Wrangler
Joined: 03/04/2006 13:29:55
Messages: 643
Offline
|
Certainly a bug, and quite a fundamental one:
http://www.caret.cam.ac.uk/jira/browse/RSF-59
|
|
|
 |
|
|