Custom selector

The fields SelectorField already allows us since several versions to open a view as a selector. This mode has been kept but selectors can override it.

  • Compatible mode : This mode has been retained and it allows you as before calling your view directly from a SelectorField. This mode should not have any impact on your previous developments. However, it does not take advantage of the new selector and it’s an outdated mode that allows migration cheaply.
  • Customizable mode : This mode use a screen of type “selector” that can integrate one or more views with new predictive capabilities, research, etc. This is the topic that we will discuss here.

Définition XML

This type of screen is defined in the “navigation” file like other Visiativ Process screens type . This type of screen doesn’t have Java provider, however, we will see that it’s the integrated views that carry their provider which may be enhanced by interfaces associated to the selector.

XML definition prototype:

<selector name="my" action="select">
  <links>
    <link name="LinkName1" label="Link label 1" default="true/false">
      <view name="ViewName" label="View Label"
            provider="com.vdoc.<...>.ProviderUsedByTheView"
            selectable="true/false"
            paginable="true/false"
            filterable="true/false"
            search="true/false"
            globalFilterColumns="columnName2;columnNameX;...;columnName2 ">

        <column name="columnName1" label="Column label 1"/>
        <column name="columnName1" label="Column label 2"/>
        ...
        <column name="columnNameX" label="Column label x"/>
      </view>
    </link>

    <link name="LinkName2">
      <view>
        ...
      </view>
    </link>
  </links>
</selector>

Tag options

Selector tag

Attribute Description
name The screen name. Ex: screen=NameOfScreenSelector (to put in the Development Attributes of a Customizable field).
action The method name. Ex: method=NameOfTheActionOrMethod (to put in the Development Attributes of a Customizable field).
predictive If set to true, it allows the auto-complete search directly in the field. No needs to open the search screen.
Note: the predictive research will use the link that matches the first of the following conditions:
  • The link with search=true else
  • The link with default=true else
  • The first link defined.
Attribute Description
name The link name.
label The label displayed to the left section of the screen. Default: LG_SELECTOR_DEFAULT_LINK_LABEL (Elements)
default (Default: false) When set to true, this link is displayed by default at the opening of the screen.
Note: a link have to be set to true and only one. The predictive search use the default link for its research.

view tag

Attribute Description
name The link name.
label The label displayed to the left section of the screen.
provider Provider class associated to the view.
globalFilterColumns Defines the columns that can be filtered (filter field in the top of the screen). Ex: globalFilterColumns=“columnName1, columnName3, …” Note: The label associated to the column will be displayed in the filter field.
filterable Specifies if filters should be positioned.
search (Default : false) If set to true, this link will be used for plain searches by the predictive search (in the Search tab). Specially useful with tree structured selectors which permits to search children.
Note: search begins when you type some text in the search field. Only one view can have this attribute set to true, and it can’t be set to true if the default link is also set to true.

Champ de filtrage des vues

Il est possible de définir sur chaque vue les colonnes sur lesquelles on peut filtrer (champ de filtrage présent dans le haut de l’écran).

Pour cela, on définit un attribut globalFilterColumns=“label;description”.

Définition de la vue utilisée pour les éléments sélectionnés (sélecteur multiple)

Il est possible de définir les colonnes qui seront utilisées pour présenter, dans le cas d’un sélecteur multiple, les éléments déjà sélectionnés.

<selector name="my" action="select">
   <links>
       <link name="defaultView" label="LG_DEFAULT_VIEW" default="true">
           <view name="allMyObjects" label="LG_ALL_MY_OBJECTS"
               provider="com.vdoc.selectors.ui.providers.MyObjectsSelectorProvider"
               selectable="true" paginable="true" filterable="true">
           </view>
       </link>
   </links>
   <currentSelection>
       <column name="label" label="LG_LABEL" />
       <column name="description" label="LG_DESCRIPTION" />
   </currentSelection>
</selector>

Définition de la vue utilisée pour les éléments récemment sélectionnés

Le système de sélecteurs mémorise les éléments récemment sélectionnés. Les colonnes utilisées pour présenter cette vue peuvent être définies.

<selector name="my" action="select">
   <links>
       <link name="defaultView" label="LG_DEFAULT_VIEW" default="true">
           <view name="allMyObjects" label="LG_ALL_MY_OBJECTS"
               provider="com.vdoc.selectors.ui.providers.MyObjectsSelectorProvider"
               selectable="true" paginable="true" filterable="true">
           </view>
       </link>
   </links>
   <lastSelection>
       <column name="label" label="LG_LABEL" />
       <column name="description" label="LG_DESCRIPTION" />
   </lastSelection>
