Un bouton dans l’entête d’une colonne de grille Ext JS

Dans cet article, nous allons créer un bouton dans une entête de colonne d’action de grille Ext JS.

Le constat

Dans une logique d’affichage et de manipulation d’ensembles de données, un exemple d’interface utilisateur revient souvent: une grille d’affichage des données couplée à un formulaire d’ajout ou d’édition d’enregistrement.
Plusieurs possibilités s’offrent alors en terme d’expérience utilisateur. Par exemple, une barre d’outils (toolbar) au-dessus ou en-dessous de la grille:
Grille avec une barre d’outils contenant des boutons d’action
Grille avec une barre d'outils contenant des boutons d'action
Ce à quoi nous allons nous intéresser ici est une solution d’interface entièrement “intégrée” à la grille elle-même. Elle comprendra un bouton d’ajout dans l’entête d’une colonne d’action. Les cellules de cette colonne elles, contiendront les boutons d’édition et de suppression d’enregistrement.
Grille avec une colonne d'action comportant un bouton d'ajout dans son entête
Ceci permet d’obtenir un design plus épuré et actuel que la précédente interface.

Le problème

L’API Ext JS pour les colonnes des grilles permet d’ajouter facilement des composants à leur entête via la config items:

{ 
  xtype: 'grid', 
  columns: [{ 
    items: [{ 
      xtype: 'button', 
      iconCls: 'x-fa fa-plus', 
      ui: 'default-toolbar' 
    }] 
  }, ...] 
}

Le problème est que cette méthode ne fonctionnera pas pour les colonnes d’action xtype: 'actioncolumn'

En effet, la propriété items de ce type de colonne sert à passer les configurations des icônes affichées dans les cellules de ces colonnes.

{
xtype: 'grid',
  columns: [{
    xtype: 'actioncolumn',
      items: [{
        iconCls: 'x-fa fa-pencil' // icone dans les cellules de la colonne
      }, {
       iconCls: 'x-fa fa-trash'
    }]
  }, ...]
}

Les solutions

  • Utiliser une colonne de template (templatecolumn)

La première solution serait d’utiliser une templatecolumn. C’est un type de colonne permettant facilement d’afficher un template Ext JS au sein de ses cellules. Dans ce cas, il serait simple de créer les balises affichant les icônes d’actions via un template HTML. Le problème de la propriété itemsde l’actioncolumn ne se poserait alors plus :

{
  xtype: 'grid',
  columns: [{
    xtype: 'templatecolumn',
    items: [{
      xtype: 'button',
      iconCls: 'x-fa fa-plus',
      ui: 'default-toolbar'
    }],
    tpl: ''
  }]
}

Cependant, peuvent arriver des complications quand il s’agira de gérer séparément le clic sur chaque icône, leur activation/désactivation ou encore un tooltip qui leur est propre.
La colonne de type actioncolumn, via les fonctions isActionDisabled(), handler(), ou encore geTtip(), permet de faciliter ces interactions de manière dynamique. Pour arriver à cela, il faudrait recoder manuellement ces différents comportements pour une colonne de type templatecolumn

  • Créer un plugin pour la colonne d’action

La seconde solution, plus propre et modulaire, serait de créer un plugin à appliquer à n’importe quelle actioncolumn de notre application. Celui-ci permettrait d’ajouter une configuration de bouton à son entête.
On garderait alors toutes les fonctionnalités d’une actioncolumn standard en ayant le résultat final recherché.

  • Nous créons notre plugin

Une classe Ext JS qui hérite de Ext.AbstractPlugin:

Ext.define('MyApp.plugin.ActionColumnHeaderButtonPlugin', {
  extend: 'Ext.AbstractPlugin',
  alias: 'plugin.actioncolumnheaderbuttonplugin',
  actionColumnHeaderId: null,
  config: {
    buttonConfig: null
  }

L’alias nous permettra de facilement identifier le plugin que l’on souhaitera rajouter plus tard à notre colonne.
Nous lui passons une propriété permettant d’identifier le conteneur de notre bouton dans l’entête de la colonne cible.
Enfin, nous déclarons une configuration de bouton, qui sera passée lors de la déclaration du plugin sur notre colonne.

  • Nous l’initialisons

Comme pour tout plugin, en surchargeant la méthode abstraite init()

 

, on est en capacité de récupérer le composant sur lequel le plugin est posé et d’intéragir avec lui.
Dans cette fonction, nous allons manuellement affecter le composant sur lequel nous posons le plugin, au plugin lui-même.

this.setCmp(cmp);

Ensuite, nous allons écouter l’événement viewready de la grille contenant la colonne (la logique exécutée lors de cet événement sera présentée plus loin dans l’article).

var ownerGrid = cmp.getView().ownerGrid;
ownerGrid.on('viewready', this.onViewReady, this);

Nous créons un identifiant unique basé sur l’identifiant de la colonne elle-même.

this.actionColumnHeaderId = cmp.getId() + '-actioncolumnheaderCt';

Enfin, nous injectons dans le corps de l’entête de colonne, un bloc HTML ayant pourid l’identifiant créé plus haut, afin de facilement y avoir accès au sein des fonctions exposées par notre plugin.

cmp.setText('<div id="' + this.actionColumnHeaderId + '"></div>');

Le code complet de la méthode init() de notre plugin:

init: function (cmp) {
  this.setCmp(cmp);
  var ownerGrid = cmp.getView().ownerGrid;
  ownerGrid.on('viewready', this.onViewReady, this);
  this.actionColumnHeaderId = cmp.getId() + '-actioncolumnheaderCt';
  cmp.setText(' div id="' + this.actionColumnHeaderId + '"></div>'); }
  • Nous ajoutons notre bouton lorsque la grille est créée

Une fois que la grille est rendue, elle lance un événement viewready que nous écoutons dans notre plugin.
Au déclenchement de cet événement, nous allons créer notre bouton à partir de la configuration passée dans buttonConfig:

var button = Ext.create('Ext.button.Button', this.getButtonConfig());

Et nous injectons ce composant dans le conteneur inséré à l’initialisation du plugin:

button.render(view.el.down('#' + this.actionColumnHeaderId));

Le code complet de notre méthode onViewReady():

onViewReady: function (view) {
  var button = Ext.create('Ext.button.Button', this.getButtonConfig());
  button.render(view.el.down('#' + this.actionColumnHeaderId));
}
  • Affectons le plugin à notre colonne d’action
columns: [{
  xtype: 'actioncolumn',
  plugins: [{
    ptype: 'actioncolumnheaderbuttonplugin',
    buttonConfig: {
      iconCls: 'x-fa fa-plus',
      handler: 'onAddButtonClick'
    }
  }],
  items: [{
    iconCls: 'x-fa fa-pencil'
  }, {
    iconCls: 'x-fa fa-trash'
  }]
}]

De ce fait, nous pourrons avoir accès à l’ensemble des propriétés et fonctions disponibles pour les items de notre actioncolumn, tout en paramétrant un bouton dans l’entête de notre colonne.
Vous pouvez voir le code complet avec quelques ajustements, et son résultat sur en accédant à ce fiddle.

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *