selectOneChoice is not able to cache the result in same task flow in oracle MAF - oracle-maf

I have a task flow with two AMX pages.
First page has a selectOneChoice (Product list) which is created from data control (having allProducts(a ArrayList of a ProductPOJO class)).
I dragged and drop allProducts from my dataControl to AMX page. and got code as-
<amx:selectOneChoice value="#{bindings.allProducts.inputValue}" label="Products" id="soc1"
valueChangeListener="#{pageFlowScope.lcBean.onProductChange}">
<amx:selectItems value="#{bindings.allProducts.items}" id="si1"/>
</amx:selectOneChoice>
and set a method onProductChange(ValueChangeEvent valueChangeEvent) inside a bean class where i am getting the selected row from selectOneChoice and doing further operation.
public void onProductChange(ValueChangeEvent valueChangeEvent) {
AmxAttributeBinding productList =
(AmxAttributeBinding) AdfmfJavaUtilities.evaluateELExpression("#{bindings.allProducts}");
AmxIteratorBinding amxListIterator = productList.getIteratorBinding();
BasicIterator basicIterator = amxListIterator.getIterator();
ProductPOJO currentProduct = (ProductPOJO) basicIterator.getDataProvider();
System.out.println("InSide onProductChange"+currentProduct.getProduct());
ValueExpression ve;
ve = AdfmfJavaUtilities.getValueExpression("#{pageFlowScope.nupMinimumsFlag}", Integer.class);
ve.setValue(AdfmfJavaUtilities.getAdfELContext(), currentProduct.getNupMinimumsFlag());
ve = AdfmfJavaUtilities.getValueExpression("#{pageFlowScope.productFlag}", Integer.class);
ve.setValue(AdfmfJavaUtilities.getAdfELContext(), currentProduct.getProductFlag());
ve = AdfmfJavaUtilities.getValueExpression("#{pageFlowScope.product}", String.class);
ve.setValue(AdfmfJavaUtilities.getAdfELContext(), currentProduct.getProduct());
}
Issue is :- when we select some value in selectOneChoice and then click on submit to go to next page. and then coming back to first page. Value of selectOneChoice is getting reset to default value(first value from collection). Its not able to cache the old(selected) value.

The working behaviour of select one choice is different on Oracle MAF. It is not able to cache the Object.
In first approach I was trying to cache an object.
To cache value on selectOneChoice we will have to get the index of selected choice and save it in your datacontrol class.
I got it fixed as below.
AMX Code :-
<amx:selectOneChoice value="#{bindings.selectedProduct.inputValue}" label="Products" id="soc1"
valueChangeListener="#{bindings.onProductChange.execute}" required="true"
showRequired="true">
<amx:selectItems value="#{bindings.allPoroducts.items}" id="si1"/>
</amx:selectOneChoice>
Java Code :-
public void onProductChange() {
System.out.println("InSide onProductChange" + selectedProduct);
ProductPOJO currentProduct = allPoroducts.get(Integer.parseInt(selectedProduct));
System.out.println("InSide onProductChange" + currentProduct.getProduct());
ValueExpression ve = AdfmfJavaUtilities.getValueExpression("#{pageFlowScope.nupMinimumsFlag}", Integer.class);
ve.setValue(AdfmfJavaUtilities.getAdfELContext(), currentProduct.getNupMinimumsFlag());
ve = AdfmfJavaUtilities.getValueExpression("#{pageFlowScope.productFlag}", Integer.class);
ve.setValue(AdfmfJavaUtilities.getAdfELContext(), currentProduct.getProductFlag());
ve = AdfmfJavaUtilities.getValueExpression("#{pageFlowScope.product}", String.class);
ve.setValue(AdfmfJavaUtilities.getAdfELContext(), currentProduct.getProduct());
}
where 'private String selectedProduct;' is a string type object. so when we will select or change value on selectonechoice it will return numbers in string format like(0, 1, 2, 3, 4, 5 basically index values). which we will convert it to int and will get the value from product array with that index.

Related

Move records up/down in grid with "SortOrder" field and "Up"/"Down" buttons

