How to order collection columns in Apache isis? - isis

I am using apache ISIS for almost a year and it is a great framework. I am trying to improve the usability of the generated user interface and I am looking for a way to order the columns of a collection as displayed on the screen.
I could not find a way to order the columns using #Collection or #CollectionLayout and could not find an example if it is possible in the layout.xml file.
It would really improve the usability of my user interface if the most relevant columns of a collection would be displayed first - on the left side of the table - in the table view.
(My domain entities showed in a collection property often have a super class defining common fields. The display of attributes of a single entity is perfect using layout.xml and the bootstrap layout, but I still looking for a similar construct for collections displayed as tables in the user interface. I could not find any hints in the documentation or the advanced guide).
Thanks for any hints how I could control the order of columns in a table displaying a collection of domain entities and improve the user interface usability.

nice to know you're enjoying using the framework.
The way to do this with annotations is using #MemberOrder, not #CollectionLayout.
However, if you have a layout.xml file then this (should) override the #MemberOrder annotation. In which case, the order of the columns is the same as the order of the properties in the layout.xml.
You can also use the <collection id="xxx" hidden="ALL_TABLES"> (or alternatively #CollectionLayout(hidden=Where.ALL_TABLES)) to hide properties that you don't want to appear as columns.
All that said, I did recently discover what I think is a bug (on 1.13.0): that the layout.xml seemed to be being ignored for the collection order, even though used for the object form. This isn't consistent and I haven't got to the bottom of it. My workaround was to just add in the #MemberOrder annotations back in; this did the trick.
One other caveat: if you have a complex mix of tabs and regular fieldsets, then you'll find that those properties in tabs are considered to be first, even if there's a property in a fieldset that comes before it. This is arguably a bug, but it's a symptom of the implementation: the code does a deep traversal of tabgroups/tabs before the fieldsets.
Finally, here's a trick. What you could do is to define a bunch of derived properties on the entity for which you want to show in columns, and configure so that only these are shown in tables, and are hidden in object form, eg:
public class Customer {
#Getter #Setter private String name;
public String getNameAsColumn() { return getName(); }
}
then in the layout.xml:
<property id="name" hidden="ALL_TABLES"/>
<property id="nameAsColumn hidden="OBJECT_FORMS">
<named>Name</named>
</property>
HTH
Dan

Related

Apache Isis: How should I mark a table so that all entries are not openable

I have a complex entity which is displayed in tables in different places. In one place I want to show a subset of properties only. For this projection I made a special in-memory object, which wraps the complex entity and has getters for the subset of properties only. How can I mark this inmemory object to be not openable?
as per the Apache Isis mailing list, can use the #cssClass() UI hint coupled with some custom CSS and Javascript.

MVC : How to use database to drive validation attributes, or ALTERNATIVES?

In summary, I'm trying to create instance-specific data-annotation attributes at runtime, based on database fields. What I have now works fine for creating the initial model, but falls over when the model is posted-back and the server-validation happens.
(I have the same input model being used in a collection within a viewmodel, but different validation must be applied to each instance in the collection....for example the first occurrence of the input may be restricted to a range of 1-100 but the next occurrence of the same model, prompted for on the same input page, would be a range of 1000-2000. Another may be a date, or a string that has to be 6 characters long.......)
I'll explain what I've done and where my issues are:
I've inherited DataAnnotationsModelMetadataProvider and provided my own implementation of GetMetadataForProperty (This doesn't have any bearing on the validation problem....yet)
I've inherited DataAnnotationsModelValidatorProvider and provided a facade implementation of GetValidators. What I want to do here is create new attributes based on my database-records and then pass those attributes through to the base implementation so the Validators are created accordingly.
However...... GetValidators is called at a PROPERTY level....When it is called with a propertyname that I want to apply validators to, I need to find the applicable DB record for this propertyname so I can find out what attributes I need to create....BUT...I can't get the DB record's key from just a propertyname of the value field.....In fact, the DB key is in the parent model.....So how do I get hold of it?!
I've tried using a static variable (YUK) and storing the key during a call for one property, and retrieving it during another call for my value field property....But because the model is serialised one-way and deserialised the opposite way I end up with my key being out-of-sync with my required attributes.
To add a slight complication I'm also using a custom model binder. I've overridden CreateModel as advised elsewhere on here, but I can't find a way of attaching metadata or additionalvalues to a PROPERTY of my output model....Only to the model itself....but how do I get at MODEL metadata/additionalvalues inside the GetValidators call for a PROPERTY ?
So....My question is twofold.....
1) Can anyone help me get my database-key from my custom-Model-binder to my GetValidators method on my ValidationProvider? Or maybe using my custom Metadata provider?
2) Is there a different, simpler, way of creating validators at runtime based on database records?
I think you are making this far more complicated than it needs to be. You just need to make whatever your validation criteria selectors are part of your view model. They don't necessarily have to be displayed (they can be stored in hiddens if they need to be kept for postback purposes).
Then you can use something like FluentValidation to create rules that say
RuleFor(model => model.myprop)
.When(model => model.criteria == whatever)
.GreaterThan(100)
.LessThan(1000);
Where criteria is whatever value you use to select when your property has to be in a certain range.
So that would mean you build your view model to include the criteria that is used for validation rule selection.
I'd asked this on the FluentValidation forums also and the lack of answers here as well as the advice against using Fluent from there led me to find my own solution (I understand this almost certainly means I'm doing something really bad / unusual / unnecessary!)
What I've ended up doing is assigning my controller static variable in my Custom Model Binder's CreateModel method, where I have access to the entire client model, rather than trying to do it through a custom MetaDataProvider. This seems to work just fine and gets me towards v1 of my app.
I'm not really happy with this solution though so will look to refactor this whole area in the coming months so would still appreciate any other comments / ideas people have about how to implement dynamic validation in a generic way.
I know this is an old question, but I am answering this so that many others can be benefited from this.
Please see the below article where they are loading the attributes from an xml
Loading C# MVC .NET Data Annotation Attributes From XML, Form Validation
I think you can follow the same approach and instead of reading from xml you can read from database and add these rules dynamically based on the model data type
You can refer the below approach also
DataAnnotations dynamically attaching attributes

A few general questions about Service Reference and MVVM pattern

There is a web service.
It provides types Zoo and Animal.
Zoo has a dictionary of animal ids and names.
Animal has properties: Id, Name and (additional stuff).
It has a method GetZoo that returns a zoo object.
It has a method GetAnimalStuffById that returns an Animal object with Id, Name and the (additional stuff).
So the idea is - GetZoo allows me to get a list of animal ids + names, and then GetAnimalStuffById fetches full animal info.
I add a "service reference" to that service in VS and want to write a MVVM app. Some things I don't fully understand and need to be brainwashed about.
Is it OK for autogenerated classes to be my models?
Not related to the example, but anyway: what "collection type" should I specify when adding service reference? Is ObservableCollection an overkill and a bad practice for models?
Say, user goes to an application page showing full animal info. Obviously, initially I have an AnimalViewModel with only Id and Name values (taken from GetZoo). As the page is navigated to, I call GetAnimalStuffById and get an Animal object with all the data. What should I do next? Replace the DataContext of my view with a new AnimalViewModel created from new Animal object (A), or just replace the values in it (B)?
If the answer is (A), how do I replace the DataContext in all the views?
If the answer is (B), what should cause that update? Should the VMs subscribe to some fancy manager's event about getting an Animal update? Or is there some other approach?
What is the purpose of INotifyPropertyChanged in the autogenerated classes? They are always returned fresh from the webservice in my case. Does Microsoft suggest to use them also as ViewModels in some scenarios?
Thanks.
Here are a few answers based on my own experience with MVVM (which may or may not be "best practice"..)
Absolutely! No need to do everything twice - see #5 and #6 (although there are people who disagree here).
Yes, unless you actually need the functionality of an ObservableCollection server-side, I would say it's overkill, and possibly confusing to others. Techincally, there's no overhead to the messages being sent across the wire, but I would go with something simpler, like an array.
Go with option B.
-
For example, you could have a single property in your AnimalViewModel to hold all the additional stuff: public Animal AdditionalData { .... Now, whoever calls GetAnimalStuffById can just update the current ViewModel's AdditionalData with that Animal object.
I assume you already know that INotifyPropertyChanged is there to let the View know that some data has changed somewhere (if not, googling "inotifypropertychanged mvvm" should get you started). Now, connecting the dots from #1 and #5, your View can now bind to the animal's additional data by going through the AdditionalData property without having to recreate everything in the ViewModel: <TextBox Text="{Binding Path=AdditionalData.HeightOrWhatever}" />.
Note: If your View isn't WPF or Silverlight, that last point won't make much sense..
And here's answers based on my experience (mainly to provide another point of view)
It's fine to autogenerate Models from an endpoint. But I would recommend POCO Models without any INPC cruft. Two reasons, a) it makes the Models simpler and easier to maintain and b) You won't be tempted to expose your Models directly to the View, or if you do they won't work properly.
Continuing on from #1, I would not use ObservableCollection in Models. Again to keep things simple and to avoid presenting Models directly to the View.
Option (B)
-
All the properties in the ViewModel should implement INPC. Then when you change them the binding will automatically update. You can either have all the AdditionalData values as properties of your AnimalViewModel which is flattening the data, or you can have an AdditionalDataViewModel object to hold the extra data. To map data from an AdditionalData object to AdditionalDataViewModel consider using a mapping tool like AutoMapper or ValueInjecter.
I don't know why the autogenerator added INPC stuff into your models. What tool are you using? In any case as I've said I do not recommend having INPC in Models, or exposing Models to the View. Instead you should be mapping from Models to ViewModels and only exposing ViewModels to the View.

Naming Conventions Regarding View Models to Avoid Long Names

I am creating view models for each screen in my ASP.NET MVC application. I put all of the logic for creating a view model in a builder class. Usually, there is special logic for converting the data objects into view models, including aggregating, filtering, and sorting. Each builder is passed a dependency set, which is an object containing properties for each dependency (repositories, other builders, etc.).
The problem is that my names are getting really long. A dependency set will usually have a name composed this way:
view-model-name+Builder+DependencySet
View models usually have names composed of where you are currently and the children. For instance, my system has categorized provider definitions. So, in order to show the provider definitions under a category, I have a view model called:
CategoryProviderDefinitionListViewModel
It will look something like this:
public sealed class CategoryProviderDefinitionListViewModel
{
public long CategoryId { get; set; }
public string CategoryName { get; set; }
public ProviderDefinitionViewModel[] ProviderDefinitions { get; set; }
}
So, my builder is called
CategoryProviderDefinitionListViewModelBuilder
So, my dependency set is called
CategoryProviderDefinitionListViewModelBuilderDependencySet
That barely fits across the screen. My poor fingers are tired. Furthermore, some screens almost show the same data, so their view model names are almost the same. When I am looking through my folder, it becomes really hard to find the specific view model classes I am looking for.
Ideally, I could group my view model classes together, associating them with the view(s) where they are used. It would be nice to avoid collisions and to make names as short as possible, while keeping them meaningful. Has anyone found a naming convention/folder organization that works well in this scenario?
I've been using the "ViewModel" suffix consistently for quite a while and to be honest, sometimes I find it redundant. I think just grouping all these classes in a different namespace should be sufficient.
My understanding is that this convention has been adopted to avoid collision between domain model and view model classes (eg Product vs ProductViewModel). However, since your view models are named after the screens, it is very unlikely that you would have a class with the same name in your domain model. In fact, it should be really questionable why you have such a class in your domain model! :)
So, if you name your view model something like ViewProduct (to allow the user to view/edit a product), you don't need to call it ViewProductViewModel. See where I'm going?
Consequently, your Builder class could simply be called ViewProductBuilder instead of ViewProductViewModelBuilder.
Regarding your dependency set, I'm not sure what is your rationale behind this. But to me it looks unnecessary. If your builder has dependencies to other objects, you'll need to inject dependencies in the constructor of builder, instead of encapsulating them into another class (DependencySet) and then passing them around.
If you find your builder dependent on too may things and this is what you are trying to hide behind DependencySet, then it could be the indication of a design smell somewhere else. If classes and their dependencies are designed in a proper object-oriented fashion, behavior should be distributed very nicely between various classes and no class should have dependency on too many other things. So, hiding those N dependencies under 1 class (DependencySet) is merely treating the symptoms not the problem itself.
Hope this help :)
I prefer post-fixing my ViewModel names with "DTO". (Where DTO stands for Data Transfer Object, ie. an object that does nothing but contain information)
This is not only to avoid long names. But it also makes me able to use the same names like User, but it will be called UserDTO, indicating to me that I am working with an object that is part of my ViewModel, and thus avoid naming collisions.
I tend to agree with Mosh.
The ViewModel suffix becomes redundant the majority of the time and while sometimes you might have matching class names, it is quite easy to manage as they are confined to your ViewModel namespace. I also find that using the odd namespace alias hurts my brain less than suffixing my class names across the board.
Of course in your presenter/controller you may have naming collisions, but that could be a sign that you need to name your Views more appropriately, e.g. not User but ViewUser/EditUser.
For search results and lists I find it is best to break out something such as IEnumerable instead of IEnumerable. The latter often means you end up with a User view model class that becomes a dumping ground for all User properties that may or may not be needed across the project. This is one of the big things to watch out for and if you find yourself with a class like this, you've gone of the track somewhere. Keep your views and view models descriptive and specific. If you have many similar view models the issue is probably a bigger design problem; some designers have a tendency to reinvent rather than reuse existing graphical structures.

