I've got a really weird problem and I can't seem to find a solution to it. I've got an SqlDataSource with a set of parameters and a select command in form of a stored procedure. One of the parameters is a string called #SearchPhrase which gets its value from a textbox that when left empty is suppose to return all posts. The parameter gets its value via a search button that first set the value of searchBox.Text.Trim() to the #SearchPhrase and then databinds a gridview that displays the search result.
The stored procedure works fine and returns the result as long as a value is entered in the textbox but when left empty it turns out the stored procedure is never executed. I know this because I've added a line to it that stores all of the parameters inserted every time it's called.
How come I can't get it to run without entering something in the textbox? The only way I can get it to execute is to enter a blank space and pass " " to the stored procedure and after that do a left and right trim on it. Why not when passing a string with length = 0?
Here's the code for the datasource and the assigning of the parameters. It's only the #SearchPhrase parameter that is causing the problem, the rest work just fine. The first three lines of the click event (the commented) is the somewhat ugly way to go if I want to assign a " " and that way make it work.
<asp:SqlDataSource ID="enterprisesDS" runat="server"
ConnectionString="<%$ ConnectionStrings:CRMdb %>"
SelectCommand="entGetEnterprises" SelectCommandType="StoredProcedure">
<SelectParameters>
<asp:Parameter Name="SearchPhrase" Type="String" />
<asp:Parameter Name="IsActive" Type="Boolean" DefaultValue="true" />
<asp:Parameter Name="Country" Type="Byte" />
<asp:Parameter Name="LocalReference" Type="String" />
<asp:Parameter Name="Class" Type="String" />
<asp:Parameter Name="LocationID" Type="Byte" />
</SelectParameters>
</asp:SqlDataSource>
protected void search_Click(object sender, EventArgs e)
{
//string searchPhrase = " ";
//if (searchBox.Text.Trim().Length > 0)
// searchPhrase = searchBox.Text.Trim();
enterprisesDS.SelectParameters["SearchPhrase"].DefaultValue = searchBox.Text;
enterprisesDS.SelectParameters["Country"].DefaultValue = countries.SelectedValue;
enterprisesDS.SelectParameters["LocalReference"].DefaultValue = localReferences.SelectedValue;
enterprisesDS.SelectParameters["Class"].DefaultValue = classes.SelectedValue;
string locID = "0";
if (locations.SelectedValue != null)
locID = locations.SelectedValue;
enterprisesDS.SelectParameters["LocationID"].DefaultValue = locID;
enterprises.DataBind();
}
Could this be it? CancelSelectOnNullParameter property on the SqlDataSource.
SqlDataSource and stored procedure call issue
Related
I have a query which gets some data and displays it on on a webpage using CFLOOP and also generates a Excel file with POI:Document in ColdFusion.
<cfloop query="qUsedListings">
<poi:row>
<poi:cell type="numeric" NumberFormat="0" value="#qUsedListings.region#" />
<poi:cell type="numeric" NumberFormat="0" value="#qUsedListings.district#" />
<poi:cell type="numeric" NumberFormat="0" value="#qUsedListings.storeId#" />
<poi:cell value="#qUsedListings.acquisitionAssociate#" />
<poi:cell type="numeric" NumberFormat="0" value="#qUsedListings.listingId#" />
<!---This above listing ID is generating duplicate values--->
<poi:cell value="#qUsedListings.description#" />
<poi:cell type="numeric" value="#qUsedListings.grossMarginPercentage#" />
<poi:cell type="numeric" value="#qUsedListings.itemCost#" />
<cfif LEN(qUsedListings.UVMAVERAGECOST)>
<poi:cell type="numeric" value="#qUsedListings.UVMAVERAGECOST#" />
<cfelse>
<poi:cell value="N/A" />
</cfif>
Also when I tried to put #qUsedListings.listingId# in its upper cell value which is <poi:cell value="#qUsedListings.acquisitionAssociate#" />
I tried <poi:cell value="#qUsedListings.acquisitionAssociate# #qUsedListings.listingId#" />
Then the ListingId were coming correct in this cell with acquisitionAssociate, but at the same time there were duplicate and unordered values in original ListingId column.
Thing is that, it has different columns and all the columns are generating fine results except one viz ListingId. It is showing duplicate (or incorrect) values which is not possible. The same data which is on Webpage is correct but in Excel file it is generating duplicate/incorrect values.
For example in the image below
The ListingID value in row 1 is 113799576, but it should be 113799573
The ListingID value in row 2 is 113807824, but it should be 113807820
acquisitionAssociate [acqAsso_val ListingId_val]
ListingId [ListingId_val]
UPDATE
I tried changing the ListingId column Data type to text(string) in code then the results are coming correct. The dataType for ListingId in DATABASE is varchar, but it contains only numeric values. So the possible error here is conversion from varchar to numeric dataType in code. But I still dont know why it is happening.
You're right, it's a conversion issue. That library handles <poi:cell type="numeric" by casting the input values to type FLOAT.
<cfset VARIABLES.Cell.SetCellValue(
JavaCast( "float", THISTAG.GeneratedContent )
) />
Float is an approximate type which can't represent all numbers exactly. That's why some of your "Listing Id" values appear differently in the spreadsheet. For example, a number like 113807820 can't be represented precisely, and becomes 113807824 when cast as a float.
You can see it in action using this example:
<cfscript>
input = "113807820";
writeDump( JavaCast( "string", input ) );
writeDump( numberFormat(JavaCast( "string", input), "0" ));
writeDump( numberFormat(JavaCast( "float", input), "0") );
</cfscript>
Notice the resulting output is different when using float?
113807820 // raw string
113807820 // string + numberFormat
113807824 // float + numberFormat
Full Demo:
<cfscript>
sheet = spreadsheetNew();
// Correct Results: Using Adobe's type "numeric"
sheet.setCellValue("113807820", 1, 1, "numeric");
// Correct Results: Using type "String"
sheet.setCellValue("113807820", 1, 2, "string");
// Wrong Results: Cast as type "Float"
sheet.setCellValue(JavaCast( "float","113807820"), 1, 3);
cfcontent (type="application/vnd.ms-excel"
, variable=spreadsheetReadBinary(sheet)
);
</cfscript>
Result:
I want to write a custom renderer for the h:selectOneMenu component and eventually make use of the description property of the UISelectItem class to add a title a.k.a. tooltip to f:selectItems following BalusC's profound guides in https://stackoverflow.com/a/25512124/3280015 and http://balusc.blogspot.de/2008/08/styling-options-in-hselectonemenu.html.
Now I did extend the com.sun.faces.renderkit.html_basic.MenuRenderer in my own CustomMenuRenderer, registered it with the faces-config.xml and overrode the renderOption method, adding the following code before option tag is terminated by the Responsewriter:
String titleAttributeValue = (String) component.getAttributes().get("title");
if (titleAttributeValue != null) {
String indexKey = component.getClientId(context)
+ "_currentOptionIndex";
Integer index = (Integer) component.getAttributes().get(indexKey);
if (index == null) {
index = 0;
}
component.getAttributes().put(indexKey, ++index);
}
I'm not quite sure I'm doing the indexKey thing right or whether I need it for the title attribute or should use a writer.writeAttribute("title", titleAttributeValue, null); instead because I don't have a list like in the optionClasses tutorial, but the code works so far!
In the actual view definition use case I did:
<h:selectOneMenu value="#{cc.data}">
<f:attribute name="title" value="outerTEST" />
<c:forEach items="#{cc.list}" var="val">
<f:selectItem value="#{val}" itemValue="#{val}" itemLabel="#{val.label}">
<f:attribute name="title" value="innerTEST #{val.description}" />
</f:selectItem>
</c:forEach>
</h:selectOneMenu>
(I just put the #{val.description} there in the title value to clarify my intention, it is currently still empty and I will have to think about how to populate it per element later, but for the sake of the question we can assume it is already filled.)
But now I'm getting the "outerTEST" properly showing up in the title attribute of the option in the resulting XHTML in the Browser, yet I'm not seeing any of the "innerTEST" which would and should be individual per selectItem and which is what this is eventually all about.
I understand the f:selectItem and f:selectItemscomponents do not have their own renderers but rendering of options is generally handled by the MenuRenderer via its renderOption method.
But how then would I add individual titles to the individual selectItems??
Thanks
I have a field called ID-Text that varies in length from 1 to 3000 words stored in a database. I want to display that in a form. Based on its size I want to vary the size of the <h:inputTextarea>. How can I do it.. will I be able to set row property from bean?
The <h:inputTextarea> supports both cols and rows attributes, as you can see from the VLD. Example:
<h:inputTextarea value="#{bean.idText}" rows="#{bean.rowNrs}" cols="40" />
Yeah you can.., Have a look this example...
Bean Class:
private int rowValue;
//getters & setters for rowValue
//if you want column size as "50" then make rowValue is divided by 50.
this.rowValue = para.length()/50; //para is a string which would have database value
Now you have row value in back bean... just use this value as..
<h:inputTextarea id="para" value="#{beanName.idText}" rows="#{beanName.rowValue}" cols="50" />
Secondary Option..
By JavaScript:
You can try by using JavaScript like..
<script language="javascript">
function SetNewSize(textArea) {
if (textArea.value.length > 5) {
textArea.cols = 50;
textArea.rows = 50;
} else {
textArea.cols = 30;
textArea.rows = 20;
}
}
</script>
<textarea name="x" onKeyUp="SetNewSize(this);" cols="15" rows="10"></textarea>
Try similar way to implement this in your inputTextArea...
Initial Question
Can I render the data from one field into multiple columns?
Background
I have created a custom field that contains a drop down list and two text boxes. The idea is that users can select a supplier from the drop down list that is connected to a list of suppliers. Which will get the contact name and number of the supplier and populate the corresponding textboxes.
I have done it this way as it is important to be able to override the contact number and the address but the client wants to see the defaults.
Heres what it looks like:
On saving the new entry the value of the field is saved as follows:
;#1;#Supplier 1;#Contact Name;#01234 567890;#
I chose to save the data in this was so I can treat it like a multi-column field when I render it.
I am using the below code to split the data and override the display pattern for the list view:
<RenderPattern Name="DisplayPattern">
<Switch>
<Expr>
<Column />
</Expr>
<Case Value="" />
<Default>
<!--<Column SubColumnNumber="0" HTMLEncode="TRUE" />
<HTML><![CDATA[<br/>]]></HTML>-->
<Column SubColumnNumber="1" HTMLEncode="TRUE" />
<HTML><![CDATA[ - ]]></HTML>
<Column SubColumnNumber="2" HTMLEncode="TRUE" />
<HTML><![CDATA[ - ]]></HTML>
<Column SubColumnNumber="3" HTMLEncode="TRUE" />
<HTML><![CDATA[]]>]></HTML>
</Default>
</Switch>
</RenderPattern>
This allows me to present the data to the end user as follows:
Question
I would like to be able to display this split data in seperate columns. I notice that the build in title field SharePoint uses has four types of columns you can add to a view for a single field. I am trying to reproduce this kind of functionality so each section of the data can be added or removed from views. Is this possible?
It turns out you have access the list item which meant I was able to simply just add to additional fields within the list item by overriding the UpdateFieldValueInItem method.
Public Overrides Sub UpdateFieldValueInItem()
Me.ItemFieldValue = ddlSupplier.SelectedItem.Value
If Me.Item.Fields.ContainsField(Me.Field.InternalName & "-" & "Telephone") Then
Me.Item(Me.Field.InternalName & "-" & "Telephone") = txtTelephone.Text
End If
End Sub
A much more effective way of doing this.
The Problem
We have a swing based front end for an enterprise application and now are implementing a (for now simpler) JSF/Seam/Richfaces front end for it.
Some of the pages include fields that, when edited, should cause other fields to change as a result. We need this change to be shown to the user immediately (i.e. they should not have to press a button or anything).
I have implemented this successfully using h:commandButton and by adding onchange="submit()" to the fields that cause other fields to change. That way, a form submit occurs when they edit the field, and the other fields are updated as a result.
This works fine functionally, but especially when the server is under significant load (which happens often) the form submits can take a long time and our users have been continuing to edit fields in the meantime which then get reverted when the responses to the onchange="submit()" requests are rendered.
To solve this problem, I was hoping to achieve something where:
Upon editing the field, if required, only that field is processed and only the fields it modifies are re-rendered (so that any other edits the user has made in the meantime do not get lost).
Upon pressing a button, all fields are processed and re-rendered as normal.
The (Unstable) Solution
Okay, I think it might be easiest to show a bit of my page first. Note that this is only an excerpt and that some pages will have many fields and many buttons.
<a4j:form id="mainForm">
...
<a4j:commandButton id="calculateButton" value="Calculate" action="#{illustrationManager.calculatePremium()}" reRender="mainForm" />
...
<h:outputLabel for="firstName" value=" First Name" />
<h:inputText id="firstName" value="#{life.firstName}" />
...
<h:outputLabel for="age" value=" Age" />
<h:inputText id="age" value="#{life.age}">
<f:convertNumber type="number" integerOnly="true" />
<a4j:support event="onchange" ajaxSingle="true" reRender="dob" />
</h:inputText>
<h:outputLabel for="dob" value=" DOB" />
<h:inputText id="dob" value="#{life.dateOfBirth}" styleClass="date">
<f:convertDateTime pattern="dd/MM/yyyy" timeZone="#{userPreference.timeZone}" />
<a4j:support event="onchange" ajaxSingle="true" reRender="age,dob" />
</h:inputText>
...
</a4j:form>
Changing the value of age causes the value of dob to change in the model and vice versa. I use reRender="dob" and reRender="age,dob" to display the changed values from the model. This works fine.
I am also using the global queue to ensure ordering of AJAX requests.
However, the onchange event does not occur until I click somewhere else on the page or press tab or something. This causes problems when the user enters a value in say, age, and then presses calculateButton without clicking somewhere else on the page or pressing tab.
The onchange event does appear to occur first as I can see the value of dob change but the two values are then reverted when the calculateButton request is performed.
So, finally, to the question: Is there a way to ensure that the model and view are updated completely before the calculateButton request is made so that it does not revert them? Why is that not happening already since I am using the AJAX queue?
The Workarounds
There are two strategies to get around this limitation but they both require bloat in the facelet code which could be confusing to other developers and cause other problems.
Workaround 1: Using a4j:support
This strategy is as follows:
Add the ajaxSingle="true" attribute to calculateButton.
Add the a4j:support tag with the ajaxSingle="true" attribute to firstName.
The first step ensures that calculateButton does not overwrite the values in age or dob since it no longer processes them. Unfortunately it has the side effect that it no longer processes firstName either. The second step is added to counter this side effect by processing firstName before calculateButton is pressed.
Keep in mind though that there could be 20+ fields like firstName. A user filling out a form could then cause 20+ requests to the server! Like I mentioned before this is also bloat that may confuse other developers.
Workaround 2: Using the process list
Thanks to #DaveMaple and #MaxKatz for suggesting this strategy, it is as follows:
Add the ajaxSingle="true" attribute to calculateButton.
Add the process="firstName" attribute to calculateButton.
The first step achieves the same as it did in the first workaround but has the same side effect. This time the second step ensures that firstName is processed with calculateButton when it is pressed.
Again, keep in mind though that there could be 20+ fields like firstName to include in this list. Like I mentioned before this is also bloat that may confuse other developers, especially since the list must include some fields but not others.
Age and DOB Setters and Getters (just in case they are the cause of the issue)
public Number getAge() {
Long age = null;
if (dateOfBirth != null) {
Calendar epochCalendar = Calendar.getInstance();
epochCalendar.setTimeInMillis(0L);
Calendar dobCalendar = Calendar.getInstance();
dobCalendar.setTimeInMillis(new Date().getTime() - dateOfBirth.getTime());
dobCalendar.add(Calendar.YEAR, epochCalendar.get(Calendar.YEAR) * -1);
age = new Long(dobCalendar.get(Calendar.YEAR));
}
return (age);
}
public void setAge(Number age) {
if (age != null) {
// This only gives a rough date of birth at 1/1/<this year minus <age> years>.
Calendar calendar = Calendar.getInstance();
calendar.set(calendar.get(Calendar.YEAR) - age.intValue(), Calendar.JANUARY, 1, 0, 0, 0);
setDateOfBirth(calendar.getTime());
}
}
public Date getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(Date dateOfBirth) {
if (notEqual(this.dateOfBirth, dateOfBirth)) {
// If only two digits were entered for the year, provide useful defaults for the decade.
Calendar calendar = Calendar.getInstance();
calendar.setTime(dateOfBirth);
if (calendar.get(Calendar.YEAR) < 50) {
// If the two digits entered are in the range 0-49, default the decade 2000.
calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) + 2000);
} else if (calendar.get(Calendar.YEAR) < 100) {
// If the two digits entered are in the range 50-99, default the decade 1900.
calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) + 1900);
}
dateOfBirth = calendar.getTime();
this.dateOfBirth = dateOfBirth;
changed = true;
}
}
What is the scope of your bean? When the button is executed, it's a new request and if your bean is in request scope, then previous values will be gone.
So, finally, to the question: Is there a way to ensure that the model and view are updated completely before the calculateButton request is made so that it does not revert them?
What you could do is disable the submit button from the time you initiate your ajax request until your ajax request completes. This will effectively prevent the user from pressing the submit button until it resolves:
<a4j:support
event="onblur"
ajaxSingle="true"
onsubmit="jQuery('#mainForm\\:calculateButton').attr('disabled', 'disabled');"
oncomplete="jQuery('#mainForm\\:calculateButton').removeAttr('disabled');" />
One thing that is additionally helpful with this approach is if you display an "ajaxy" image to the end user while this is happening so that they intuitively understand that stuff is happening that will resolve soon. You can show this image in the onsubmit method as well and then hide it oncomplete.
EDIT:
The issue may just be that you need to add the process attribute to your a4j:commandButton. This attribute specifies the components by id that should participate in the model update phase:
<a4j:commandButton
id="calculateButton"
value="Calculate"
action="#{illustrationManager.calculatePremium()}"
process="firstName"
reRender="mainForm" />
i guess i can provide another work-around.
we should have two flags on js level:
var requestBlocked = false;
var requestShouldBeSentAfterBlock = false;
h:inputText element blocks the ajax request on blur:
<h:inputText id="dob" onblur="requestBlocked = true;" ...
a4j:commandButton sends the request if requestBlocked is false:
<a4j:commandButton id="calculateButton"
onkeyup="requestShouldBeSentAfterBlock = requestBlocked;
return !requestBlocked;" ...
a4j:support sends the request if requestShouldBeSentAfterBlock is true:
<a4j:support event="onchange"
oncomplete="requestBlocked = false;
if (requestShouldBeSentAfterBlock) {
requestShouldBeSentAfterBlock = false;
document.getElementById('calculateButton').click();
}" ...
since oncomplete block works after all needed elements are re-rendered, things will work in the needed order.
It looks like a reason is somewhere in getters/setters.
For example one of the possibilities: when no ajaxSingle=true and the calculate button is clicked. All the values are set to the model, so both setAge and setDateOfBirth are invoked. And it may happen so that setDateOfBirth is invoked before setAge.
Looking closer at setAge method it in fact resets the date to the beginning of the year, even though the date could have had the right year already.
I would recommend simplifying the logic first. For example have separate disconnected fields for year and birth day, and check if the issue is still reproducible to find minimal required conditions to reproduce.
Also from user experience standpoint, a common practice is to do something like the following, so the event fires once user stops typing:
<a4j:support event="onkeyup"
ajaxSingle="true"
ignoreDupResponses="true"
requestDelay="300"
reRender="..."/>