I am developing pagination logic using JSF1.2 and in the process I have 4 links for first, previous, next and last pages corresponding to the results. I enter the search criteria in the Search page and click submit to get some records. I have a view link corresponding to each of the records to see the complete details. So I have two Managed beans one for Search/pagination functionality and other for View complete record details.
Whats the problem then?
When I search for the records the pagination works completely fine. However when I view the details of a record and come back to search page, I find that every time I click the next button the next() method is getting called twice.
Is there any solution for this?
The code is as follows:-
Inside search:-
<h:commandLink value="#{msg['heading.nextLink']}"
binding="#{searchRoutesForView.nextLink}"
actionListener="#{searchRoutesForView.next}">
</h:commandLink>
Inside SearchManagedbean:-
public void next(ActionEvent actionEvent) {
if ((pointer + noOfRecordsToBeDisplayed) >= readConfig.length) {
readRoutingResponse.setReadConfig(Arrays.copyOfRange(readConfig,
pointer, readConfig.length));
pointer = readConfig.length;
System.out.println("pointer inside next =" + pointer);
setOrDisableLinks(false, false, true, true);
} else {
readRoutingResponse.setReadConfig(Arrays.copyOfRange(readConfig,
pointer, pointer + noOfRecordsToBeDisplayed));
pointer += noOfRecordsToBeDisplayed;
System.out.println("pointer inside next -- else =" + pointer);
setOrDisableLinks(false, false, false, false);
}
}
Please have a look at the lifecyle of JSF
Below approach may not be the best one..
public void next(ActionEvent actionEvent)
{
if((pointer + noOfRecordsToBeDisplayed) >= readConfig.length) && (isVisitedOnce == false))
{
visitedOnce = true //set one boolean indicating this method is already visited
}
}
This will not work if bean is in Request scope.
Related
I am working on one old application which is developed in JSF. In that, there is first page on which user fills details and clicks on next button. One filled values, it displays values on second page.
Here one bean is created who is holding first page values and it's request scoped. The page having bean of session scoped. I'm trying to access it using below code:
try {
if (obj == null) {
Application application = FacesContext.getCurrentInstance().getApplication();
appReqDetails = (ClassName) application.createValueBinding("#{obj}")
.getValue(FacesContext.getCurrentInstance());
}
} catch (Exception e) {
logger.error("Error/Exception occured:", e);
}
But approx. out of 100 users, 2-3 users are facing issue of values are not populating.
How to resolve this? Can I change scope of obj? Or any other way to get request scoped object into session scoped class?
On an xpage I calculate the message for an xp:confirm control:
var arr = viewScope.get("attachmentsAll");
if(arr.length>0){
return "";
}else{
return arr.length + " Are you sure want to upload the file?";
}
the viewScope is being updated after the event has been executed. I check this via a xp:text and I notice that this assumption is true.
<xp:text escape="true" id="computedField1"><xp:this.value><![CDATA[#{javascript:var arr = viewScope.get("attachmentsAll")
return arr.length + " number?"}]]></xp:this.value></xp:text>
The xp:confirm and xp:text reside in the same panel that is being partially updated after the event.
Can anyone explain me why the value for the viewScope variable is updated in the xp:text control and not in the xp:confirm control?
The main idea of my answer to your previous question was to put hidden input with computed value. What if you try to use <xp:this.script> instead of xp:confirm, and get the confirmation message from that hidden input the same way?
Update
The reason and the alternative solution that does not require to make any changes to existing xpage
It came out that the back-end instance of xp:confirm evaluates the new message correctly. The new value is even being sent to the browser with the response to ajax request. But one of the functions of XSP client module is built so that it won't update the querySubmit listener function if there already exists one with the same name. So, we are stuck with the old confirmation function that contains the old message. There is a way to override this behavior without breaking any other features. I have tried this and it works for me.
Create a new JavaScript library (client-side). Add the code:
if (!XSP._pushListenerTuned) {
XSP.__pushListener = XSP._pushListener;
XSP._pushListener = function x__pl(listeners, formId, clientId, scriptId, listener) {
if (scriptId && scriptId.endsWith("_confirm")) {
for (var i = 0; i < listeners.length; i++) {
if (scriptId == listeners[i].scriptId) {
listeners.splice(i, 1);
}
}
listeners.push(new this._SubmitListener(formId, listener, clientId, scriptId));
} else {
XSP.__pushListener(listeners, formId, clientId, scriptId, listener);
}
}
XSP._pushListenerTuned = true;
}
Attach your new library as resource globally via theme or as page resource on required page. I guess placing the above code as scriptBlock on a required page should also work. Now, any xp:confirm component on any page (if you used theme resource), or on particular page and everywhere after visiting this page (if you used page resource or scriptBlock), will work as naturally expected.
I have a webMethods CAF task that has a big form with a save button and a submit button. Many elements on the form have validation. The user needs to be able to hit Save and have the form submitted to the back end model so it can be saved as task data, without firing validation. Hitting Submit should fire validation.
How can I configure the page to do this. It's such a normal requirement, and I'm stuck!
It's not much fun.
Give your Save button a nice ID. Say, saveButton
Create a getter in your Java code that returns a boolean. Inside it, return true if the button's ID is one of the submitted fields, otherwise false:
private boolean validationRequired() {
return mapValueEndsWith((Map<String, String>)getRequestParam(),
new String[] {
"saveButton", // Your save button
"anotherButton", // Perhaps another button also shouldn't validate
"myForm:aThirdButton" // perhaps you want to be specific to a form
});
}
In every field that should be required, except on Save, bind the Validation->required attribute to your validationRequired getter.
That's it! Very tedious with a lot of fields on the screen, but it works.
P.s. what's mapValueEndswith? Just a utility; removed whitespace for compactness' sake:
private boolean mapValueEndsWith(Map<String, String> haystack, String[] needles) {
for(String needle : needles) if(mapValueEndsWith(haystack, needle)) return true;
return false;
}
private boolean mapValueEndsWith(Map<String, String> haystack, String needle) {
for(String value : haystack.values()) if(value.endsWith(needle)) return true;
return false;
}
As I see the approach provided, works only if the form contains only string type fields. If there are any other data types like integer, float, data-time are mapped to UI fields and conversion is used then this might fail if wrong data is entered in those fields.
I am stuck with an issue on valueChangeListener. I am using JSF 1.2 on Weblogic 6.0.
I have a page, where on top of the page there are 2 radio buttons with name as Name1 and Name2. When Name1 is clicked, the Name1 details(there are about 30 fields) is displayed below the radio buttons and when Name2 is clicked, Name2 details is displayed. Now the user can updated the clicked named details. e.g. the user clicks on Name1 and changes the address field and then clicks on Name2 and changes the age of Name2. When the user clicks back Name1, the address should be shown updated with the new value and again when the user clicks on Name2, the age should be updated.
I have used valueChangeListener to tackle it because I need the old and new value of the changed event. The problem is, as the valueChangeListener is invoked at the end of the VALIDATION phase, I am not getting the updated address of the Name1 field in the valueChangeListener method. Can someone help me out to get any workaround ?
as the valueChangeListener is invoked at the end of the VALIDATION phase, I am not getting the updated address of the Name1 field in the valueChangeListener method
Queue the event to the INVOKE_ACTION phase so that it acts like as an action(listener) method.
public void valueChangeListenerMethod(ValueChangeEvent event) {
if (event.getPhaseId() != PhaseId.INVOKE_APPLICATION) {
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
event.queue();
return;
}
// Do your original job here.
// It will only be entered when current phase ID is INVOKE_APPLICATION.
}
As INVOKE_ACTION phase runs after UPDATE_MODEL_VALUES, the updated model values will be just available "the usual way".
(Extension to BalusC answer)
As noted in my comment to BalusC answer, here the support method with usage example:
public static boolean moveToEndOfUpdateModel(FacesEvent event) {
if (event.getPhaseId() == null || event.getPhaseId().getOrdinal() < PhaseId.UPDATE_MODEL_VALUES.getOrdinal()) {
// see UIViewRoot.processUpdates(): first components.processUpdates(), then broadcastEvents()
event.setPhaseId(PhaseId.UPDATE_MODEL_VALUES);
event.queue();
return true;
}
return false;
}
(And to repeat the reason for the relevant change by me:
The event is put at the end of the queue, so if you have a valueChangeEvent together with an action-method the event will be executed after the action-method - typically not what you want.)
In the listener method I use it like this:
public void myValueChangeMethod(ValueChangeEvent vce) {
if (JSFUtils.moveToEndOfUpdateModel(vce)) return;
// ... perform my actions here, with access to already updated bean values ...
}
And for JSF 2+ you can better use <f:ajax listener="..."> - if you are not forced to use a component lib not supporting this (like the old Trinidad).
Im using subsonic 2.2
I tried asking this question another way but didnt get the answer i was looking for.
Basically i ususally include validation at page level or in my code behind for my user controls or aspx pages. However i haev seen some small bits of info advising this can be done within partial classes generated from subsonic.
So my question is, where do i put these, are there particular events i add my validation / business logic into such as inserting, or updating. - If so, and validation isnt met, how do i stop the insert or update. And if anyone has a code example of how this looks it would be great to start me off.
Any info greatly appreciated.
First you should create a partial class for you DAL object you want to use.
In my project I have a folder Generated where the generated classes live in and I have another folder Extended.
Let's say you have a Subsonic generated class Product. Create a new file Product.cs in your Extended (or whatever) folder an create a partial class Product and ensure that the namespace matches the subsonic generated classes namespace.
namespace Your.Namespace.DAL
{
public partial class Product
{
}
}
Now you have the ability to extend the product class. The interesting part ist that subsonic offers some methods to override.
namespace Your.Namespace.DAL
{
public partial class Product
{
public override bool Validate()
{
ValidateColumnSettings();
if (string.IsNullOrEmpty(this.ProductName))
this.Errors.Add("ProductName cannot be empty");
return Errors.Count == 0;
}
// another way
protected override void BeforeValidate()
{
if (string.IsNullOrEmpty(this.ProductName))
throw new Exception("ProductName cannot be empty");
}
protected override void BeforeInsert()
{
this.ProductUUID = Guid.NewGuid().ToString();
}
protected override void BeforeUpdate()
{
this.Total = this.Net + this.Tax;
}
protected override void AfterCommit()
{
DB.Update<ProductSales>()
.Set(ProductSales.ProductName).EqualTo(this.ProductName)
.Where(ProductSales.ProductId).IsEqualTo(this.ProductId)
.Execute();
}
}
}
In response to Dan's question:
First, have a look here: http://github.com/subsonic/SubSonic-2.0/blob/master/SubSonic/ActiveRecord/ActiveRecord.cs
In this file lives the whole logic I showed in my other post.
Validate: Is called during Save(), if Validate() returns false an exception is thrown.
Get's only called if the Property ValidateWhenSaving (which is a constant so you have to recompile SubSonic to change it) is true (default)
BeforeValidate: Is called during Save() when ValidateWhenSaving is true. Does nothing by default
BeforeInsert: Is called during Save() if the record is new. Does nothing by default.
BeforeUpdate: Is called during Save() if the record is new. Does nothing by default.
AfterCommit: Is called after sucessfully inserting/updating a record. Does nothing by default.
In my Validate() example, I first let the default ValidatColumnSettings() method run, which will add errors like "Maximum String lenght exceeded for column ProductName" if product name is longer than the value defined in the database. Then I add another errorstring if ProductName is empty and return false if the overall error count is bigger than zero.
This will throw an exception during Save() so you can't store the record in the DB.
I would suggest you call Validate() yourself and if it returns false you display the elements of this.Errors at the bottom of the page (the easy way) or (more elegant) you create a Dictionary<string, string> where the key is the columnname and the value is the reason.
private Dictionary<string, string> CustomErrors = new Dictionary<string, string>
protected override bool Validate()
{
this.CustomErrors.Clear();
ValidateColumnSettings();
if (string.IsNullOrEmpty(this.ProductName))
this.CustomErrors.Add(this.Columns.ProductName, "cannot be empty");
if (this.UnitPrice < 0)
this.CustomErrors.Add(this.Columns.UnitPrice, "has to be 0 or bigger");
return this.CustomErrors.Count == 0 && Errors.Count == 0;
}
Then if Validate() returns false you can add the reason directly besides/below the right field in your webpage.
If Validate() returns true you can safely call Save() but keep in mind that Save() could throw other errors during persistance like "Dublicate Key ...";
Thanks for the response, but can you confirm this for me as im alittle confused, if your validating the column (ProductName) value within validate() or the beforevalidate() is string empty or NULL, doesnt this mean that the insert / update has already been actioned, as otherwise it wouldnt know that youve tried to insert or update a null value from the UI / aspx fields within the page to the column??
Also, within asp.net insert or updating events we use e.cancel = true to stop the insert update, if beforevalidate failes does it automatically stop the action to insert or update?
If this is the case, isnt it eaiser to add page level validation to stop the insert or update being fired in the first place.
I guess im alittle confused at the lifecyle for these methods and when they come into play