Crear un renderer de columna y una columna calculada

Introducción

En este tutorial se mostrará como crear un renderer para una columna y una columna calculada para las tablas. Usaremos el formulario de las cuentas

Crear un renderer de una columna

Un renderer de una columna nos permite modificar la visualización de los datos de esa columna. Hasta ahora, se han visto 2 tipos de renderers, el de las imágenes y el de las fechas, que cambian su visualización. En esta parte del tutorial, crearemos nuestro propio renderer para una columna. Para crear un renderer, crearemos un componente nuevo, en ese caso, le llamaremos account-render. Para crear este nuevo componente, podemos situarnos dentro de la carpeta de account-home, y ejecutar el comando:

npx ng g component --skip-tests account-number-render

Se habrá creado una carpeta con el componente que usaremos para usar el renderer. Dado que este no es un renderer predefinido, y lo estamos creando desde cero, es necesario seguir algunos pasos, para obtener algo similar al siguiente mockup.

tutorial_o_web_24.png

Lo primero que debemos hacer es importar el nuevo componente y añadirlo al array de declaraciones.

accounts.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { OntimizeWebModule } from 'ontimize-web-ngx';
import { AccountsRoutingModule } from './accounts-routing.module';
import { AccountsHomeComponent } from './accounts-home/accounts-home.component';
import { AccountNumberRenderComponent } from './accounts-home/account-number-render/account-number-render.component';


@NgModule({
  declarations: [
    AccountsHomeComponent,
    AccountNumberRenderComponent
  ],
  imports: [
    CommonModule,
    OntimizeWebModule,
    AccountsRoutingModule
  ]
})
export class AccountsModule { }

Nuestro componente debe extender de la clase OBaseTableCellRenderer e insertamos la siguiente línea @ViewChild('templateref', { read: TemplateRef, static: false }) public templateref: TemplateRef<any>; para adquirir el contenido según una plantilla y acceder a la vista. En el constructor tenemos que añadir el siguiente código.

constructor(protected injector: Injector) {
  super(injector);
}

y sobrecargar el método getCellData(cellvalue: any, rowvalue?: any): string para que podamos modificar el valor que muestra la columna de la tabla.

La idea de este renderer es visualizar una única columna que contenga los valores de la entidad, la oficina, el dígito de control y el identificador de la cuenta para obtener el número de cuenta total, separado por espacios. Como el método recibe por parámetros el valor de la celda y los valores de toda la fila, podemos concatenar todos los datos que queremos y devolverlo todo junto.

account-number-render.component.ts

import { ViewChild, TemplateRef, Injector, Component, OnInit } from '@angular/core';
import { OBaseTableCellRenderer } from 'ontimize-web-ngx';

@Component({
  selector: 'app-account-number-render',
  templateUrl: './account-number-render.component.html',
  styleUrls: ['./account-number-render.component.css']
})
export class AccountNumberRenderComponent extends OBaseTableCellRenderer {

  @ViewChild('templateref', { read: TemplateRef, static: false }) public templateref: TemplateRef<any>;

  constructor(protected injector: Injector) {
    super(injector);
  }

  getCellData(cellvalue: any, rowvalue?: any): string {
    return rowvalue["ENTITYID"] + " " + rowvalue["OFFICEID"] + " " + rowvalue["CDID"] + " " + rowvalue["ANID"];
  }
}

El componente del render, en su fichero account-number-render.component.html tiene que comenzar por <ng-template #templateref let-cellvalue="cellvalue" let-rowvalue="rowvalue"> y acabar por </ng-template>. La palabra let declara la variable de la plantilla que será referenciada desde esta. Las variables de entrada en este caso son cellvalue y rowvalue. El parseador traduce las variables y se las pasa al método getCellValue().

account-number-render.component.html

<ng-template #templateref let-cellvalue="cellvalue" let-rowvalue="rowvalue">
    {{getCellData(cellvalue, rowvalue)}}
