Crear un formulario de detalle

Introducción

En este tutorial se creará un nuevo componente (el formulario de detalle de un cliente) para poder visualizar los detalles de dicho cliente al hacer clic sobre el registro de la tabla correspondiente.

Crear el formulario de detalle

Mostrar, editar y borrar

Para crear el formulario de detalle, lo crearemos dentro del módulo de customers. Para ello, nos situamos en la terminal dentro del módulo de customers (a la altura del componente customers-home) y ejecutamos el comando de creación de componentes de Angular CLI:

npx ng g component --skip-tests customers-detail

(Más información acerca del comando aquí). Al terminar, tendremos una carpeta llamada customers-detail, que contendrá los archivos relacionados con el componente.

Modificamos el fichero customers.module.ts, añadiéndole el import del nuevo componente, y situándolo en el array de declaraciones. A su vez, establecemos la nueva ruta en el fichero customers-routing.module.ts. Para conocer la ruta que el componente tabla indicará para el detalle de cada uno de sus registros, tenemos que comprobar cual es el parámetro key de la tabla para la cual queremos obtener el detalle (en este caso, tenemos que ver el parámetro key de la tabla del fichero customers-home.component.html). Esto es así debido a que cuando naveguemos en la tabla, al abrir un registro, concatenará el valor de la columna definida en el atributo key a la URL. Para definir ese comportamiento en el módulo de enrutamiento, precedemos el nombre de la columna por el símbolo de los dos puntos :.

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';


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

customers-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CustomersHomeComponent } from './customers-home/customers-home.component';
import { CustomersDetailComponent } from './customers-detail/customers-detail.component';

const routes: Routes = [{
  path: '',
  component: CustomersHomeComponent
},
{
  path: ':CUSTOMERID',
  component: CustomersDetailComponent
}];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class CustomersRoutingModule { }

Si hacemos clic en el registro de una tabla, se cargará el formulario del fichero customers-detail.component.html. A continuación modificaremos el contenido del fichero para conseguir un formulario similar al siguiente mockup.

tutorial_o_web_12.png

Lo primero es establecer el layout de pestañas, para que cada nuevo registro que se consulte se añada a una pestaña nueva. Para ello, en el componente customers-home.component.html añadimos un o-form-layout-manager

customers-home.component.html

<o-form-layout-manager attr="customersHome" title="{{'CUSTOMERS' | oTranslate }}" separator=" " mode="tab" label-columns="NAME;SURNAME">
    <o-table attr="customersTable" service="customers" entity="customer" keys="CUSTOMERID"
        columns="CUSTOMERID;ID;PHOTO;NAME;SURNAME;STARTDATE;EMAIL"
        visible-columns="ID;PHOTO;NAME;SURNAME;STARTDATE;EMAIL" query-rows="20">
        <o-table-column attr="PHOTO" title="PHOTO" orderable="no" searchable="no" type="image" avatar="yes"
            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>
</o-form-layout-manager>
o-form-layout-manager (atributos de o-form-layout-manager)
Atributo Valor Significado
attr customersHome Establece un identificador al layout manager
title {{'CUSTOMERS' | oTranslate }} Establece el título que tendrá la pestaña principal. En este caso se usa la interpolación de Angular, que intenta evaluar la cadena "CUSTOMERS" a traves del pipe "| oTranslate", que devuelve la cadena que se le pasa como entrada traducida al idioma en el que esté definida la aplicación.
separator "" Separador que se usará entre las columnas en los títulos de las pestañas
mode tab Modo en el que funcionará el o-form-layout-manager. En este caso, en modo pestañas
label-columns NAME;SURNAME Columnas que usará para el título de cada una de las pestañas. Estas estarán separadas por la cadena definida en el atributo separator

Ahora, en el fichero customers-detail.component.html, construiremos el formulario de detalle

customers-detail.component.html

