[CUBA-component] declarative controllers


(Mario David) #1

Hi,

we’ve created an application component you might be interested in. It makes it possible do declarativly use generic features in your UI controllers and hold the logic for implementing those in a single place.

It is open source (Apache 2 license) and you can find it on github:

If you have any thoughts on that, like bugs, feature requests etc. we would love to hear from you.

I’ll just copy over the README.md so you get an impression on what it does:

CUBA Platform Component - Declarative Controllers

CUBA component that allows to write generic features for a Controller and use them in a declarative way.

Installation

Currently you have to download the app-component manually and import it into Studio. After opening it in studio, you have to execute “Run > Install app component”. After that you can go into your project and add the dependency to you project through “Project Properties > Edit > custom components (+) > cuba-component-runtime-diagnose”.

Note: This manual installation step will probably simplify with Version 6.6 of CUBA and studio.

Motivation & Example usage: Comments feature

So what does that mean? Here’s an example balvi/cuba-example-declarative-comments:

Let’s say you want to add a generic “comments-feature” on entities (in our case you can comment on “Customer” as well as “Product”) and you already got everything setup on the entity layer.

Now, as you want to present a default screen for comments on every browser that shows Customers or Products.
How do you not duplicate the UI logic, where you add a section on the side of the browser to show the comments on a selected item?

The default answer to this would be to create a Superclass called CommentsBrowser which extends AbstractLookup.
In this controller in the ready method you would probably hook into the ready callback and do your stuff.

Now you set CustomerBrowse extends CommentsBrowser and ProductBrowse extends CommentsBrowser and you are ready to go - no code shared, so what is the problem?

Inheritance works exactly once

The problem occurs when you want to implement another feature. Let’s imagine, we want to have another generic feature like that it is possible to
assign one or more Documents to entities. When we try to do it as above, we have the problem that ProductBrowse already extends CommentsBrowse.

How do we resolve this? Creating another subclass CommentsBrowserWithDocuments? Would work, but what if i only want the Documents feature?
This would require two classes: DocumentsBrowser for the case where we want only the documents feature and the CommentsBrowserWithDocuments class for
the case of both features.

Delegation to the rescue
As this does not scale at all (having five distinct features would end up in 2^5 = 32 classes), we need to do Delegation - and this is where this app-component comes into play.

Declarative definition of features through Annotations

This application component brings not only a way of dealing with the inheritance problem.
It also makes it possible to, instead of programmatically define that certain features should be used, activate these generic features
in a declarative way through the Usage of Annotations.

Here is how the ProductBrowse looks with the declarative definition of the Comments feature:

@Commentable(listComponent = "productsTable")
@HasDocuments(listComponent = "productsTable", createDocument = true)
public class ProductBrowse extends AnnotatableAbstractLookup {

}

The CustomerBrowse looks like this:

[url=]@Commentable(listComponent = “productsTable”)
public class CustomerBrowse extends AnnotatableAbstractLookup {

}[/url]

You just pick and choose your features and the implementation gets injected into your controllers. The only requirement is that your Controller
extends from AnnotatableAbstractLookup instead of AbstractLookup directly (or AnnotatableAbstractEditor for editor controllers). But this
is the only superclass you need to extend from. Not for every feature another superclass.

Defining Annotations

Generally, there is another example repository, which shows the usage of this application component: balvi/cuba-example-declarative-comments
with the exact example of the @Commentable.

To create custom feature like @Commentable you have to do the following:

  1. Create a Annotation in your web module like this:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Commentable {
    String listComponent() default "";
    String buttonId() default "commentBtn";
    String buttonsPanel() default "buttonsPanel";
}

When you want to parameterise your Annotation, you can define these settings as well as default values within the Annotation.

  1. Create a spring bean (in the web module) that implements either BrowseAnnotationExecutor or EditorAnnotationExecutor for Browse screens or Editors
    which contains the logic that sholuld get injected into the controllers:
@Component
class CommentableBrowseAnnotationExecutor implements BrowseAnnotationExecutor<Commentable> {


    @Override
    boolean supports(Annotation annotation) {
        annotation instanceof Commentable
    }
    
    @Override
    void init(Commentable annotation, Window.Lookup browse, Map<String, Object> params) {
        // do your logic to add a button to the browse screen...
    }


    @Override
    void ready(Commentable annotation, Window.Lookup browse, Map<String, Object> params) {

    }
}

The supports method declares for which Annotation this class is responsible.
Besides that it contains Hook methods for the corresponding Controllers (just like in the regular controllers).

For Browse screens these are:

  • init
  • ready

For editors the hook methods are:

  • init
  • postInit

Those hook methods have the same semantics as the standard CUBA controller hook methods. For more information
you can take a look at the corresponding CUBA docs for
AbstractLookup and AbstractEditor.

That’s it.

With this you have a single place where you can put your UI logic that is accessible for different screens.

You can easily take this and create CUBA studio templates that will add your Annotations to your screens,
so that you have a UI where you have a couple of checkboxes enable all of your generic features.


(Rares Codrin) #2

Normally, conform github page, it should be possible to import directly from maven repos (
de.balvi.cuba.declarativecontrollers:declarativecontrollers-core:0.6.0). Though, it does not work:



(Mario David) #3

hi,

the maven coordinates are:
de.balvi.cuba.declarativecontrollers:declarativecontrollers-global:0.6.0

can you retry? should just work…

BTW: since the app component is in the CUBA marketplace, you do not need to add the custom maven repo to your build.gradle file anymore. See: https://www.cuba-platform.com/marketplace/declarative-controllers

Bye
Mario