How to prevent multiple classes for the same business object?

A lot of the time I will have a Business object that has a property for a user index or a set of indexes for some data. When I display this object in a form or some other view I need the users full name or some of the other properties of the data. Usually I create another class myObjectView or something similar. What is the best way to handle this case?
To further clarify:
If I had a class an issue tracker and my class for an issue has IxCreatedByUser as a property and a collection of IxAttachment values (indexes for attachment records). When I display this on a web page I want to show John Doe instead of the IxCreatedByUser and I want to show a link to the Attachment and the file name on the page. So usually I create a new class with a Collection of Attachment objects and a CreatedByUserFullName property or something of that nature. It just feels wrong creating this second class to display data on a page. Perhaps I am wrong?
The façade pattern.
I think your approach, creating a façade pattern to abstract the complexities with multiple datasources is often appropriate, and will make your code easy to understand.
Care should be taken to create too many layers of abstractions, because the level of indirection will ruin the initial attempt at making the code easier to read. Especially, if you feel you just write classes to match what you've done in other places. For intance if you have a myLoanView, doesn't necessarily you need to create a myView for every single dialogue in the system. Take 10-steps back from the code, and maybe make a façade which is a reusable and intuitive abstraction, you can use in several places.
Feel free to elaborate on the exact nature of your challenge.
One key principle is that each of your classes should have a defined purpose. If the purpose of your "Business object" class is to expose relevant data related to the business object, it may be entirely reasonable to create a property on the class that delegates the request for the lookup description to the related class that is responsible for that information. Any formatting that is specific to your class would be done in the property.
Here's some guidelines to help you with deciding how to handle this (pretty common, IMO) pattern:
If you all you need is a quickie link to a lookup table that does not change often (e.g. a table of addresses that links to a table of states and/or countries), you can keep a lazy-loaded, static copy of the lookup table.
If you have a really big class that would take a lot of joins or subqueries to load just for display purposes, you probably want to make a "view" or "info" class for display purposes like you've described above. Just make sure the XInfo class (for displaying) loads significantly faster than the X class (for editing). This is a situation where using a view on the database side may be a very good idea.