The selectManyListBox is an ordinary dropdown listbox with a list of selected values opposed to the single value for its SelectOneListbox sibbling. Unfortunately at the moment of implementing the ice:selectManyListbox was still buggy: the menurenderer.convertSelectValue didnot call the associated converter for multiple values. Apparently things have been ironed out in icefaces 1.6.1 Check the iceforum for a more detailed explanation.
<h2>Roles</h2>
<ice:selectManyListbox id="selectedRoles" value="#{subjectMgr.selectedRoles}" converter="#{subjectMgr.roleConverter}" size="5">
<f:selectItems value="#{subjectMgr.allAvailableRoles}"/>
</ice:selectManyListbox>
You need 2 lists for selectmany listbox: a list with all available roles, individually stored into a selectitem and (2) a list with the selected roles. A converter (I think there is a seam tag, to circumvent the converter but at the moment of experimenting that was still in beta) to convert the values inside the selectitem tag to some unique serializable key (string) for rendering in the select tag and vice versa (in my case ordinary findById methods).
A successful display of the selectmanylistbox is not that hard, the hardest part is storing the selected and deleting the unselected roles.
Check the code below for SubjectMgrBean.setSelectedRoles. The implementation I came up with was a maintenance of three lists: the new list (originating from the selectManyListBox component), the list with actors to remove (A map with a Role - Actor mapping). And the list with actors to be created. The rolesToAdd and the rolesToRemove lists are initialized by the current roles on the subject in focus. For the removal we take the presentRoles (roles to Remove) exclude all roles in the newly selected roles, and we're are on our own with the roles which are up for deletion. An iteration is needed because in jpa-ql it is not possible to do a cascaded delete on a parent entity.
A similar operation is used for the roles to add. All roles appearing in the presentRoles, are removed from the selected roles leaving a collection with roles to add. Every Role is attached to the subject in focus, by creating a new Actor and adding it the set of existing actors. A final merge on the entity manager is sufficient to store all roles (actors) at once.
As you have noticed the code for the selectmanylistbox may not be the world's cleanest code. But I'm convinced that a lot of plumbing is needed to get this to work. The amount of code for such a simple use case is also contra intuitive Also major drawback is the big bug in icefaces forcing you to either hack their code or using the reference-jsf's selectmanylistbox implemention. Particular caveat is the implementation of the equals method of the entity beans. However the most annoying feature is the gui element automatically disselect all choices in the box when the user hits a wrong element without the cmd key pressed (on iMac), leaving nothing but the cancel button to the user. If i would have realized this feature earlier I probably would have never taken the selectmanylistbox for this use case.
PS the last part can be found in September.
SubjectMgrBean.java
001 package nl.jeroen.testdb.persist;
002
003 import java.io.Serializable;
038 import org.jboss.seam.annotations.datamodel.DataModelSelection;
039 import ....;
041 import com.icesoft.util.DebugException;
042
043 @Scope(ScopeType.CONVERSATION)
044 @Stateful
045 @Name("subjectMgr")
046 public class SubjectMgrBean implements SubjectMgr, Serializable {
047 @Logger
048 Log logger;
049
050 public static final String EMPTY = "__EMPTY__";
051 private static final long serialVersionUID = 8523599219865145149L;
052 private Map<String, Role> roleValues;
053 private List<SelectItem> allAvailableRoles;
054 private List<Role> selectedRoles;
055
056 @In(required=true)
057 IApplicationSession sessionstore;
058
059 @PersistenceContext(type=PersistenceContextType.EXTENDED)
060 private EntityManager em;
061
062 @In(required=false)
063 @Out(required=false)
064 private Subject subject;
065
066 @DataModel(value="subjectList")
067 List<Subject> subjectList;
068 @DataModelSelection(value="subjectList")
069 @Out(required=false, value="focusSubject")
070 private Subject focusSubject;
071
072 public List<SelectItem> getAllAvailableRoles() {
073 final String methodName = "getAllAvailableRoles: ";
074 if (logger.isTraceEnabled()) {
075 logger.trace(LogUtil.getLogMessage(methodName, LogUtil.VERSION,
076 "starting '" + "'"));
077 }
078 boolean populateRoleValues = false;
079 // List<SelectItem> result = null;
080 if (allAvailableRoles == null || allAvailableRoles.size() == 0) {
081 List<Role> l = em.createQuery("from Role role").getResultList();
082 allAvailableRoles = new ArrayList<SelectItem>(l.size());
083 if (roleValues == null) {
084 populateRoleValues = true;
085 roleValues = new TreeMap<String, Role>();
086 }
087 for (Role r : l) {
088 SelectItem item = new SelectItem(r,r.getName());
089 allAvailableRoles.add(item);
090 if (populateRoleValues) {
091 roleValues.put(r.getName(),r);
092 }
093 }
094
095 }
096 if (logger.isDebugEnabled()) {
097 logger.debug(LogUtil.getLogMessage(methodName, LogUtil.VERSION,
098 "ending with '" + allAvailableRoles.size() + "' in available roles"));
099 }
100 return allAvailableRoles;
101 }
102 public void setAllAvailableRoles(List<SelectItem> allAvailableRoles) {
103 this.allAvailableRoles = allAvailableRoles;
104 }
105
106 public List<Role> getSelectedRoles() {
107 final String methodName = "getSelectedRoles: ";
108 if (logger.isTraceEnabled()) {
109 logger.trace(LogUtil.getLogMessage(methodName, LogUtil.VERSION,
110 "starting '" + focusSubject.getSubjectid() + "'"));
111 }
112 selectedRoles = em.createQuery("Select r from Role r, in (r.actors) a where a.subject = :subject")
113 .setParameter("subject", focusSubject).getResultList();
114 if (logger.isDebugEnabled()) {
115 logger.debug(LogUtil.getLogMessage(methodName, LogUtil.VERSION, "returning '" + selectedRoles.size() + "'"));
116 }
117 return selectedRoles;
118 }
119 public void setSelectedRoles(List<Role> sltdRoles) {
120 final String methodName = "setSelectedRoles: ";
121 if (logger.isDebugEnabled()) {
122 logger.debug(LogUtil.getLogMessage(methodName,
123 LogUtil.VERSION, "starting '" + sltdRoles.size() + "'"));
124 }
125 try {
126 if (logger.isDebugEnabled()) {
127 for (Role r : sltdRoles) {
128 logger.debug(LogUtil.getLogMessage(methodName,
129 LogUtil.VERSION, "selected roles: '" + r.getName() + "' (" + r + ")"));
130 }
131 }
132 Period prd = sessionstore.getFocusPeriod();
133 logger.info("focust period is #0 name is '#1'", prd.getPeriodid(), prd.getInfo());
134 Map<Role,Actor> presentRolesToRemove = new HashMap<Role, Actor>();
135 Set<Role> presentRolesToCreate = new HashSet<Role>();
136 for (Actor act :focusSubject.getActors()) {
137 presentRolesToRemove.put(act.getRole(), act);
138 presentRolesToCreate.add(act.getRole());
139 }
140 presentRolesToRemove.keySet().removeAll(sltdRoles);
141 for (Role r : presentRolesToRemove.keySet()) {
142 Actor act = presentRolesToRemove.get(r);
143 focusSubject.getActors().remove(act);
144 em.remove(act);
145 }
146 sltdRoles.removeAll(presentRolesToCreate);
147 for (Role r : sltdRoles) {
148 focusSubject.getActors().add(new Actor(-1,sessionstore.getFocusPeriod(), r, focusSubject));
149 }
150 em.merge(focusSubject);
151 } catch (Throwable e) {
152 logger.error("error ocurrued", e);
153 }
154 }
155
156 public Converter getRoleConverter() {
157 return new Converter (){
158
159 public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) throws ConverterException {
160 final String methodName = "getAsObject";
161 if (logger.isTraceEnabled()) {
162 logger.trace(LogUtil.getLogMessage(LogUtil.VERSION, methodName
163 , "start with argument '" + arg2 + "'"));
164 }
165 if (arg2 == null || arg2.equals("") || arg2.equals(EMPTY)) {
166 return null;
167 }
168 try {
169 int roleid = Integer.parseInt(arg2);
170 Iterator<SelectItem> iter = allAvailableRoles.iterator();
171 boolean found = false;
172 Role result = null;
173 while (!found && iter.hasNext()) {
174 result = (Role) iter.next().getValue();
175 if (result.getRoleid() == roleid)
176 found = true;
177 }
178 try {
179 EntityManager em1 = (EntityManager) Component.getInstance("entityManager", true);
180 em1.find(Role.class, roleid);
181 } catch (Exception e) {
182 logger.error(LogUtil.getLogMessage(methodName,
183 LogUtil.VERSION, "Error occured retrieving them from inner class."), e);
184 }
185 if (logger.isDebugEnabled()) {
186 logger.debug(LogUtil.getLogMessage(methodName,
187 LogUtil.VERSION, "'" + roleid + "'"));
188 }
189 return result;
190 } catch (NumberFormatException nfe) {
191 logger.error(LogUtil.getLogMessage(LogUtil.VERSION, methodName
192 , "passed argument '" + arg2 + "' cannot converted to int"), nfe);
193 }
194 return null;
195 }
196
197 public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) throws ConverterException {
198 final String methodName = "getAsString";
199 if (logger.isTraceEnabled()) {
200 logger.trace(LogUtil.getLogMessage(LogUtil.VERSION, methodName
201 , "start '" + arg2 + "' of type '" + (arg2 != null ? arg2.getClass() : "") + "'"));
202 }
203 if (arg2 == null) return EMPTY;
204 if ("-1".equals(arg2)) return EMPTY;
205 if (arg2 instanceof Role) {
206 return "" + ((Role)arg2).getRoleid();
207 } else {
208 logger.error(LogUtil.getLogMessage(LogUtil.VERSION, methodName
209 , "argument is not expected type Role returning _EMPTY"));
210 }
211 return EMPTY;
212 }
213 };
214
215 }
216
217
218 public Role findRoleById(Integer id) {
219 return (Role) em.createQuery("r from Role r where r.roleid = :id").setParameter("id", id).getSingleResult();
220 }
221
Geen opmerkingen:
Een reactie posten