I need to display <p:selectManyCheckbox> items with images. I tried to display images with in <p:selectOneRadio>. It works fine. I am programmatically adding components on UI. This is my code.
answerRadio.setLayout("custom"); //answerRadio is SelectOneRadio
customPnl = (PanelGrid) app.createComponent(PanelGrid.COMPONENT_TYPE);
customPnl.setId("pnl"+qstnCnt);
customPnl.setColumns(3);
radioBtn = (RadioButton) app.createComponent(RadioButton.COMPONENT_TYPE);
radioBtn.setId("opt"+qstnAnsIndx);
radioBtn.setFor("ID of answerRadio");
radioBtn.setItemIndex(ansIndx);
customPnl.getChildren().add(radioBtn);
outPnl.getChildren().add(answerRadio); //outPnl is OutputPanel that include answerRadio
outPnl.getChildren().add(customPnl);
That's <p:selectOneRadio> with images.
I'd like to use <p:selectManyCheckbox> in same way. But PrimeFaces has only a <p:radioButton> for custom layoue and not a <p:checkbox> like that. How can I achieve it anyway? How can I display <p:selectManyCheckbox> items with images ?
That's not possible with <p:selectManyCheckbox>. Your best bet is to just use a bunch of <p:selectBooleanCheckbox> components instead and change the model to be Map<Entity, Boolean> instead of List<Entity>. You can loop over it using <ui:repeat>.
E.g. (normal XHTML variant; I am not going to advocate the Java createComponent() equivalent):
<ui:repeat value="#{bean.entities}" var="entity">
<p:selectBooleanCheckbox value="#{bean.selection[entity]}" />
... (you can put here image, label, anything)
</ui:repeat>
with
private List<Entity> entites;
private Map<Entity, Boolean> selection;
#PostConstruct
public void init() {
entities = service.list();
selection = new HashMap<>(); // No need to prefill it!
}
To check which ones are selected, loop over the map in action method:
List<Entity> selectedEntities = new ArrayList<>();
for (Entry<Entity, Boolean> entry : selection.entrySet()) {
if (entry.getValue()) {
selectedEntities.add(entry.getKey());
}
}
Related
I'm developing a custom field type that extends from LookupEx. The purpose of the control is to allow the user to select from the drop down, and based upon that selection populated additional fields of type Multilist.
public class CascadingDroplink : LookupEx
{
private const string sourceFieldName = "CascadingDroplink";
protected override void DoRender(System.Web.UI.HtmlTextWriter output)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("<script type=\"text / javascript\">");
sb.AppendLine(" var $j = jQuery.noConflict(); ");
sb.AppendLine(string.Format(" $j(\"#{0}\").change(function(event) {{ ", this.ID));
sb.AppendLine(string.Format("scForm.invoke('contenteditor:save', event);"));
sb.AppendLine(string.Format(" }});"));
sb.AppendLine("</script>");
output.Write(sb.ToString());
Fromt the embeded javascript, you can see I have found a way to perform a callback by simulating the click even of the Save button:
scForm.invoke('contenteditor:save', event);
And while this works, the content editor is refreshed, and the Multilist fields are updated with custom datasources, saving isn't ideal because there could be validation present.
How can the contenteditor be refreshed, as if the area was within a callback panel, without calling save?
Thanks!
In my application I have a list of keys (strings), where the user can select one of them. In the user-interface, the keys will be output according to the current locale:
<h:selectOneMenu value="#{bean.selectedKey}">
<f:selectItems value="#{bean.allKeys}" var="_key" itemLabel="#{msgs[_key]}" />
</h:selectOneMenu>
My setup uses a standard resource-bundle configured in faces-config.xml as explained in this answer by BalusC. msgs in the example above is the name of the resource-bundle variable.
What I want now, is the items from the selectOneMenu to be sorted in alphabetic order. Of course the order depends on the used locale. The problem is, I can't/won't do the sorting in the backing-bean, as I don't know how the JSF-page will output the keys.
This problem seems quite generic to me, so I'm wondering what the best practice is to solve this kind of problem.
(Of course the problem is not only applicable to selectOneMenu. Any list/collection that will be output in the user-interface suffers from the same problem.)
You've basically 2 options.
Sort in client side with a little help of JS. I'll for simplicity assume that you're using jQuery.
<h:selectOneMenu ... styleClass="ordered">
$("select.ordered").each(function(index, select) {
var $select = $(select);
$select.find("option").sort(function(left, right) {
return left.text == right.text ? 0 : left.text < right.text ? -1 : 1;
}).appendTo($select);
if (!$select.find("option[selected]").length) {
select.options.selectedIndex = 0;
}
});
Sort in server side wherein you create List<SelectItem> and grab #{msgs} via injection. Assuming that you're using CDI and thus can't use #ManagedProperty("#{msgs}"), you'd need to create a producer for that first. I'll for simplicity assume that you're using OmniFaces (which is also confirmed based on your question history).
public class BundleProducer {
#Produces
public PropertyResourceBundle getBundle() {
return Faces.evaluateExpressionGet("#{msgs}");
}
}
Then you can make use of it as follows in the backing bean associated with <f:selectItems value>:
private static final Comparator<SelectItem> SORT_SELECTITEM_BY_LABEL = new Comparator<SelectItem>() {
#Override
public int compare(SelectItem left, SelectItem right) {
return left.getLabel().compareTo(right.getLabel());
}
};
private List<SelectItem> allKeys;
#Inject
private PropertyResourceBundle msgs;
#PostConstruct
public void init() {
allKeys = new ArrayList<>();
for (String key : keys) {
allKeys.add(new SelectItem(key, msgs.getString(key)));
}
Collections.sort(allKeys, SORT_SELECTITEM_BY_LABEL);
}
And reference it directly without var as follows:
<f:selectItems value="#{bean.allKeys}" />
I'm currently working on an Adobe AIR application which is targeting the iPad2 as the hardware platform, and can not get decent scrolling performance on one of the screens. I'm using a spark list, with a custom item renderer like so:
<s:List id="productList" top="116" bottom="0" left="10" right="10"
width="100%"
visible="true" includeInLayout="true"
height="0"
maxHeight="500"
opaqueBackground="#ffffff"
itemRenderer="myRenderer">
</s:List>
Originally, I was using an .mxml renderer, but after seeing the nasty performance I decided to roll my own, extending UIComponent (I've left off the package and braces to save on horizontal space):
import mx.controls.listClasses.IListItemRenderer;
import mx.core.UIComponent;
import mx.events.FlexEvent;
import mx.utils.ColorUtil;
import spark.components.Label;
import spark.components.TextInput;
public final class OrderViewProductLineTestIR extends UIComponent implements IListItemRenderer
{
public function OrderViewProductLineTestIR()
{
super();
}
// Internal variable for the property value.
private var _data:Object;
private var productName:Label;
private var orderQty:TextInput;
private var stockQty:TextInput;
// Make the data property bindable.
[Bindable("dataChange")]
// Define the getter method.
public function get data():Object
{
return _data;
}
// Define the setter method, and dispatch an event when the property
// changes to support data binding.
public function set data(value:Object):void
{
_data = value;
invalidateProperties();
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
override protected function createChildren():void
{
super.createChildren();
productName = new Label();
// productName.visible = true;
addChild(productName);
orderQty = new TextInput();
addChild(orderQty);
stockQty = new TextInput();
addChild(stockQty);
}
override protected function commitProperties():void
{
super.commitProperties();
productName.text = _data.Name;
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
productName.move(0, 0);
productName.setActualSize(250, 48);
orderQty.move(270, 0);
orderQty.setActualSize(100, 48);
stockQty.move(390, 0);
stockQty.setActualSize(100, 48);
}
override protected function measure():void
{
super.measure();
measuredWidth = 490;
measuredHeight = 48;
}
}
As you can see this is pretty light-weight, yet my dataprovider contains upwards of 100 items, and 11 of them can be on screen at any one time. Everything I've read around increasing performance for scrolling revolves around using opaqueBackground and cacheAsBitmap, however no matter what I try neither help here. Using cacheAsBitmap at the list level doesn't help as the item renderer recycling kicks in once you've scrolled more than a couple of lines requiring the whole thing to be re-rendered, and using it at the item renderer level is still horribly slow when scrolling fast — presumably because many are being recycled at once during a very fast scroll.
I know the iPad should have no problem blitting a screenful of information in a frame at 60 fps, yet when I scroll quickly I'm seeing it struggle to make 10 fps (from sight). So the question: have I missed something obvious, or is this to be expected due to the number of layers (and vector rendering) involved when using AIR? For the record, I have tried changing the render mode for the application and tried changing the frame rate to eliminate the obvious.
This is a bit of guessing, but can the itemRenderer be optimized? Do all the children need to be positioned and sized every time component redraws? Do you need to update productName.text every time commitProperties run?
Here is how I might modify things:
import mx.controls.listClasses.IListItemRenderer;
import mx.core.UIComponent;
import mx.events.FlexEvent;
import mx.utils.ColorUtil;
import spark.components.Label;
import spark.components.TextInput;
public final class OrderViewProductLineTestIR extends UIComponent implements IListItemRenderer
{
public function OrderViewProductLineTestIR()
{
super();
}
// Internal variable for the property value.
private var _data:Object;
// Add a dataChanged property
private var dataChanged :Boolean = false
private var productName:Label;
private var orderQty:TextInput;
private var stockQty:TextInput;
// Make the data property bindable.
[Bindable("dataChange")]
// Define the getter method.
public function get data():Object
{
return _data;
}
// Define the setter method, and dispatch an event when the property
// changes to support data binding.
public function set data(value:Object):void
{
_data = value;
// switch the dataChanged flag
dataChanged = true;
invalidateProperties();
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
override protected function createChildren():void
{
super.createChildren();
productName = new Label();
// productName.visible = true;
addChild(productName);
orderQty = new TextInput();
addChild(orderQty);
stockQty = new TextInput();
addChild(stockQty);
}
override protected function commitProperties():void
{
super.commitProperties();
// Only update the display if the data actually changed
If(dataChanged){
productName.text = _data.Name;
dataChanged = false;
}
}
// add variable to tell whether the component's children have been sized and positioned or not
// since they have static locations, no need to set these each time
protected var compChildrenSized :Boolean = false;
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(!compChildrenSized){
productName.move(0, 0);
productName.setActualSize(250, 48);
orderQty.move(270, 0);
orderQty.setActualSize(100, 48);
stockQty.move(390, 0);
stockQty.setActualSize(100, 48);
compChildrenSized = true;
}
}
override protected function measure():void
{
super.measure();
measuredWidth = 490;
measuredHeight = 48;
}
}
I guess I'll add that I'm not sure measure will ever run. What happens if you replace the textInputs with labels? Are you using Flex 4.6, and if so are you using StyleableStageText (AKA StageText) or the 4.5 skin which uses StyleableTextField? I wonder if StageText scrolling could kill performance because it hangs out above the Flash Display list.
What happens if you remove the textInput completely and replace with labels?
These are little things, and I'm not sure if they'll help.
Try just this on your "Spark List component"
1/ add a layout (horizontal or vertical depending on what you want ;)
2/ add attribute "useVirtualLayout=true" on your list component !
And tell me what's the result ... but i think it will be smoother (very very smoother),
because when you add your elements to the list, all the components are rendered, even if
they are out of the layout ... so whith "useVirtualLayout" just items that are on the viewport are rendered others are destroyed ...
See ya ! and have fun whith Flash Builder ! it's a so cool technology to have a multi support app !
A bit late. but.
Its true that its better to use starling and feather if you want something close to native performance.
But you can still optimise your renderer.
Please dont use binding anywhere in a Flex mobile application.
Extending UIComponent is the right thing to do.
I think it would be better to not use invalidation and set your properties in the data setter.
Hope it helps.
I would like to know how to update a panel when we select a drop down chioce values, that is in onUpdate() method.
My custom panel has AjaxFallbackDefaultDataTable.
Below is Panel and drop down components code. When user selects date, I want to replace my entire Panel. Currently I have commened that target.addComponent code, but I want to have implementation here. Any suggestions?
List<DealHistory> dealHistoryList = ServicesCaller
.getAllDealHistoryRecords();
DealHistoryTablePanel dealHistoryTablePanel = new DealHistoryTablePanel(
"deal_history_table_panel", dealHistoryList);
dealHistoryTablePanel.setOutputMarkupId(true);
add(dealHistoryTablePanel);
IModel<List<? extends String>> dateChoices = new AbstractReadOnlyModel<List<? extends String>>() {
#Override
public List<String> getObject() {
List<String> list = new ArrayList<String>();
list.add("Last 3 months");
list.add("Last 6 months");
return list;
}
};
final DropDownChoice<String> datesDropDown = new DropDownChoice<String>(
"dates", new PropertyModel<String>(this, "selectedDate"),
dateChoices);
datesDropDown.add(new AjaxFormComponentUpdatingBehavior("onchange") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
//target.addComponent(dealHistoryTablePanel);
}
});
add(datesDropDown);
You're definitely on the right track. The basic thing that will make it happen is having the
target.addComponent(dealHistoryTablePanel);
exactly where you have it, in an AjaxFormComponentUpdatingBehavior.
You'll also likely want to change the model in your DealHistoryTablePanel, probably by replacing the list it contains with one gotten by a different call to your service. To say anything more explicit, I'd have to see the code for DealHistoryTablePanel.
An example showing the updating of one DropDownChoice after the selction of one is instructive, though of course the component it updates is different.
The functionality I am trying to use is:
- Create a ObjectDataSource for selection and updating controls on a web page (User Control).
- Use the DataObjectTypeName to have an object created that would send the data to an UpdateMethod.
- Before the values are populated in the DataObjectTypeName’s object, I would like to pre-populate the object so the unused items in the class are not defaulted to zeros and empty strings without me knowing whether the zero or default string was set by the user or by the application.
I cannot find a way to pre-populate the values (this was an issue back in 2006 with framework 2.0). One might ask “Why would anyone need to pre-populate the object?”. The simple answer is: I want to be able to randomly place controls on different User Controls and not have to be concerned with which UpdateMethod needs to handle which fields of an object.
For Example, let’s say I have a class (that reflects a SQL Table) that includes the fields: FirstName, LastName, Address, City, State, Zip. I may want to give the user the option to change the FirstName and LastName and not even see the Address, City, State, Zip (or vice-versa). I do not want to create two UpdateMethods where one handled FirstName and LastName and the other method handles the other fields. I am working with a Class of some 40+ columns from multiple tables and I may want some fields on one screen and not another and decide later to change those fields from one screen to another (which breaks my UpdateMethods without me knowing).
I hope I explained my issue well enough.
Thanks
This is hardly a solution to the problem, but it's my best stab at it.
I have a GridView with its DataSourceID set to an ObjectDataSource.
Whenever a row is updated, I want the property values in the object to be selectively updated - that is - only updated if they appear as columns in the GridView.
I've created the following extension:
public static class GridViewExtensions
{
public static void EnableLimitUpdateToGridViewColumns(this GridView gridView)
{
_gridView = gridView;
if (_gridView.DataSourceObject != null)
{
((ObjectDataSource)_gridView.DataSourceObject)
.Updating += new ObjectDataSourceMethodEventHandler(objectDataSource_Updating);
}
}
private static GridView _gridView;
private static void objectDataSource_Updating(object sender, ObjectDataSourceMethodEventArgs e)
{
var newObject = ((object)e.InputParameters[0]);
var oldObjects = ((ObjectDataSource)_gridView.DataSourceObject).Select().Cast<object>();
Type type = oldObjects.First().GetType();
object oldObject = null;
foreach (var obj in oldObjects)
{
if (type.GetProperty(_gridView.DataKeyNames.First()).GetValue(obj, null).ToString() ==
type.GetProperty(_gridView.DataKeyNames.First()).GetValue(newObject, null).ToString())
{
oldObject = obj;
break;
}
}
if (oldObject == null) return;
var dynamicColumns = _gridView.Columns.OfType<DynamicField>();
foreach (var property in type.GetProperties())
{
if (dynamicColumns.Where(c => c.DataField == property.Name).Count() == 0)
{
property.SetValue(newObject, property.GetValue(oldObject, null), null);
}
}
}
}
And in the Page_Init event of my page, I apply it to the GridView, like so:
protected void Page_Init()
{
GridView1.EnableLimitUpdateToGridViewColumns();
}
This is working well for me at the moment.
You could probably apply similar logic to other controls, e.g. ListView or DetailsView.
I'm currently scratching my head to think of a way this can be done in a rendering-agnostic manner - i.e. without having to know about the rendering control being used.
I hope this ends up as a normal feature of the GridView or ObjectDataSource control rather than having to hack it.