</ng-template>

Ahora, en el formulario, definimos una nueva columna en las visible-columns, por ejemplo, ACCOUNTNUMBER. A continuación, definimos una nueva columna llamada de la misma manera y eleminamos las o-table-column que ya no son necesarias.

accounts-home.component.html

<o-form-layout-manager title="{{'ACCOUNTS' | oTranslate }}" separator=" " mode="dialog" label-columns="ANID">
    <o-table attr="accountsTable" service="branches" entity="account" keys="ACCOUNTID"
        columns="ACCOUNTID;ENTITYID;OFFICEID;CDID;ANID;STARTDATE;ENDDATE;INTERESRATE;ACCOUNTTYP"
        visible-columns="ACCOUNTNUMBER;STARTDATE;ENDDATE;INTERESRATE;ACCOUNTTYP" query-rows="20">
        <o-table-column attr="STARTDATE" title="STARTDATE" type="date" format="LL"></o-table-column>
        <o-table-column attr="ENDDATE" title="ENDDATE" type="date" format="LL"></o-table-column>
        <o-table-column attr="INTERESRATE" title="INTERESRATE" type="percentage" width="150px" decimal-separator=","
            content-align="center"></o-table-column>
        <o-table-column attr="ACCOUNTNUMBER" title="ACCOUNTNUMBER" content-align="center">
            <app-account-number-render></app-account-number-render>
        </o-table-column>
    </o-table>
</o-form-layout-manager>
o-table-column (atributos de o-table-column)
Atributo Valor Significado
content-align center Centra el contenido de la columna

en.json

{
  ...
  "ACCOUNTNUMBER": "Account number"
}

es.json

{
  ...
  "ACCOUNTNUMBER": "Nº cuenta"
}

Si hemos abierto antes este formulario (en el tutorial anterior, por ejemplo), es posible que no nos aparezca el render que acabamos de crear, ya que la tabla guarda las preferencias de las columnas mostradas al usuario actual. Podemos reestablecer las preferencias por defecto haciendo clic en el menú more_vert de la tabla y haciedo clic en Configuración > Aplicar configuración > Configuración por defecto

También podemos eliminar todas las preferencias que ha guardado la web abriendo las Herramientas de desarrollo y eliminar los datos almacenados de la página desde la pestaña de Aplicación

Permitir el uso del render en otros módulos

Aunque lo ideal sería que hubiésemos definido el render anterior dentro del módulo shared, realizamos este paso para demostrar que se puede usar desde múltiples sitio, sin importar la ubicación del componente. Importamos el componente AccountNumberRenderComponent desde el módulo shared y lo añadimos al array de declaraciones y de exportación

shared.module.ts

import { NgModule } from '@angular/core';
import { OntimizeWebModule } from 'ontimize-web-ngx';
import { CommonModule } from '@angular/common';
import { AccountNumberRenderComponent } from '../main/accounts/accounts-home/account-number-render/account-number-render.component';

@NgModule({
  imports: [
    OntimizeWebModule
  ],
  declarations: [
    AccountNumberRenderComponent
  ],
  exports: [
    CommonModule,
    AccountNumberRenderComponent
  ]
})
export class SharedModule { }

Ahora, cambiaremos la importación del componente en el módulo de cuentas, sustituyéndolo por la importación del módulo shared, pero retirándolo del array de declaraciones y añadiéndolo al array de importaciones. Haremos lo mismo para cada módulo en el que queramos usar el render

Crear una nueva columna calculada

Las columnas calculadas son columnas cuyo valor se calculan en base a operaciones sobre otras columnas. En este ejemplo, crearemos una nueva columna calculada, que mostrará el porcentaje de interés mensual, esto es, dividiendo entre 12 el valor del porcentaje de interés. Esta función para obtener la columna del interés mensual la usaremos en múltiples sitios, por lo que la definiremos en el módulo de shared. Obtendremos un resultado similar al siguiente mockup.