</selector>

The view providers for selectors

The views of selectors will have to implement the ICollectionViewProvider interface (See this topic).

To summarize, the view provider will inherit of a BaseViewProvider and will implement a ICollectionViewProvider.

It will then be possible to enhance the view provider with additional interfaces (ISelectorViewProvider), giving us SDK inputs points that will be called.

ISelectorViewProvider interface:

public interface ISelectorViewProvider {
  /**
  * Méthode appelée à chaque ouverture du sélecteur avancé
  */
  public void onOpen();

  /**
  * Méthode appelée avant l'affichage de la vue dans l'explorer
  */
  public void onActivate();
}

The field selector : SelectorInputComponent

This selector is used to navigate to your screen selector via its name and method.

<field ctrl="com.axemble.vdp.ui.framework.widgets.components.sys.forms.SelectorInputComponent" screen="selectorName" method="selectorAction" />
Info

It is recommended that when you create a selector in this mode, to no longer use the SelectorField but to use the SelectorInputComponent.

Exemple

Voici le développement d’un sélecteur d’utilisateurs de l’annuaire. Ce sélecteur a comme fonctionnalités :

  • Une vue de recherche
  • Une vue par défaut correspondant à tous les utilisateurs (que l’utilisateur a le droit de visualiser)
  • Une vue des utilisateurs de mon organisation (ce link sera caché pour sysadmin)

ExampleSelector.xml :

<definition name="exampleDirectory">
    <screens>
        <selector name="exampleDirectory" action="select">
            <links>
                <link name="search" label="LG_SEARCH">
                    <view name="search" provider="com.vdoc.selectors.view.providers.AllUsersViewProvider"
                          paginable="true" filterable="true" search="true"
                          globalFilterColumns="email;firstName;lastName">
                        <column name="firstName" label="LG_FIRSTNAME"/>
                        <column name="lastName" label="LG_LASTNAME"/>
                        <column name="email" label="LG_EMAIL"/>
                        <column name="organization" label="LG_ORGANIZATION"/>
                    </view>
                </link>
                <link name="defaultView" label="LG_ALLUSERS_VIEW" default="true">
                    <view name="allUsers" label="LG_ALLUSERS_VIEW"
                          provider="com.vdoc.selectors.view.providers.AllUsersViewProvider" selectable="true"
                          paginable="true" filterable="true" globalFilterColumns="email;firstName;lastName">
                        <column name="firstName" label="LG_FIRSTNAME"/>
                        <column name="lastName" label="LG_LASTNAME"/>
                        <column name="email" label="LG_EMAIL"/>
                        <column name="organization" label="LG_ORGANIZATION"/>
                    </view>
                </link>
                <link name="organizationView" label="LG_ORGANIZATION_USER_VIEW">
                    <view name="organizationUsers" label="LG_ORGANIZATION_USER_VIEW"
                          provider="com.vdoc.selectors.view.providers.OrganizationUsersViewProvider" selectable="true"
                          paginable="true" filterable="true" globalFilterColumns="email;firstName;lastName">
                        <column name="firstName" label="LG_FIRSTNAME"/>
                        <column name="lastName" label="LG_LASTNAME"/>
                        <column name="email" label="LG_EMAIL"/>
                    </view>
                </link>
            </links>
        </selector>
    </screens>
</definition>

OrganizationUsersViewProvider.java :

package com.vdoc.selectors.view.providers;

import java.util.ArrayList;
import java.util.Collection;

import com.axemble.vdoc.sdk.Modules;
import com.axemble.vdoc.sdk.interfaces.IUser;
import com.axemble.vdoc.sdk.interfaces.runtime.INavigateContext;
import com.axemble.vdoc.sdk.modules.IDirectoryModule;
import com.axemble.vdoc.sdk.providers.BaseViewProvider;
import com.axemble.vdp.ui.core.providers.ICollectionViewProvider;
import com.axemble.vdp.ui.framework.composites.base.CtlAbstractView;
import com.axemble.vdp.ui.framework.composites.base.models.views.ViewModelItem;


public class OrganizationUsersViewProvider extends BaseViewProvider implements ICollectionViewProvider<IUser> {
    
    private static com.axemble.vdoc.sdk.utils.Logger LOG = com.axemble.vdoc.sdk.utils.Logger.getLogger(OrganizationUsersViewProvider.class);
    
    public OrganizationUsersViewProvider(INavigateContext context, CtlAbstractView view) {
        super(context, view);
    }

