We have a Set<String> that we are printing to the page in Thymeleaf:
<h5>Unique Students names</h5>
<ol>
<div th:each="uniqueName: ${course.uniqueStudentsNames}">
<li th:text="${uniqueName}"></li>
</div>
</ol>
Since it is a Set<String> there is no order of the items in the set.
We like to convert the non-ordered Set<String> to an ordred List<String> so we will be able to print it by ABC. Since it is a UI issue, we don't like the backend to change its data structure.
An option we thought about is to create such a list in the Model:
model.addAttribute("course", course);
model.addAttribute("orderedList", /* convert Set to ordred List */);
But this seems very strange that the Model needs to handle such a UI issue.
Is there a way to do it in Thymeleaf?
Instantiate it as a TreeSet (you are probably using hashset?) and you have a set with the ordering behaviour you want. Set<String> (Set<anything> really) guarantees there won't be repeated items, but it does not imply anything about the order/unorder.
On the other side, thymeleaf, as a template engine, won't do such things as adding ordering behaviour to a collection that does not implement it. The most it will do is allow you to access methods of the collection; in the case of set: set utility methods.
If you no matter what want to use a non ordered set, you'd have to use something like javascript to manipulate the items on the client to order them.
Use the Thymeleaf utility method to convert the object to a List:
${#lists.toList(object)}.
Then perform a utility operation to sort as needed from there, optionally using your own Comparator.
The current Thymeleaf source code shows that the toList() method will check if the target object is an instance of Iterable. In your case, Set implements the interface.
To reference the external link:
if (target instanceof Iterable<?>) {
final List<Object> elements = new ArrayList<Object>(10);
for (final Object element : (Iterable<?>)target) {
elements.add(element);
}
return elements;
}
Related
I have some ExtJs component.
I set itemId for it, but id is autogenerated.
Now Ext.getCmp('autogenerated-id') returns my component.
But Ext.ComponentQuery.query('#autogenerated-id') returns an empty array.
But:
Ext.ComponentQuery.query('[id=assets-information-form-1918]') again returns my component. :)
I use ExtJs 6.5.3 classic.
It seems like itemId config property hides autogenerated id from Ext.ComponentQuery, so they become mutually exclusive.
I don't need other means for search or advice to set id for the component, to write letter to Sencha support or to write post on their forum.
I need:
Means to force my Ext.ComponentQuery.query('#autogenerated-id') to
find the Component for which getId() returns 'autogenerated-id'.
If it is not possible by design, I need a link to some documentation
describing this behavior, a link to some bug report, or a filename and line number in ExtJs sources + a little snippet copy/paste from there.
From the documentation
Summary Provides searching of Components within Ext.ComponentManager
(globally) or a specific Ext.container.Container on the document with
a similar syntax to a CSS selector. Returns Array of matching
Components, or empty Array.
Ext.ComonpentQuery.query('#itemId') returns and array. Your cold above is using the auto-generated id of the component. The # indicates to query based on the component itemId and not the component id.
Try
Ext.ComponentQuery.query('assets-information-form-1918');
which will return an array, as noted in the documentation.
Ext.getCmp()
This is shorthand reference to Ext.ComponentManager#get. Looks up an
existing Ext.Component by id
Therefore it returns the component object.
Ext.ComponentQuery.query('#itemId')[0] would return the first object in the array.
Ext.ComponentQuery is the Sencha preferred method because it is more powerful when used as it does return an array so you an also query items by xtype and other attributes.
I just wasted half a day trying to figure this out, reading about some workarounds, and thinking "it can't be that bad - there must be a straightforward to do edit a collection in Grails, whethere using scaffolded views or my own."
Let's say I have this domain object:
class TreeGroup {
String name
List<Tree> trees
static hasMany = ['trees': MyTree]
}
Just to explain the choice of data structure - I need my records to be unique, but in the order I set. That's why I chose List, AFAIK one cannot rely on order in a Set. So there are 2 pieces to this question - 1) how to remove from any Collection, for example a Set, 2) is List the best replacement for Set in this context (preserving order).
I want to be able to create a group record with no trees in it and make 4 updates:
edit/save
edit the group record to reference 2 trees A and B
add another tree C
remove A
remove B and C
And obviously, I want the desired state after every step. Currently though, I can only add records, and if I even edit/save to list, the list elements are added to it again.
I am using the multiple select tag for this. It looks like this:
<g:select name="trees" from="${allTrees}" optionKey="id"
multiple="true" class="many-to-many"
value="${trees ? trees*.id : treeGroupInstance?.trees*.id}" />
and that's fine, in the sense that it generates an HTTP header with these variables on update:
_method:PUT
version:19
name:d5
trees:1
_action_update:Update
But the data binder only adds new elements, it never lets you edit a list.
What is the cleanest way to do it ? Is it me, not reading something obvious, or is this a design flaw of grails data binding (and of so, when/how will it be fixed) ?
Is there a way perhaps via a hidden HTTP parameter to clear the list before (re)adding elements ?
Thanks
I ended up doing this:
private repopulate(def domainObject, String propertyName, Class domainKlaz) {
if (params[propertyName] != null) {
domainObject[propertyName].clear()
domainObject[propertyName].addAll(
params[propertyName].collect { domainKlaz.get(it) }
)
}
}
and I am calling it in update controller method before save(), for every collection. OMG how ugly.
I have this code in my controller:
def cols = grailsApplication.getDomainClass('com.archie.Build').persistentProperties.collect {it.name}
The code above will allow me to list all the property names I have in Build class. Now, I would like to include also the properties data type, ie. boolean, String etc...
Somewhat like the output is:
[floorType:String, floorWidth:Float, ......]
Maybe not exactly like that, or maybe similar, but as long as I can return their data type. Can someone help? Thank you.
Each entry in persistentProperties is a GrailsDomainClassProperty, and this provides access to the type of the property as a Class object:
def props = [:]
grailsApplication.getDomainClass('com.archie.Build'
).persistentProperties.each {
props[it.name] = it.type.name
}
Or just pass the persistentProperties array itself through to the GSP, then extract .name and .type there.
You may also wish to consider using constrainedProperties instead of/in addition to the persistentProperties. The constrainedProperties map lists only those properties that are mentioned in the domain class constraints block, but the iterator over this map is guaranteed to return the properties in the order they are listed in the constraints. This is how the default scaffolding operates, as I'm not aware of any way to control the order of the persistentProperties array.
I'm trying to create a dynamic survey application in Prime Faces. I have a list of Question Objects that each contains a list of AnswerChoices. These are given to the f:selectItems value attribute. This is fine. The question object also contains a List of selectedValues which is given to the relevant selectOne/many component.
Because I'm looking to be generic, there will be questions that have multiple selected values and also some that have only one selected value. I wanted to be able to point the selectOne and selectMany components to the List of strings within the relevant Question object that represents the selectedValues.
This works ok for the selectMany component, but not for the selectOne component which needs to be pointed at a singular object rather than a list. Is there an easy way around this that I'm missing - as I'd like to only have one object representing the selectedValues if possible
You can use brace notation to bind the value to a list/array item at a specific index. The below example binds the value to the 1st item of the list/array.
<h:selectOneMenu value="#{bean.selectedAnswers[0]}" />
There's however a caveat: you need to prepare the list/array with the single item yourself during bean's (post)construction. JSF/EL won't do that in case of a <h:selectOneMenu>.
E.g.
#PostConstruct
public void init() {
selectedAnswers = new ArrayList<Answer>();
selectedAnswers.add(null);
}
It doesn't harm to reuse this preinitializated property for UISelectMany components by the way.
I develop web part with custom editor part and faced with this question.
Is it possible in web part set Personalizable attribute to generic List?
For example I want something like this:
[WebBrowsable(false)]
[Personalizable(PersonalizationScope.Shared)]
public List<AnnouncementItem> Announcements
{
get { return _announcements; }
set { _announcements = value; }
}
Is it possible, and what kind of types at all can be used as "Personalizable"?
Thanks.
Solution:
I use a custom EditorPart to select multiple lists using AssetUrlSelector, but I need a way to personalize this collection for end user.List<of custom objects> doesn't work, but I found that List<string> (and only string) work perfectly. So, I get required lists in EditorPart and pass their to the web part using List<string>.
Try using a custom EditorPart to add/remove items from the collection. I've never built a web part that personalized a collection so I don't know if it works but I'd definitely try the collection with an EditorPart. If it doesn't work, serialize XML into a string property.
Your question does not seem to match your code. Your code shows a collection of custom objects. I doubt an end user will be able to set such a property. To have a property that points to a generic list, you would probably be better off defining the property as a string that contains the URL to a list.