Current customization project I'm working on has the requirement of displaying / editing a grid with a "Sort Order" for records. The "SortOrder" field is read only with up/down buttons to allow the user to re-order the items in the grid.
The "SortOrder" column in the DAC is a simple Int field.
The PXSelect statement for the grid is using a OrderBy>> to display the records.
The Grid in the ASPX is a defined with "SyncPosition= true"
I've added an Up/Down button that increments/decrements the "SortOrder" value for the current selected record.
The issue that I'm running into is that the first time "Up" or "Down" is clicked, the "SortOrder" field is updated however the rows do not move. Once I click Save to persist the update, the grid then refreshes with the right order.
I've looked through the the rest of the code but all other situations where this is used is for treeviews, not grids.
I've tried adding a View.RequestRefresh() at the end of my Action but this doesn't cause the reorder.
What would be the best way without a Persist after each move to get the Grid to update and reflect the current order from the cache values? As usual I'm assuming I'm overlooking something simple.
Any advice would be appreciated.
I had a look at the generic inquiry designer source code - it has an up/down button in the grid to reorder the fields. The views don't have an OrderBy clause:
public PXSelect<GIFilter, Where<GIFilter.designID, Equal<Current<GIDesign.designID>>>> Parameters;
OrderBy is not necessary because the LineNbr field is a key field - system automatically orders the records by the key fields.
public abstract class lineNbr : IBqlField { }
[PXDBInt(IsKey = true)]
[PXDefault]
[PXLineNbr(typeof(GIDesign))]
[PXParent(typeof(Select<GIDesign,
Where<GIDesign.designID, Equal<Current<GIFilter.designID>>>>))]
public virtual int? LineNbr { get; set; }
The code for the button looks like this:
[PXButton(ImageKey = Sprite.Main.ArrowUp, Tooltip = ActionsMessages.ttipRowUp)]
[PXUIField(DisplayName = ActionsMessages.RowUp, MapEnableRights = PXCacheRights.Update)]
protected void moveUpFilter()
{
if (this.Parameters.Current == null)
return;
GIFilter prev = PXSelect<GIFilter, Where<GIFilter.designID, Equal<Current<GIDesign.designID>>, And<GIFilter.lineNbr, Less<Current<GIFilter.lineNbr>>>>, OrderBy<Desc<GIFilter.lineNbr>>>.Select(this);
if (prev != null)
this.SwapItems(this.Parameters.Cache, prev, this.Parameters.Current);
}
[PXButton(ImageKey = Sprite.Main.ArrowDown, Tooltip = ActionsMessages.ttipRowDown)]
[PXUIField(DisplayName = ActionsMessages.RowDown, MapEnableRights = PXCacheRights.Update)]
protected void moveDownFilter()
{
if (this.Parameters.Current == null)
return;
GIFilter next = PXSelect<GIFilter, Where<GIFilter.designID, Equal<Current<GIDesign.designID>>, And<GIFilter.lineNbr, Greater<Current<GIFilter.lineNbr>>>>, OrderBy<Asc<GIFilter.lineNbr>>>.Select(this);
if (next != null)
this.SwapItems(this.Parameters.Cache, next, this.Parameters.Current);
}
The SwapItems function is shared between all the move up / move down actions:
private void SwapItems(PXCache cache, object first, object second)
{
object temp = cache.CreateCopy(first);
foreach (Type field in cache.BqlFields)
if (!cache.BqlKeys.Contains(field))
cache.SetValue(first, field.Name, cache.GetValue(second, field.Name));
foreach (Type field in cache.BqlFields)
if (!cache.BqlKeys.Contains(field))
cache.SetValue(second, field.Name, cache.GetValue(temp, field.Name));
cache.Update(first);
cache.Update(second);
}
Finally, there's a bit of JavaScript code in the ASPX code - it may or may not be what you're missing to get the feature to work correctly; i'm not exactly sure what it's doing but would encourage you to open SM208000.aspx in an editor and look for commandResult. Also check out the CallbackCommands that are defined on the grids which support up/down - it may have something to do with it.