    /**
     * @see com.axemble.vdp.ui.core.providers.ICollectionViewProvider#getObjects()
     */
    @Override
    public Collection<IUser> getObjects() {
        Collection<IUser> users = new ArrayList<>();
        IDirectoryModule directoryModule = Modules.getDirectoryModule();
        try {
            if (!directoryModule.getLoggedOnUser().isSysadmin()) {
                users.addAll(directoryModule.getLoggedOnUser().getOrganization().getMembers());
            }
        } finally {
            Modules.releaseModule(directoryModule);
        }
        return users;
    }

    /**
     * @see com.axemble.vdp.ui.core.providers.ISelectableViewProvider#fetchLine(java.lang.Object)
     */
    @Override
    public ViewModelItem fetchLine(IUser user) {
        ViewModelItem viewModelItem = new ViewModelItem(user);
        viewModelItem.setValue("firstName", user.getFirstName());
        viewModelItem.setValue("lastName", user.getLastName());
        viewModelItem.setValue("email", user.getEmail());
        viewModelItem.setValue("organization", user.getOrganization().getLabel());
        return viewModelItem;
    }

}

AllUsersViewProvider.java :

package com.vdoc.selectors.view.providers;

import java.util.ArrayList;
import java.util.Collection;

import com.axemble.vdoc.sdk.Modules;
import com.axemble.vdoc.sdk.interfaces.IUser;
import com.axemble.vdoc.sdk.interfaces.runtime.INavigateContext;
import com.axemble.vdoc.sdk.modules.IDirectoryModule;
import com.axemble.vdoc.sdk.providers.BaseViewProvider;
import com.axemble.vdp.ui.core.providers.ICollectionViewProvider;
import com.axemble.vdp.ui.core.providers.ISelectorViewProvider;
import com.axemble.vdp.ui.framework.composites.base.CtlAbstractExplorer.CtlTabsGroup.CtlLauncher.CtlExplorerLink;
import com.axemble.vdp.ui.framework.composites.base.CtlAbstractView;
import com.axemble.vdp.ui.framework.composites.base.models.views.ViewModelItem;

public class AllUsersViewProvider extends BaseViewProvider implements ICollectionViewProvider<IUser>, ISelectorViewProvider {
    
    private static com.axemble.vdoc.sdk.utils.Logger LOG = com.axemble.vdoc.sdk.utils.Logger.getLogger(AllUsersViewProvider.class);
    
    public AllUsersViewProvider(INavigateContext context, CtlAbstractView view) {
        super(context, view);
    }

    /**
     * @see com.axemble.vdp.ui.core.providers.ICollectionViewProvider#getObjects()
     */
    @Override
    public Collection<IUser> getObjects() {
        Collection<IUser> users = new ArrayList<>();
        IDirectoryModule directoryModule = Modules.getDirectoryModule();
        try {
            users.addAll(directoryModule.getUsers(getLoggedOnContext()));
        } finally {
            Modules.releaseModule(directoryModule);
        }
        return users;
    }

    /**
     * @see com.axemble.vdp.ui.core.providers.ISelectableViewProvider#fetchLine(java.lang.Object)
     */
    @Override
    public ViewModelItem fetchLine(IUser user) {
        ViewModelItem viewModelItem = new ViewModelItem(user);
        viewModelItem.setValue("firstName", user.getFirstName());
        viewModelItem.setValue("lastName", user.getLastName());
        viewModelItem.setValue("email", user.getEmail());
        viewModelItem.setValue("organization", user.getOrganization().getLabel());
        return viewModelItem;
    }

    /**
     * @see com.axemble.vdp.ui.core.providers.ISelectorViewProvider#onActivate()
     */
    @Override
    public void onActivate() {

    }

    /**
     * @see com.axemble.vdp.ui.core.providers.ISelectorViewProvider#onOpen()
     */
    @Override
    public void onOpen() {
        // We hide dynamically the second link if sysadmin (no organization, so we don't need...)
        com.axemble.vdp.ui.framework.composites.base.CtlAbstractExplorer.CtlTabsGroup.CtlLauncher launcher = (com.axemble.vdp.ui.framework.composites.base.CtlAbstractExplorer.CtlTabsGroup.CtlLauncher) getParentSelector().getExplorer().getTabsGroup().getCurrentTab();
        ((CtlExplorerLink) launcher.getLinks().get(2)).setHidden(getWorkflowModule().getLoggedOnUser().isSysadmin());
    }
}

Source : https://wiki.myvdoc.net/xwiki/bin/view/Dev+Floor/HowToBuildNewVDoc14Selectors