<o-form attr="customerDetail" service="customers" entity="customer" keys="CUSTOMERID" header-actions="R;I;U;D"
    show-header-navigation="no">
    <o-text-input attr="CUSTOMERID" sql-type="INTEGER" enabled="no"></o-text-input>
    <div fxLayout="row">
        <div>
            <o-image id="CUSTOMER_PHOTO" attr="PHOTO" empty-image="assets/images/no-image.png"
                sql-type="OTHER"></o-image>
        </div>
        <o-column fxFlex title="CUSTOMER_PERSONAL_INFORMATION">
            <div fxLayout="row" fxLayoutGap="8px">
                <o-text-input fxFlex="40" attr="NAME" required="yes"></o-text-input>
                <o-text-input fxFlex="40" attr="SURNAME" required="yes"></o-text-input>
                <o-date-input fxFlex="20" attr="STARTDATE"></o-date-input>
            </div>
            <div fxLayout="row" fxLayoutGap="8px">
                <o-nif-input fxFlex="40" attr="ID" required="yes"></o-nif-input>
                <o-integer-input fxFlex="40" attr="PHONE" step="0" thousand-separator=" "></o-integer-input>
                <o-combo fxFlex="20" attr="CUSTOMERTYPEID" service="customers" entity="customerType"
                    keys="CUSTOMERTYPEID" columns="CUSTOMERTYPEID;DESCRIPTION" visible-columns="DESCRIPTION"
                    value-column="CUSTOMERTYPEID"></o-combo>
            </div>
            <o-email-input attr="EMAIL"></o-email-input>
            <o-text-input attr="ADDRESS"></o-text-input>
            <div fxLayout="row" fxLayoutGap="8px">
                <o-real-input fxFlex="50" attr="LONGITUDE" decimal-separator="," max-decimal-digits="10"
                    min-decimal-digits="0"></o-real-input>
                <o-real-input fxFlex="50" attr="LATITUDE" decimal-separator="," max-decimal-digits="10"
                    min-decimal-digits="0"></o-real-input>
            </div>
            <o-textarea-input attr="COMMENTS"></o-textarea-input>
        </o-column>
    </div>
</o-form>
o-form (atributos de o-form)
Atributo Valor Significado
service customers Ruta del servicio REST
entity customer Entidad del servicio
keys CUSTOMERID Claves de la entidad, separadas por ;
header-actions R;I;U;D Botones de acción disponibles en la botonera para las operaciones CRUD estándar, separadas por ;. Las operaciones disponibles son R(Refrescar), I (Insertar), U (Actualizar), D (Borrar)
show-header-navigation no Incluye botones de navegación en la botonera. No se incluyen cuando se encuentra en modo pestaña (definido por el componente o-form-layout-manager)
o-text-input (atributos de o-text-input)
Atributo Valor Significado
attr CUSTOMERID Identificador del campo
sql-type INTEGER Indica el tipo de dato que contiene este campo. Se debe especificar la cadena del tipo de dato de esta lista
enabled no Indica si el campo está habilitado o no
required yes/no Indica si se requiere que este campo contenga valor
Directivas de maquetación Angular Layout - Demo interactiva
Atributo Valor Significado
fxLayout row Los componentes internos se colocan en forma de fila
fxFlex 50/40/20 Ocupa el 50 / 40 / 20 % del ancho disponible del contenedor padre. Si no tiene valor, trata de ocupar todo el esté disponible
fxLayoutGap 8px Mantiene una separación de 8px entre los elementos de este contenedor
o-row y o-column (atributos de containers)
Atributo Valor Significado
title CUSTOMER_PERSONAL_INFORMATION Etiqueta de la columna de título
o-image (atributos de o-image)
Atributo Valor Significado
attr PHOTO Identificador del campo
empty-image assets/images/no-image.png Imagen mostrada cuando el componente no tiene valor.
o-date-input (atributos de o-date-input)
Atributo Valor Significado
attr STARTDATE Identificador del campo
o-nif-input (atributos de o-nif-input)
Atributo Valor Significado
attr STARTDATE Identificador del campo
o-integer-input (atributos de o-integer-input)
Atributo Valor Significado
attr PHONE Identificador del campo
step 1 Número de incremento en los botones del campo
grouping no Indica si se debe agrupar el número con los separadores de millar.
o-combo (atributos de o-combo)
Atributo Valor Significado
attr CUSTOMERTYPEID Identificador del campo
service customers Ruta del servicio REST
entity customerType Entidad del servicio
keys CUSTOMERTYPEID Clave primaria de la entidad
columns CUSTOMERTYPEID;DESCRIPTION Columnas de la entidad que se consulta
visible-columns DESCRIPTION Columnas visibles
value-column CUSTOMERTYPEID Columna de la entidad de las que el componente obtiene el valor
o-email-input (atributos de o-email-input)
Atributo Valor Significado
attr EMAIL Identificador del campo
o-real-input (atributos de o-real-input)
Atributo Valor Significado
attr LONGITUDE Identificador de campo
decimal-separator , Separador decimal
max-decimal-digits 10 Máximos dígitos decimales
min-decimal-digits 0 Mínimos dígitos decimales
o-textarea-input (atributos de o-textarea-input)
Atributo Valor Significado
attr COMMENTS Identificador de campo

