Spring WebFlow, Validations and Models - spring-webflow

I am building a sample Spring WebFlow application and wanted to get some inputs on how the handle the below scenario.
I have 2 pages, the first page is a form where the user inputs data and the second page just displays the data the user entered in the previous page. The first page has a 'discard' and a 'continue' link and the second page just has the edit link which takes the user to the first screen so as to make edits on the data.
The scenario I am testing is..the user enters data, hits the continue link, the validators(Spring/WebFlow validators) are executed and the second page is displayed with the data (correct data). Now the user hits the edit link, changes a field on the first page, hits the continue link, the validators are executed and a error message(ex. user entered an invalid email address in the email field which is define as String in the model object) is shown on the same page (first page). Now the user hits the discard link and goes to the second page where the data is displayed. Now since the validations on the first page failed, the data displayed is not the correct one (shows the invalid email address).
Appreciate if some one can help me with displaying the old data (correct data) once the user hits the discard link since the data/model is not persisted anywhere.

If you're on a view-state with a model and you take a transition with bind="true" (the default), the model object is changed for that snapshot. That is, once you "continue", you've updated the object.
I don't know if you could track and return the user to a previous snapshot. If not, you'd need to retain a "backup" object and manually restore that one in the code attached to "discard".

Related

Session changed

Website set at company server, and use the session to store user information like user account & name. I wrote the session on the login page and never rewrote it, but the client pc has an account changing problem as user A in the middle of operating his account will change to user B maybe while user B logs in. User A and User B are on the same Intranet and use different PC.
Is the session make this problem? How to solve this problem modify code less?
Well, you have two rather seperate issues.
First the user - can logon with different logons. That in fact should give them each seperate sesisons. Perhaps you not logging out the user correctly when they logoff. (or maybe you rolled your own logon system - bad idea!! - since now you can and will have a session probem.
Next up:
You have to adopt a design in which session() allows the ONE user to work, and work if they right click - new tab, or even launch another copy of the web browser. So, you have to be REALLY careful here. Say a user clicks on a gridview - typical select some project, product or whatever. So you shove the PK id of that selected row into session, and then of course jump to a new page.
but, if they have two tabs open, or two browsers open on that grid? Well, now they can click on one copy, select row, jump to new page. Then the user does the same on the other grid.
Now, your PK session ID is DIFFERENT for the first page. If that code continues to use session() for that information, you are in BIG trouble!!! - the PK id in session on the first page is now different, and not the PK the user selected.
So, what to do in above? well, there is a hodge podge of workarounds - some add a custom number to some session value - some add a number to the URL - all are quite messey.
The simple soluion? Adopet a coding practice that you ONLY use session() to pass values, but on first page load, you transfer that to ViewState. View state is per page, (or per tab, or per browser page). Session() of course is global to user.
So, if they pick a house to buy from that grid, jump to a new page. Now they do the same on the 2nd browser copy - they are sitting on two different houses, but your session PK value is different!!! If they click buy or any other kind of operations on the first page? That code can NOT use session() anymore, can it?
So, even when I have say 4-5 values i must pass to the next page via session? I on page load (is postback = false), transfer those values to ViewState. You now write ALL CODE ON that page to ALWAYS use ViewState, and thus session() values never trip over each other. Two web pages on the same grid, user clicks - pass via session, transfer to viewstate. So, now you don't care.
And the above approch would solve your probelm at least of passing values. However, I see VERY little reason to store user information in session(). That is what the membership, and roles and the logon system does for you, and thus, if they logon with different ID, then membership GetUserID etc. should and will return a correct user ID. So, while the above design pattern will solve the tripping over PK and vaues passed around, it will not solve your session informaton that you have for the user. But as I stated, you REALLY do not, and should not need to store user logon information in session anyway.
however if you do correctly need and let a user change/flip/jump to a new different logon? If you first logout the user, then session can and should start over - it not clear how you logout the user, but I would look at that code.
I use this to logout a user:
Session.Abandon()
FormsAuthentication.SignOut()
So the above should start a new session - even if you allow user to flip, or change or logon to a different user.
However, the general over all issue of the session? Your problem is not only user session information, but that using session() values in general code - be careful, and always ask if the values in session() are ok to be used if the user say had 2 or 5 copies of the browser open.

Google App Maker Widget Validate on Datasource select

I have three different fields/textbox widgets, that rely on querying the same data source to be checked to avoid duplication. For reasons, I do not want to turn on the unique/required for those three fields.
I put some code to check for validation. My problem is that when I call the form's validate function, it takes some time till the validation comes back with an error message. However the form's validate returns immediately and allows people to click Submit.
How can I avoid this problem?
Block till validation kicks (setTimeout function?)
Set a separate invisible field such as working and set the validationError on the field and clear after validations clear? This will probably be a numeric field so that I can wait for all streams in parallel to finish.
Final question. is Validate a blocking function as it goes through the fields? I am guessing Yes.

Using a class in PostBack

I am totally new to classes and OOP, so please bear with me.
I am creating a large scale web app which I'm trying to keep tidy by creating my own classes.
For instance I have a Public Class Product which has several properties. One way I am using it is on page load a product ID is assigned to the ID property which in turn gets the details for that product and assigns the various data to the other properties. So within my code I can used for example product.price or product.description and get the appropriate values. This has worked fine, but I found that because the class was initiated on page load it was getting the data from the DB each time that the page refreshed. I stopped this by using an If Not IsPostback to initiate the class. This meant that the data was pulled in only on the initial page load. So far so good.
I then needed to compare a value in a textbox with a property of the product. I have a textchanged event with
If textbox1.Text <> product.description Then....
but here I get a wavy line under product.description and VS2010 is saying that the object is not defined. Its Dim'd in the page.load so I moved the Dim statement outside the page class so that it will be accessible to all events on the page.
The dim statement is Dim product as New product
In my not ispostback chunk of code I have for example product.ID = 1 which will get all the product properties for product 1
The wavy line has gone but when I run the page all works fine on page load. Data is displayed so my product class is working fine. As soon as I make a change in textbox1 and the event triggers product.description is nothing. It got reinitalised.
How do I stop this from happening...
Your "Product" is not persisted between postbacks.
Only control objects in aspx page are persisted/restored automatically.
To remedy this there are multiple approaches.
If Product is loaded via setting "Product.id=1" then what I woudl do is have a hiddenfield that receives the value of the product.id during prerender event (to save it in the page) and in an init event I would restore the "Product.id=hiddenfield.value" but only when it is a postback to reload your object.
EDIT
Thanks for picking my answer. I'll elaborate a little on the various ways to deal with this and why I suggested my answer.
Store Key in HiddenField Reload from DB:
PROS: Product is always Fresh/Correct/Current values. Corresponding to the database. Databases are very efficient to return a record based on a primary key. Very little data is sent to and posted back from the client browser. Low complexity. Each page opened by the client is safely isolated.
CONS: Multiple database transactions. If the DB is already strained or extremely massive you may need to consider even the smallest efficiency gain, but this is not common or likely on a primary key based record
Session State (store entire object):
PROS: Lowest time to "load" object since it's available in memory already once loaded. Less DB Transactions. No data piggy backed to the client and back again.
CONS: Object can become "out-of-date" if altered in the DB. Users who open multiple pages of your application can end up getting the wrong object if both require a different "Product", so instead to be totally safe you need a more complex structure in place to store more then one product or store them based on some kind of key (such as the product ID). Server Memory is used, if serving thousands of users or your product data is large it can become an issue, especially if you do this in many pages with many objects.
Serialization (store the entire object in the page in a field, similar to event state):
PROS: Once loaded, the Database is accessed only once for a specific product, then the product is held, in it's entirety inside the page, it is recreated by the server from the data in the field or via viewstate. Each page opened by the client is safely isolated. Is fairly easy to implement storing in ViewState of the Page.
CONS: Object can become "out-of-date" if altered in the DB. ALLOT more data is added to your page responce and the users next page request. Is more complex to implement because the object needs to be designed to be serialized correctly. Complex objects require allot of manual code to be serialized successfully.
again, there are many other ways to deal with this, such as storing items in a synclocked dictionary style object global to the application, but is considerablby more and more complex as you go.
This is likely the standard ASP.NET page life cycle problem.
After you initialize the page, it gets sent to the user's browser. When the user clicks on something, the browser sends a postback request back to your application. The view state allows the textbox1 object to remember what was in its Text property. However, your Page_Load ran from scratch, and, yes, everything including your product object got recreated from scratch.
If you want your product object to "remember" what it knew before the postback, you'll have to remind it. One way would be to store the initialized value in Session state, and then refresh your product object during the postback section of the Page_Load method.
Every time you do a postback, you're working with a new instance of your page class. The prior copy of your class was thrown away and probably disposed before your browser even rendered the page to the screen.
If you want to persist a value across http requests (of which postbacks are just one type), then you need to put it somewhere like the Session.

Tracking down error on form or in session

Apologies if this is classic ASP 101, but its been so long since I did any ASP Im struggling to understand / track this error down. What makes it worse is Ive inherited this application and I cant ask the original author..
I have a shopping cart that includes an input checkbox and numerous other fields. When the form is processed and submitted it is run through some javascript and then if all is ok, redirected to another page. (Nothing unusual there). Firebug shows that at this point the value of the check box is different depending on its checked state.
When the form is submitted it goes to another page that iterates over the session.Contents() collection, and builds up a string that is sent to a 3rd party. Using fiddler, it appears that whilst the name of the checkBox is in this string, the value is always 'on'
From reading Google, I see that the session.Contents collection is all parameters that have been placed in the session / application. but a grep across all the files in the project directory doesnt turn up anywhere that the checkbox is added to the session.
So, is the cb there simply because it is on the form or used in javascript, or are there other ways of adding the variable into the session. (Grep on the name doesnt turn up any other instances).
And secondly, if the variable is in the session, no matter how it got there, why is it always set to "on". Im assuming that somehow it has been added to the session and set to On before the form is processed. But the checkbox defaults to unchecked, so Im confused!
Can anyone help explain this, or even suggest how I can track it down / fix it. (The obvious answer is to try to force it into the session with the correct value, but I'd like to know why it is misbehaving rather than just ignore it in case I meet something like this again!
Thanks
I am going to assume that you have already determined that the JavaScript is not modifying checkbox state priort to allowing the submission.
When your form is submitted, the fields that are submitted are in the Request.Form collection. When a checkbox is not checked, it is not part of the Request.Form collection. Therefore, there will be as many checkbox fields in your Request.Form collection as you had checked when submitting, and they will all have the value of their respective "value" property.
If you then add these to the Session.Contents collection, they persist until the session ends. If you never explicitly clear the Session.Contents collection, but submit the form more than once with different values, then the Session.Contents collection will continue to accrue more and more (checkbox_name, checkbox_value) pairs until such time as it contains a (checkbox_name, checkbox_value) pair for every checkbox on your form.
You may wish to write a function that clears each one of your form fields from the session, and call this either after processing a form, or before processing a form (whichever makes sense for your application). Alternatively, just use the Request.Form collection.

view state in ASP.NET MVC Application

I have read that viewstate is not there in asp.net MVC application.
I am doing model validation. Now if i have two text boxes on my page,and i am doing required filed validation for both of them in model. This validation is done on server side on click of a button. I will fill one text box and click on submit button. It does the validation and returns the result saying second field is required. At this time value of the first text box is retained. So can you tell me how this text box is retaining the value even after postback?
There is no "postback". There's just a post.
Server returns HTML.
Browser renders it.
User POSTs crap data.
All of the user's submitted data is saved in the Controller.ModelState collection.
Each ModelState entry has its Errors property set if there is a validation error.
Server looks at crap data. Returns page same as (1) except that it includes user's submitted data, good or bad, and validation errors for the crap data.
Browser renders that.
When you call, say, Html.TextBox("someName", someValue) then the text box will contain someValue unless there's a ModelState key for "someName", in which case the value from ModelState is used instead. This is how the default data (if any) is displayed originally, but the user's data is displayed after an error.
Read about ModelState. When you post http form, values are stored in ModelState object and reused when you generate form again using html helpers (Html.Label, Html.Hidden, Html.TextBox).
Form is shown using Html.TextBox().
User enters value first time.
Form is posted.
ModelState object holds textbox value.
Form is shown second time using Html.TextBox() again. Method looks into ModelState and sets its value again as it was posted first time. Even if you provide new value, it will be searched in ModelState object.
View is rendered using Model. On failed validation if you pass the same model (with ModelState errors) to the view it will repopulate the same view with extra Validation messages which are rendered using ModelState Errors.
The value of the textbox is bound to the Model value.
Upon validation failure the page is redisplayed with it's Model in the state it was when the submitted (i.e. with a value for the first textbox) and any ModelState Errors added.
No viewstate coming in to play ;-)
Enabling view state is actually supposed to retain values of the controls if you are posting back to the same page

Resources