Xpages "filter by category name" with two fields values (Restricting a View to Multiple Categories)

I want to design a view that display only projects of selected customer and services, so I created a view that customer and service columns are categorized and a view panel for this view. If I put
document1.getDocument().getItemValueString("Customer")
it works with a restrict single category. How I can do this with tow categorized columns.
Thanks in advance
Could you implement a repeat within a repeat for this?
The first repeat would capture your first category. Then inside you have a panel and a repeat and the second repeat would further filter the datasource by the second category
You cheat. Have one column that concatenates customer and project. Grab that column in your selection and split it in code.
Then use the selected value for the one column. Lets presume your original columns are customer and project. So your new column would have the formula customer+"~"+project. You then read this values to populate a SSJS object or a variable, so you can retrieve the customers (first dropdown) and the projects (second dropdown). In a dropdown you can use the format Display|Value, so a good approach is to have the value in the format customer~project.
As said you can do that in Java or JavaScript. Since I like Java's collection framework a lot, here's the Java version:
import java.util.Set;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import lotus.domino.Database;
import lotus.domino.NotesException;
import lotus.domino.View;
import lotus.domino.ViewEntry;
import lotus.domino.ViewEntryCollection;
public class SplitCategoryBean {
private static final String SEPARATOR = "~";
private final Map<String,Set<String>> allCategories = new TreeMap<String, Set<String>>();
public void populate(Database db, String viewName) throws NotesException {
View v = db.getView(viewName);
ViewEntryCollection vec = v.getAllEntries();
ViewEntry ve = vec.getFirstEntry();
while (ve != null) {
ViewEntry nextVE = vec.getNextEntry(ve);
this.addEntry(ve.getColumnValues().get(0).toString());
ve.recycle();
ve = nextVE;
}
vec.recycle();
v.recycle();
}
private void addEntry(String combinedCategory) {
String[] splitCategory = combinedCategory.split(SEPARATOR);
String key = splitCategory[0];
String value = splitCategory[1];
Set<String> thisCategory = (this.allCategories.containsKey(key)) ? this.allCategories.get(key): new TreeSet<String>();
thisCategory.add(value+"|"+combinedCategory);
this.allCategories.put(key, thisCategory);
}
public Set<String> getFirstCategory() {
return this.allCategories.keySet();
}
public Set<String> getSecondCategoryReadyForDropDown(String key) {
return this.allCategories.get(key);
}
}
You would configure that as a managed bean (viewScope) and in the queryOpen you call the populate method. Then you can easily bind your first selection to #{beanName.firstCategory} for the selection and e.g. #{viewScope.curCustomer} for the value. The second drop-down you use the rendered="#{viewScope.curCustomer}" so it only shows when the customer is selected. And you bind the selections to #{javascript:beanName.getSecondCategoryReadyForDropDown(viewScope.curCustomer);
Put refreshs on the change events and render the view only if you have a project selected.
Does that work for you?
Thanks guys
I created a view that the first column is categorized and its value is Customer+Service then put
document1.getDocument().getItemValueString("Customer") + document1.getDocument().getItemValueString(“Service")
in "filter by category name" of view panel it works now.

Get Values from a valueExpression JSF1.2

I am trying to get the value contained in a value expression. I need to use it for some validation. When I set the value expression in an HTMLOutput it is displayed on the screen. Please suggest what am I doing wrong?
HtmlOutputText output = new HtmlOutputText();
ve = ef.createValueExpression(elCtx, "#{dynamicRow["+ i + "]}", String.class);
//I have tried all options here that I came across.
//Either test1 , test2 , test3 should have the value ve has, but all of them have value "".
String test1 = (String)ve.getValue(elCtx);
String test2 = (String)output.getValue();
Application app = fCtx.getApplication( );
String test3 = (String) app.evaluateExpressionGet(fCtx, "#{dynamicRow["+ i + "]}", String.class);
output.setValueExpression("value", ve);
//the ve gets displayed in the column with correct string values and not ""
column.getChildren().add(output);
// I also tried ValueReference as given in ValueExpression: how to get base Object?
ValueReference valueReference = ve.getValueReference(elCtx);
//an exception is thrown here javax.el.PropertyNotFoundException: Target Unreachable, identifier 'dynamicRow' resolved to null
Object base = valueReference.getBase();
Object property = valueReference.getProperty();
where my table has the following code:
FacesContext fCtx = FacesContext.getCurrentInstance();
ELContext elCtx = fCtx.getELContext();
ExpressionFactory ef = fCtx.getApplication().getExpressionFactory();
HtmlExtendedDataTable dynamicDataTable = new HtmlExtendedDataTable();
HtmlDatascroller datascroller = new HtmlDatascroller();
ValueExpression ve = ef.createValueExpression(elCtx,"#{myController.dynamicList}", List.class);
dynamicDataTable.setValueExpression("value", ve);
dynamicDataTable.setVar("dynamicRow");
The var value dynamicRow is only in scope when the table places it into scope during processing. This is done by the various process* methods on component.
See setRowIndex(int) documentation:
Save current state information for all descendant components (as described below).
Store the new row index, and pass it on to the DataModel associated with this UIData instance.
If the new rowIndex value is -1:
If the var property is not null, remove the corresponding request scope attribute (if any).
Reset the state information for all descendant components (as described below).
If the new rowIndex value is not -1:
If the var property is not null, call getRowData() and expose the resulting data object as a request scope attribute whose key is
the var property value.
Reset the state information for all descendant components (as described below).

Is it possible to use custom JSF converter for UISelectOne, depending on value of Bean property or UIInputText value?

I need to get help with a problem.
I have an application, which displays data from database.
In database I am using one column for identifying an object with changes in time (version of object).
I mean that rows have different IDs, but same OBJECT_ID.
They are also differentiated by validity DATE columns (VALID_FROM and VALID_TO), so i can find the proper version of object in selected time.
When page is shown to user, he sets DATE for displaying data.
There is form on page, with Select Boxes (their items are object from database).
I need to show only items, that satisfy the validity condition.
Everything is OK before form handling.
I am using custom converter, that converts String value (ID of row in database table) to Object of my model, but I need two values for row to find. (ID and DATE from user form).
Converter method getAsObject gets only one VALUE parameter (ID from Select Box).
How to get second parametr to work?
Can i find it from Bean or from JSF input object?
If there is some way that includes using <f:attribute/>, is there way to set <f:attribute/> by Java code in Bean? (Form is generated by bean method).
I am sorry for my English, i try my best :o)
P.S.: Date parameter is important in sqlMap because of JOIN used. I search by ID, but other tables are joined by OBJECT_ID (version) => and i need to get one row only
Thanks for answers and have a nice day
Worsik
Edit:
Select Boxes are generated by this code:
uiInput = new HtmlSelectOneMenu();
UISelectItems items = new UISelectItems();
items.setValueExpression("value", createValueExpression(
"#{myBean.referencedList." + item.getName() + "}",
List.class));
uiInput.getChildren().add(items);
Converter method looks like this:
public Object getAsObject(FacesContext context, UIComponent component, String value)
{
if (value.equals("")) {
return null;
}
Class entityType = component.getValueExpression("value").getType(
FacesContext.getCurrentInstance().getELContext());
// TODO: need to get date value from form
Date day = new Date();
return myService(context).findEntityById(entityType, Long.valueOf(value), day);
}
entityType is Class of model for object from database
myService calls Dao object and sqlMap is called
select * from object_table as t1
JOIN other_table as t2 ON t1.object_fk = t2.object_id // (or version_id)
where t1.id = #id:INTEGER#
and t2.valid_from <= #day:DATE#
and t2.valid_to >= #day:DATE#
I figured out how to do this by <f:attribute/> by code in converter:
Date day = (Date) component.getAttributes().get("dateForSearch");
and in bean method after generating SelectBox with code:
uiInput.getAttributes().put("dateForSearch", getSelectedDate());
and in pages without dynamic generating of forms i used:
<SelectOneMenu>
...
<f:attribute name="dateForSearch" value="#{myBean.selectedDate}" />
</SelectOneMenu>
I hope that this will not be only self answered question and will help somebody else