A continuación, se incluyen las nuevas traducciones para el formulario de detalle.

en.json

{
  ...
  "CUSTOMER_PERSONAL_INFORMATION": "Personal Information",
  "CUSTOMERID": "Customer Id.",
  "PHONE": "Phone",
  "CUSTOMERTYPEID": "Customer type",
  "ADDRESS": "Address",
  "LONGITUDE": "Longitude",
  "LATITUDE": "Latitude",
  "COMMENTS": "Comments"
}

es.json

{
  ...
  "CUSTOMER_PERSONAL_INFORMATION": "Información personal",
  "CUSTOMERID": "Id. cliente",
  "PHONE": "Teléfono",
  "CUSTOMERTYPEID": "Tipo cliente",
  "ADDRESS": "Dirección",
  "LONGITUDE": "Longitud",
  "LATITUDE": "Latitud",
  "COMMENTS": "Comentarios"
}

Este será el aspecto final del formulario:

tutorial_o_web_13.png

Insertar

Para insertar un nuevo registro, hay que indicar en el módulo de rutas cual es el componente que se usará para mostrar el formulario. Se puede usar un componente existente, o se puede crear uno nuevo específico. En este ejemplo crearemos un nuevo componente, donde usaremos un formulario prácticamente idéntico al del detalle. Volveremos a crear un nuevo componente mediante la línea de comando:

npx ng g component --skip-tests customers-new

En este componente, se creará un formulario idéntico al de detalle, excepto por que desaparece el campo del Id. de cliente

customers-new.component.html

<o-form attr="customerDetail" service="customers" entity="customer" keys="CUSTOMERID" header-actions="R;I;U;D"
    show-header-navigation="no">
    <div fxLayout="row">
        <div>
            <o-image id="CUSTOMER_PHOTO" attr="PHOTO" empty-image="assets/images/no-image.png"
                sql-type="OTHER"></o-image>
        </div>
        <o-column fxFlex title="CUSTOMER_PERSONAL_INFORMATION">
            <div fxLayout="row" fxLayoutGap="8px">
                <o-text-input fxFlex="40" attr="NAME" required="yes"></o-text-input>
                <o-text-input fxFlex="40" attr="SURNAME" required="yes"></o-text-input>
                <o-date-input fxFlex="20" attr="STARTDATE"></o-date-input>
            </div>
            <div fxLayout="row" fxLayoutGap="8px">
                <o-nif-input fxFlex="40" attr="ID" required="yes"></o-nif-input>
                <o-integer-input fxFlex="40" attr="PHONE" step="0" thousand-separator=" "></o-integer-input>
                <o-combo fxFlex="20" attr="CUSTOMERTYPEID" service="customers" entity="customerType"
                    keys="CUSTOMERTYPEID" columns="CUSTOMERTYPEID;DESCRIPTION" visible-columns="DESCRIPTION"
                    value-column="CUSTOMERTYPEID"></o-combo>
            </div>
            <o-email-input attr="EMAIL"></o-email-input>
            <o-text-input attr="ADDRESS"></o-text-input>
            <div fxLayout="row" fxLayoutGap="8px">
                <o-real-input fxFlex="50" attr="LONGITUDE" decimal-separator="," max-decimal-digits="10"
                    min-decimal-digits="0"></o-real-input>
                <o-real-input fxFlex="50" attr="LATITUDE" decimal-separator="," max-decimal-digits="10"
                    min-decimal-digits="0"></o-real-input>
            </div>
            <o-textarea-input attr="COMMENTS"></o-textarea-input>
        </o-column>
    </div>
</o-form>

Declaramos cuál es el nuevo módulo, y la ruta para el que se usará este nuevo componente

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';


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

customers-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CustomersHomeComponent } from './customers-home/customers-home.component';
import { CustomersDetailComponent } from './customers-detail/customers-detail.component';
import { CustomersNewComponent } from './customers-new/customers-new.component';

const routes: Routes = [{
  path: '',
  component: CustomersHomeComponent
},
{
  path: "new",
  component: CustomersNewComponent
},
{
  path: ':CUSTOMERID',
  component: CustomersDetailComponent
}];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class CustomersRoutingModule { }

arrow_back Tutorial anterior Próximo tutorial arrow_forward