tutorial_o_web_27.png

Debemos declarar una nueva columna en las columnas visibles, por lo que la denominaremos INTERESRATE_MONTHLY. Ahora, definimos una columna calculada <o-table-column-calculated>. Este tag tiene un atributo [operation-function] al que añadimos un nombre, que será el nombre del método que invocaremos. Luego se establece el centrado de los elementos en el espacio de las columnas y un renderer por defecto

accounts-home.component.html

<o-form-layout-manager title="{{'ACCOUNTS' | oTranslate }}" separator=" " mode="dialog" label-columns="ANID">
    <o-table attr="accountsTable" service="branches" entity="account" keys="ACCOUNTID"
        columns="ACCOUNTID;ENTITYID;OFFICEID;CDID;ANID;STARTDATE;ENDDATE;INTERESRATE;ACCOUNTTYP"
        visible-columns="ACCOUNTNUMBER;STARTDATE;ENDDATE;INTERESRATE;INTERESRATE_MONTHLY;ACCOUNTTYP"
        query-rows="20">
        <o-table-column attr="STARTDATE" title="STARTDATE" type="date" format="LL"></o-table-column>
        <o-table-column attr="ENDDATE" title="ENDDATE" type="date" format="LL"></o-table-column>
        <o-table-column attr="INTERESRATE" title="INTERESRATE" type="percentage" width="150px" decimal-separator=","
            content-align="center"></o-table-column>
        <o-table-column attr="ACCOUNTNUMBER" title="ACCOUNTNUMBER" content-align="center">
            <app-account-number-render></app-account-number-render>
        </o-table-column>
        <o-table-column-calculated attr="INTERESRATE_MONTHLY" title="INTERESRATE_MONTHLY"
            [operation-function]="intRateMonthly" type="percentage" decimal-separator="," content-align="center">
        </o-table-column-calculated>
    </o-table>
</o-form-layout-manager>

Creamos una función en el fichero shared.module.ts que llamaremos igual que el nombre de la función que hemos estabablecido en el *.html, acabado en Function (por comodidad). Exportamos esta función, que DEBE devolver un número y y ser pública y tener como parámetros de entrada un array con los datos de la fila (rowData: Array<any>)

shared.module.ts

import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { OntimizeWebModule } from 'ontimize-web-ngx';
import { AccountNumberRenderComponent } from '../main/accounts/accounts-home/account-number-render/account-number-render.component';

export function intRateMonthlyFunction(rowData: Array<any>): number {
  return rowData["INTERESRATE"] / 12;
}

@NgModule({
  imports: [
    OntimizeWebModule
  ],
  declarations: [
    AccountNumberRenderComponent
  ],
  exports: [
    CommonModule,
    AccountNumberRenderComponent
  ]
})
export class SharedModule { }

Ahora, desde el fichero typescript del componente donde queramos utilizarla, la importaremos (ya que la hemos marcado con el modificador export) y se establecerá como el valor de una variable con el mismo nombre que la función que hemos definido en el fichero *.html

Esto es, si la definimos en el fichero accounts-home.component.html, la importaremos en el fichero accounts-home.component.ts

accounts-home.component.ts

import { Component } from '@angular/core';
import { intRateMonthlyFunction } from 'src/app/shared/shared.module';

@Component({
  selector: 'app-accounts-home',
  templateUrl: './accounts-home.component.html',
  styleUrls: ['./accounts-home.component.css']
})
export class AccountsHomeComponent {

  public intRateMonthly = intRateMonthlyFunction

}

Y por último, añadimos nuevas traducciones a los bundle de traducción

en.json

{
  ...
  "INTERESRATE_MONTHLY": "Monthly interest rate"
}

es.json

{
  ...
  "INTERESRATE_MONTHLY": "% interés mensual"
}

arrow_back Tutorial anterior Próximo tutorial arrow_forward