What is the correct way to format SPGridView values being displayed?

Problem
As we know, SharePoint saves data in database in plain text. Some fields even have concatenated strings like <id>;#<value> for user fields. Percents are saved as doubles (1.00000000000000 for 100%) and etc.
Ofcourse, I want to display data as they are displayed in lists.
What should I do?
Should I use derived SPBoundField to format values (Which I actually did and it works fine until you want to filter (probably SPBoundField won't format me values because i use ObjectDataSource not list and with reflector I saw if there are SPListItems in datasource, then it formats correctly. Not my case)
alt text http://img199.imageshack.us/img199/2797/ss20090820110331.png
Or must I loop through all the DataTable and format each row accordingly?
What are Your techniques?
Thank you.
Here is how I solved this issue.
<asp:TemplateField HeaderText="Campaign Members">
<ItemTemplate>
<%# RemoveCharacters(Eval("CampaignMembers").ToString())%>
</ItemTemplate>
</asp:TemplateField>
// Make sure declare using System.Text.RegularExpression;
protected string RemoveCharacters(object String)
{
string s1 = String.ToString();
string newString = Regex.Replace(s1, #"#[\d-];", string.Empty);
newString = Regex.Replace(newString, "#", " ");
return newString.ToString();
}
I normaly use ItemTemplates that inherit from ITemplate. With in the ItemTemplate I use the SPFieldxxxValue classes or some custom formating code. This saves looping through the DataTable and the ItemTemplates can be reused.
The ItemTemplates are attached in Column Binding
E.G
// Normal Data Binding
SPBoundField fld = new SPBoundField();
fld.HeaderText = field.DisplayName;
fld.DataField = field.InternalName;
fld.SortExpression = field.InternalName;
grid.Columns.Add(fld);
// ItemTemplate Binding
TemplateField fld = new TemplateField();
fld.HeaderText = field.DisplayName;
fld.ItemTemplate = new CustomItemTemplateClass(field.InternalName);
fld.SortExpression = field.InternalName;
grid.Columns.Add(fld);
An example of a ItemTemplate
public class CustomItemTemplateClass : ITemplate
{
private string FieldName
{ get; set; }
public CustomItemTemplateClass(string fieldName, string formatString)
{
FieldName = fieldName;
}
#region ITemplate Members
public void InstantiateIn(Control container)
{
Literal lit = new Literal();
lit.DataBinding += new EventHandler(lit_DataBinding);
container.Controls.Add(lit);
}
#endregion
void lit_DataBinding(object sender, EventArgs e)
{
Literal lit = (Literal)sender;
SPGridViewRow container = (SPGridViewRow)lit.NamingContainer;
string fieldValue = ((DataRowView)container.DataItem)[FieldName].ToString();
//Prosses Filed value here
SPFieldLookupValue lookupValue = new SPFieldLookupValue(fieldValue);
//Display new value
lit.Text = lookupValue.LookupValue;
}
}
Here are a few options. I don't know the output of all of them (would be a good blog post) but one of them should do what you want:
SPListItem.GetFormattedValue()
SPField.GetFieldValue()
SPField.GetFieldValueAsHtml()
SPField.GetFieldValueAsText()
It may also be handy to know that if you ever want to make use of the raw values then have a look at the SPField*XYZ*Value classes. For example the form <id>;#<value> you mention is represented by the class SPFieldUserValue. You can pass the raw text to its constructor and extract the ID, value, and most usefully User very easily.
I would suggest either to format the values before binding them to the spgridview. Linq and an anonymous type is preffered or to call a code behind function on the field that needs the formatting upon binding.
DataField='<%# FormatUserField(Eval("UserFieldName")) %>'
or...maybe a templated field?
After all, i did have not know any other solution to loop through DataTable rows and format them accordingly.
If your SPGridView's data source is list, try out SPBoundField.

Resources