Añadir un render personalizado a una columna

Última modificación por Pablo Martínez Kirsten el 2022/03/04 09:54

Introducción

En este ejemplo añadiremos un render con una imagen a una columna y otro render con un símbolo y color de fuente diferentes según el valor que tenga la celda de cada una de las tablas.

Render con una imagen personalizada

Para este ejemplo, mostraremos imágenes diferentes en el listado de clientes según su tipo (Normal, VIP u Otro). Modificaremos el listado que ya tenemos y añadiremos los nuevos elementos y clases necesarias


render_mockup.png


El primer paso es ubicarnos dentro de la ruta src/app/main/customers/customers-home y ejecutar el siguiente comando

npx ng g component --skip-import --skipTests customertype-column-renderer

Esto creará el nuevo componente que usaremos para realizar el nuevo render. Como nuestra idea es poder usar este render en múltiples lugares de la aplicación, aunque su ubicación física esté en esta ruta, el módulo que tendrá la declaración y exportación del componente es el módulo shared

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';
import { CustomertypeColumnRendererComponent } from '../main/customers/customers-home/customertype-column-renderer/customertype-column-renderer.component';

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

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

Ahora, el propio modulo de customers debe importar el módulo de shared (que ya lo importa de ejercicios anteriores)

customers.module.ts
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { OntimizeWebModule } from "ontimize-web-ngx";
import { CustomersRoutingModule } from "./customers-routing.module";
import { CustomersHomeComponent } from "./customers-home/customers-home.component";
import { CustomersDetailComponent } from "./customers-detail/customers-detail.component"
import { CustomersNewComponent } from "./customers-new/customers-new.component";
import { SharedModule } from '../../shared/shared.module';

@NgModule({
  imports: [
    CommonModule,
    OntimizeWebModule,
    CustomersRoutingModule,
    SharedModule
  ],
  declarations: [
    CustomersHomeComponent,
    CustomersDetailComponent,
    CustomersNewComponent
  ]
})
export class CustomersModule {}

En nuestro fichero customertype-column-renderer.html, crearemos la platilla que se mostrará. En dicha plantilla, utilizaremos las directrices *ngIfg de Angular para seleccionar que imagen que queremos mostrar a través de la variable cellvalue

customertype-column-renderer.html
<ng-template #templateref let-cellvalue="cellvalue" let-rowvalue="rowvalue">
   <img *ngIf="cellvalue == 1" src="assets/images/normal_24.png" width="24" height="24">
   <img *ngIf="cellvalue == 2" src="assets/images/vip_24.png" width="24" height="24">
   <img *ngIf="cellvalue == 3" src="assets/images/other_24.png" width="24" height="24">
</ng-template>

En el fichero customertype-column-renderer.ts, extenderemos la clase OBaseTableCellRenderer, incluyendo la anotacion @ViewChild y el injector en el constructor

customertype-column-renderer.ts
import { Component, Injector, TemplateRef, ViewChild } from '@angular/core';
import { OBaseTableCellRenderer } from 'ontimize-web-ngx';

@Component({
  selector: 'app-customertype-column-renderer',
  templateUrl: './customertype-column-renderer.component.html',
  styleUrls: ['./customertype-column-renderer.component.css']
})
export class CustomertypeColumnRendererComponent extends OBaseTableCellRenderer {

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

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

  ngOnInit() {
  }
}

Para utilizar este render, lo declaramos dentro de la columna de CUSTOMERTYPEID

customers-home.component.html
<o-form-layout-manager title="{{'CUSTOMERS' | oTranslate }}" separator=" " mode="tab" label-columns="NAME;SURNAME">
 <div fxFlex>
   <o-table fxFlex attr="customersTable" service="customers" entity="customer" keys="CUSTOMERID"
     columns="CUSTOMERID;ID;PHOTO;NAME;SURNAME;STARTDATE;EMAIL;CUSTOMERTYPEID"
     visible-columns="ID;PHOTO;NAME;SURNAME;STARTDATE;EMAIL;CUSTOMERTYPEID" query-rows="20">
     <o-table-column attr="CUSTOMERTYPEID" title="CUSTOMERTYPEID">
       <app-customertype-column-renderer></app-customertype-column-renderer>
     </o-table-column>
     <o-table-column attr="PHOTO" title="PHOTO" orderable="no" searchable="no" type="image" avatar="no"
       empty-image="assets/images/no-image.png" image-type="base64"></o-table-column>
     <o-table-column attr="STARTDATE" title="STARTDATE" type="date" format="LL"></o-table-column>
     <o-table-column attr="ID" title="ID" width="100px"></o-table-column>
   </o-table>
 </div>
</o-form-layout-manager>

Las imágenes tienen que estar dentro de la carpeta src/assets/images

normal_24.pngnormal_24.png
vip_24.pngvip_24.png
other_24.pngother_24.png

Render con símbolos y colores personalizados

En este ejemplo añadiremos un render a la columna de movimientos dentro del detalle de las cuentas, que según su valor se modifique el color y se añadan símbolos y además añadiremos el render que hemos creado para el tipo de cliente.


render_mov_mockup.png


Nos ubicamos dentro de la carpeta que contiene el detalle de las cuentas. accounts-detail

npx ng g component --skip-import --skipTests movement-column-renderer

Al ejecutar el comando, crearemos el componente que usaremos como render. De igual forma que el render anterior, este componente estará declarado y exportado en el módulo shared

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';
import { CustomertypeColumnRendererComponent } from '../main/customers/customers-home/customertype-column-renderer/customertype-column-renderer.component';
import { MovementColumnRendererComponent } from '../main/accounts/accounts-detail/movement-column-renderer/movement-column-renderer.component';

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

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

Es necesario que el módulo accounts.module.ts importe el módulo shared (importado en ejercicios anteriores)

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 { SharedModule } from 'src/app/shared/shared.module';
import { AccountsDetailComponent } from './accounts-detail/accounts-detail.component';
import { AccountsNewComponent } from './accounts-new/accounts-new.component';
import { AddCustomerComponent } from './add-customer/add-customer.component';
import { AddMovementComponent } from './add-movement/add-movement.component';


@NgModule({
  declarations: [AccountsHomeComponent, AccountsDetailComponent, AccountsNewComponent, AddCustomerComponent, AddMovementComponent],
  imports: [
    CommonModule,
    SharedModule,
    OntimizeWebModule,
    AccountsRoutingModule
  ]
})
export class AccountsModule { }

El fichero movement-column-renderer.component.html contendrá la plantilla que mostrará la columna de la tabla que contendrá el render. Mediante directivas *ngIf, y calculando el valor numérico de la variable cellvalue, se mostrará el componente <span> correspondiente. Estos componentes <span> contienen un icono (<mat-icon>arrow_drop_down</mat-icon> o <mat-icon>arrow_drop_up</mat-icon>), tomarán un color u otro dependiendo del valor de cellvalue y añadirán un texto depués de pasar el valor cellvalue al método getCellData(value: any) del fichero movement-column-renderer.component.ts

movement-column-renderer.component.html
<ng-template #templateref let-cellvalue="cellvalue" let-rowvalue="rowvalue">
 <span *ngIf="cellvalue < 0" style="color:red;" fxLayoutAlign="end center">
   <mat-icon>arrow_drop_down</mat-icon>{{ getCellData(cellvalue) }}
 </span>
 <span *ngIf="cellvalue >= 0" style="color:green" fxLayoutAlign="end center">
   <mat-icon>arrow_drop_up</mat-icon>{{ getCellData(cellvalue) }}
 </span>
</ng-template>

El el fichero movement-column-renderer.component.ts la clase extenderá de OBaseTableCellRenderer. Esta vez, utilizaremos pipes para transformar el valor. En en constructor, llamaremos al método setComponentPipe() que estará definido en la propia clase, para que indique el el pipe del render es de tipo OCurrencPipe. En el método ngOnInit, se declaran los argumentos que aceptará el pipe, como  puede ser el símbolo de la moneda, los separadores de decimales y millares, etc. Dentro del método getCellData(value: any) transformará el valor con los argumentos de los pipe.

movement-column-renderer.component.ts
import { Component, OnInit, Injector, TemplateRef, ViewChild } from '@angular/core';
import { OBaseTableCellRenderer, OCurrencyPipe } from 'ontimize-web-ngx';

@Component({
  selector: 'app-movement-column-renderer',
  templateUrl: './movement-column-renderer.component.html',
  styleUrls: ['./movement-column-renderer.component.css']
})
export class MovementColumnRendererComponent extends OBaseTableCellRenderer implements OnInit {

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

 constructor(protected injector: Injector) {
   super(injector);
   this.setComponentPipe();
  }

  setComponentPipe() {
   this.componentPipe = new OCurrencyPipe(this.injector);
  }

  ngOnInit() {
   this.pipeArguments = {
      currencySimbol: '€',
      currencySymbolPosition: 'right',
      decimalDigits: 2,
      decimalSeparator: ',',
      grouping: true,
      thousandSeparator: '.'
    };
  }

  getCellData(value: any) {
   let cellValue: string;
   if (this.componentPipe && typeof this.pipeArguments !== 'undefined' && value !== undefined) {
      cellValue = this.componentPipe.transform(value, this.pipeArguments);
    }
   return cellValue;
  }
}

Solo resta añadir el render del tipo de cliente y el render del movimiento al detalle de las cuentas

accounts-detail.component.html
<o-form fxFill editable-detail="false" service="branches" entity="accountBalance" keys="ACCOUNTID"
 columns="ACCOUNTID;ACCOUNTNUMBER" show-header="yes" class="form-expanded" header-actions="R;D"
 show-header-navigation="yes" keys-sql-types="INTEGER">
 <o-row>
   <o-text-input class="input-padding" attr="ACCOUNTNUMBER" sql-type="STRING" fxFlex="40"></o-text-input>
   <o-combo attr="OFFICEID" fxFlex="50" service="branches" entity="branch" keys="OFFICEID" columns="OFFICEID;NAME"
     visible-columns="NAME" value-column="OFFICEID" class="input-padding"></o-combo>
   <o-currency-input attr="BALANCE" fxFlex="20" currency-symbol="EUR" max-decimal-digits="2"></o-currency-input>
 </o-row>
 <o-row>
   <o-date-input attr="ENDDATE" class="input-padding" fxFlex="20"></o-date-input>
   <o-percent-input attr="INTERESRATE" class="input-padding" max-decimal-digits="4" fxFlex="20"></o-percent-input>
   <o-text-input attr="ACCOUNTTYP" sql-type="STRING" fxFlex="60"></o-text-input>
 </o-row>
 <div layout="column" fxFlex>
   <div fxLayout="row" fxFlexFill>
     <o-table fxFlex="40" class="input-padding" attr="customersTable" service="customers" entity="vCustomerAccount"
       parent-keys="ACCOUNTID" keys="CUSTOMERACCOUNTID"
       columns="CUSTOMERACCOUNTID;CUSTOMERTYPEID;ID;NAME;SURNAME;ACCOUNTID"
       visible-columns="ID;NAME;SURNAME;CUSTOMERTYPEID" query-rows="15" insert-button="yes"
       insert-form-route="addcustomer/new" detail-mode="none">
       <o-table-column attr="ID" title="ID" class="o-table-column-centered"></o-table-column>
       <o-table-column attr="NAME" title="NAME" class="o-table-column-centered"></o-table-column>
       <o-table-column attr="SURNAME" title="SURNAME" class="o-table-column-centered"></o-table-column>
       <o-table-column attr="CUSTOMERTYPEID" title="CUSTOMERTYPEID" class="o-table-column-centered">
         <app-customertype-column-renderer></app-customertype-column-renderer>
       </o-table-column>
     </o-table>
     <o-table fxFlex="60" attr="movementsTable" service="movements" entity="movement" parent-keys="ACCOUNTID"
       keys="MOVEMENTID" columns="DATE_;CONCEPT;MOVEMENT;MOVEMENTTYPEID"
       visible-columns="DATE_;CONCEPT;MOVEMENT;MOVEMENTTYPEID" query-rows="15" insert-form-route="addMovement/new"
       detail-mode="none" insert-button="yes">
       <o-table-column attr="DATE_" title="DATE_" type="date" format="LL"></o-table-column>
       <o-table-column attr="MOVEMENT" title="MOVEMENT" class="o-table-column-centered">
         <app-movement-column-renderer></app-movement-column-renderer>
       </o-table-column>
       <o-table-column attr="MOVEMENTTYPEID" title="MOVEMENTTYPEID">
         <o-table-cell-renderer-service service="movements" entity="movementType" columns="DESCRIPTION;MOVEMENTTYPEID"
           value-column="DESCRIPTION">
         </o-table-cell-renderer-service>
       </o-table-column>
     </o-table>
   </div>
 </div>
</o-form>

Como se ha añadido una clase llamada form-expanded la añadimos al fichero de estilos para que 

styles.scss
/* You can add global styles to this file, and also import other style files */
.input-padding {
 padding-right: 6px;
}

.o-table-column-centered {
 text-align: center;
}

.form-expanded {
 form.inner-form {
   height: 100%;
  }
}

Este es el aspecto final del listado de clientes y el aspecto de la vista de cuentas.


11_1.png


11_2.png

<< Tutorial previoÍndicePróximo tutorial >>
Etiquetas:
Creado por Pablo Martínez Kirsten el 2021/02/11 16:51
    
This wiki is licensed under a Ontimize license
XWiki Enterprise 8.0