The environment is JSF 2, GlassFish, PrimeFaces.
I am working on an application for editing the data in a very complex model.
All classes in the model have relations to other classes, which in turn have relations to other classes, and so on. In many cases, for a given class, the "cluster" of relations (the whole expanded tree for the class) contains the same class at many places in the tree.
For example :
Class A has relations to class B, C and D.
Class B has no relations.
Class C has relations to class B.
Class D has relations to class E.
Class E has relations to class B.
Now that I have summarized the context, here's what troubles me.
A design decision has been made to put all the data of a given instance of a class in the same page. The current architecture for doing this is the following for a given "root" class :
The "root" class is displayed in a PrimeFaces TabView. The tabs are :
"Main" : contains the simple attributes (String, Integer, ...) of the class.
There is one tab for each relation.
In each tab, there is an include pointing to the appropriate XHTML file.
If the relation points to a single class, the XHTML file pointed to is similar to the "root" form described here. The adequate object is passed as a parameter to the XHTML file in the include directive. There are at most 3 nested TabViews.
If the relation points to a list, the XHTML file pointed to displays a list, with "add", "edit" and "delete" buttons. The adequate list is passed as a parameter to the XHTML file in the include directive.
There are additional complexities concerning lists, but I won't go there for the moment. Just with the single elements, we can see that there is a problem.
At first, I used the same parameter name for all XHTML files to be included. This does not work, because the parameter is "global" to the view. It seems to work for the initial display, but as soon as there is an update in one tab, I get an javax.el.PropertyNotFoundException because the form points to another object.
To mitigate the problem, I changed the parameter name to be unique for each class. Before, all parameters were called "element". After the modification, the parameter for class A is called "aElement", for class B it's "bElement", etc.
This works if all classes in a cluster are different. If a collision occurs, we are back to the same problem as before : in two different inclusions, we use the same parameter names, with different values.
I have thought about a very convoluted way to do it, but it uses Javascript :
I pass an additional parameter to the XHTML forms, which is a unique identifier for the passed object.
I get the identifier in the XHTML form, and put it in a hidden field, which is separated from the rest of the form.
I make sure that the hidden field is never updated, read its value when the form gets focus, and retrieve the right object by an AJAX request using the value.
So my question is : is there a "pure" Java/JSF way to handle this ?
TL;DR : Thanks for reading !
For very complex natures CDI has a lot more to offer then plain JSF2. You could use producers and during runtime you can evaluate and modify objects freely.
You can keep track on what's already been produced and adapt the naming accordingly. For example save every occupied name in a map. Good luck.
Related
Working on a project that will display hierarchies of "tasks". I'm running into a problem where it will not allow for multiple entries of the same object. From what I can tell, the "duplicate" item is under a different parent.
The domain data allows for this - a given task may appear in lots of places.
It would seem that this is intentional (maybe), but is there a way around this?
It's intentional to a degree; each Grid and TreeGrid data item is expected to be unique. You could work around this by creating your own implementation of the hierarchical DataProvider class (for example extend AbstractHierarchicalDataProvider) which overrides the getId method along with the other required methods. The return value of this method needs to be unique per item, as it's used as a hash key.
Well, this is probably not the best solution, but it works.
I added a field to the abstract super class that is initialized with the current time (long ms). When I am adding items to the tree grid, I check to see if the tree contains the item and if so, I randomize the field and then add it. The new field is marked #Transient so it's not persisted.
I'm developing own custom field type in JIRA.
My class is very simple, it extends GenericTextCFType.
My goal is to store some identifier (ID) of field value in database but to show human-readable caption of the field value on Issue form.
I searched methods of GenericTextCFType class and found method getSingularObjectFromString, and I don't understand, what it does.
JIRA javadoc says: "Returns a Singular Object, given the string value as passed by the presentation tier"
But what is the Singular Object and what is it needed for?
Yes, it's not a great name. I wrote about it in detail in "Practical JIRA Plugins"
(O'Reilly). Here's an extract from there describing many of the methods in detail (sorry about the formatting). The book also has worked examples available at https://bitbucket.org/mdoar/practical-jira-plugins
CustomFieldType Methods
The example’s custom field type class will implement the CustomFieldType interface as usual, but instead will extend a class higher up in the inheritance hierarchy than NumberCFType. The class we will extend is AbstractCustomFieldType and it’s at the root of most classes that implement CustomFieldType.
The methods in the CustomFieldType interface with “SingularObject” in their name refer to the singular object, in this example a Carrier object. All other methods in JIRA 4 custom fields that refer to an Object are referring the transport object, e.g., a Collection of Carrier objects. JIRA 5 removed the use of Object in most custom field methods.
For more information about what changed in JIRA 5.0 with custom fields see https://developer.atlassian.com/display/JIRADEV/Java+API+Changes+in+JIRA+5.0#JavaAPIChangesinJIRA5.0-CustomFieldTypes. There were some major changes in the class hierarchy, and most classes now have a Java generic as a parameter instead of just using an Object as before.
There are two objects that are typically injected into the constructor of a custom field type’s class. The first is a CustomFieldValuePersister persister object, which is what will actually interact with the database. The second is a GenericConfigManager object that is used for storing and retrieving default values for the custom field. Other objects are injected into the constructor as needed—for example, the DoubleConverter in Example 2-2.
The first set of methods to consider are the ones that the custom field type uses to interact with the database in some way.
getSingularObjectFromString()
This method converts a string taken from the database such as “42.0###The answer” into a Carrier object. A null value means that there is no such object defined.
Fields with Multiple Values
Collection<Carrier> getValueFromIssue(CustomField field, Issue issue)
This is the main method for extracting what a field contains for a given issue. It uses the persister to retrieve the values from the database for the issue, converts each value into a Carrier object and then puts all the Carrier objects into a trans- port object Collection. A null value means that this field has no value stored for the given issue. This is one of the methods that used to return an Object before JIRA 5.0
createValue(CustomField field, Issue issue, Collection<Carrier> value)
updateValue(CustomField field, Issue issue, Collection<Carrier> value)
These methods create a new value or update an existing value for the field in the given issue. The persister that does this expects a Collection of Strings to store, so both of these methods call the method getDbValueFromCollection to help with that.
getDbValueFromCollection()
A private convenience method found in many custom field type classes, sometimes with a different name. It is used to convert a transport object (e.g., a Collection of Carrier objects) to a Collection of Strings for storing in the database.
setDefaultValue(FieldConfig fieldConfig, Collection<Carrier> value)
Convert a transport object (a Collection of Carrier objects) to its database repre- sentation and store it in the database in the genericconfiguration table.
Collection<Carrier> getDefaultValue(FieldConfig fieldConfig)
Retrieve a default value, if any, from the database and convert it to a transport object (a Collection of Carrier objects). The FieldConfig object is what represents the context of each default value in a custom field.
The next set of methods to consider are the ones that interact with a web page in some way. All values from web pages arrive at a custom field type object as part of a Custom FieldParams object, which is a holder for a Map of the values of the HTML input elements.
validateFromParams(CustomFieldParams params, ErrorCollection errors, FieldConfig config)
This is the first method that is called after a user has edited a custom field’s value. Any errors recorded here will be nicely displayed next to the field in the edit page.
getValueFromCustomFieldParams(CustomFieldParams customFieldParams)
This method is where a new value for a field that has been accepted by validate FromParams is cleaned and converted into a transport object. The custom FieldParams object will only contain strings for the HTML elements with a name attribute that is the custom field ID—e.g., customfield_10010. A null value means that there is no value for this field.
getStringValueFromCustomFieldParams(CustomFieldParams parameters)
This method returns an object that may be a String, a Collection of Strings or even a CustomFieldParams object. It’s used to populate the value variable used in Chapter 3: Advanced Custom Field Types Velocity templates. It’s also used in the Provider classes that are used by custom field searchers.
String getStringFromSingularObject(Carrier singularObject)
This method is not the direct opposite of getSingularObjectFromString as you might expect. Instead, it is used to convert a singular object (Carrier) to the string that is used in the web page, not to the database value. The returned String is also sometimes what is stored in the Lucene indexes for searching (“More Complex Searchers” on page 57). The singular object was passed into this method as an Object before JIRA 5.0.
The final set of CustomFieldType methods to consider are:
Set<Long> remove(CustomField field)
This method is called when a custom field is entirely removed from a JIRA instance, and returns the issue ids that were affected by the removal. The correct method to use for deleting a value from a field is updateValue.
String getChangelogValue(CustomField field, Object value)
String getChangelogString(CustomField field, Object value)
These methods are how the text that is seen in the History tab of an issue is gen- erated. When a custom field of this type changes, these methods are called with the before and after values of the field. The difference between the two methods is that the if the value later becomes invalid, the string will be shown instead (https://developer.atlassian.com/display/JIRADEV/Database+Schema#DatabaseSchema-ChangeHistory).
extractTransferObjectFromString()
extractStringFromTransferObject()
These methods are not from the CustomFieldType interface but they exist in the standard Multi fields for use during project imports.
Other Interfaces
There are a few other interfaces that are commonly implemented by custom field types.
ProjectImportableCustomField
The getProjectImporter method from this interface is used to implement how the custom field is populated during importing a project from an XML backup. If you don’t implement this interface then project imports will not import values for your custom field.
MultipleCustomFieldType
MultipleSettableCustomFieldType
These two interfaces are used by custom fields with options and that furthermore can have more than one option. For these classes, the values can be accessed using the Options class, which is a simple subclass of a Java List. These interfaces are not really intended for use by general-purpose multiple value custom field types.
Fields with Multiple Values | 41
SortableCustomField
This interface contains a compare method for comparing two singular objects. This is used by the Issue Navigator when you click on a column’s heading to sort a page of issues. This is actually a slower fallback for custom fields that don’t have a searcher associated with them (see Chapter 4).
RestAwareCustomFieldType
RestCustomFieldTypeOperations
These two interfaces are how the JIRA REST API knows which fields can be retrieved or updated. New in JIRA 5.0.
I'm working a feature in the application where model will be dynamic in the sense that any settings data could be displayed and the view will get the model based on what tab they clicked on. I use Hidden field to store what the settings name was because they are same as model name. for ex., if tab1-> Settings1 then Model is Settings1[already exists in the Model].So I used # model dynamic in View and used #Html.EditotForModel() to draw the required UI based off the model. My problem is when I do HttpPost on Edit currently I'm using FormCollection to read the data on that page when I declare the model name in the param it will get it for me but I don't know which model is coming back other than by the Hidden variable and I need it because the Model validation is broken because of this issue. Any help or feedback is appreciated? I can give more details if required? Has anybody crossed this issue before??
Dynamics can be a good thing and a bad thing. Using them on models that have a common interface in a controlled manor is best.
There are different options that you can look at:
1)
Have you tried making the action method accept a dynamic type? That might be the easiest way.
You might have to set up a casting helper to cast the object to the correct type based on the hidden field.
2)
I have a similar idea in some code, but I created a viewmetamodel class that contained all my types as nullable properties. My action method accepts this viewmetamodel type and validates the properties that are not null.
In line with this, if your data is not too large, then you could load all the settings tabs and use Jquery apply the tab with on click.
3)
You could also create #sections or use EditorFor(c=>c.settings) for each tab. That way each tab will load a type safe object. You would need to create controllers for each.
I would say pick the easiest method for you. I hope that this at least gives you some ideas.
As an exercise in learning .NET, I'm moving some simple forms over into MVC, and have run into an issue. The form in question is a multi-part form that has option sections. For example, Section 0 is static and contains information like username, real name, email address. After that is a radio button with several options. If you click the first radio, it displays Section 1. If you choose the second, it displays Section 2, and so on.
In WebForms this was no biggy, as I just validated on postback and said if Radio1.Selected validate this, if Radio2.Selected validate that, etc. So now I've got a strongly-typed view with [Required] members, which obviously isn't going to work - I can't require members that aren't always going to be required.
With that said, is this the correct approach to the problem:
Create the members that belong in Section 0 in my strongly-typed view model class.
Create references to each partial's strongly-typed class in my view model class.
Create the partial views and then render them in the main view.
Depending on which radio button is selected, render the appropriate partial view.
Validate the model like usual...which hopefully will cascade to the partial models.
Does this make sense, or is the approach wrong?
That's a typical scenario where you need conditional validation, i.e. if some value is set then validate that some other value is required. Achieving this with static data annotations which are simple attributes baked at compile time can quickly turn into a nightmare due to their declarative nature. Well, you could always roll your own custom validation attributes but the problem with attributes is that you will have to specify property names as strings as they need to be known at compile time.
That's one of the reasons why I use FluentValidation.NET. Not only that validation rules are separate from the view models and that it integrates really nicely with ASP.NET MVC but handling scenarios like this would be very easy. You could have a sub-view model containing all the properties of the sub-section and then conditionally include it based on the value of a given property on the main view model inside its validator.
I have a Person model which has many Animal models as pets. Dog is an Animal with a "favorite bone" field, and Cat is an Animal with a "likes catnip?" field and a "favorite fish" field.
#models
class Person(db.model):
pass
class Animal(db.model):
models.ForeignKey(Person) #owner
name = CharField()
class Dog(Animal):
favorite_bone = CharField()
class Cat(Animal):
favorite_fish = CharField()
likes_catnip = BooleanField()
I would like to inline edit all of a Persons pets, in the Person admin form however, I've read that Django inline admin forms don't support polymorphic inline forms[1], in that, you will only get the parent class fields (e.g. not the favorite_bone or favorite_fish and likes_catnip fields.
Where does this problem come from?
What changes could be made to the framework to accommodate this?
If these changes should not be made, why not?
[1] http://www.mail-archive.com/django-users#googlegroups.com/msg66410.html
(This is an old question, but I thought I'd add an answer in case it is still useful. I've been working on a similar question recently.)
I believe it would be challenging to change Django form-generation to do what you want. The reason is that the inline formset uses a single class/form for all rows of the inline -- there are no configuration options that are evaluated per-row of the inline form. I have convinced myself of this by reading the code itself --- look for "inline" and "formset" in django.contrib.admin.options.py, especially lines 1039-1047 (version 1.5.1). This is also the reason why you can't have some fields read-only in existing items and changeable in new items (see this SO question, for example).
The workarounds found for the readonly case have involved a custom widget that produces the desired behavior such as this one. That still won't directly support polymorphism, however. I think you would need to end up mapping your divergent types back to a common ancestor (e.g. have all pet classes able to return a dict of their unique attributes and values), and then create a single custom widget that renders out the polymorphic part for you. You'd then have to map the values back on save.
This might be more challenging than it is worth, and may lead back to the suggestion in the other answer to not use admin for this :-)
may have a look here.
but i think the modeladmin is currently not able todo such things.
you are able to create a custom edit view for your model...
there is almost everything possible.
It may be possible to do this with Generic Relations.