Árvore de páginas

Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.

CONTEÚDO

  1. Visão Geral
  2. Pré-Requisitos
  3. Técnicas 
    1. Back-End Progress
    2. Front-End PO-UI
  4. Exemplo de utilização
    1. Back-End Progress
    2. Front-End PO-UI
  5. Facilitadores Progress
  6. Links Úteis


01. VISÃO GERAL

O Objetivo desta técnica é apresentar uma forma de customizar as telas HTML que foram construídas utilizando o Form Dinâmica do PO-UI.

No PO-UI, a Form Dinâmica trabalha recebendo uma lista de campos que serão apresentados em tela, bem como uma outra lista contendo os dados que serão apresentados nestes campos.

Este guia será divido basicamente em duas partes, como vamos trabalhar no Back-End Progress e acessar esses dados através do Front-End PO-UI.


CARLOS - Colocar aqui as imagens para o pessoal ter uma visao geral (piscar o olho)


02. Pré-Requisitos

Temos como pré-requisito para execução da técnica citada abaixo: 

03. TÉCNICAS

Técnica Back-End Progress:


Introdução:

A técnica Back-End Progress é formada pelos passos abaixo:

  • Construção de API Rest para tela customizada;
  • Cadastro da API Rest no Cadastro de Programas (men012aa) com a respectiva UPC;
  • Utilizar a include "include/i-epcrest.i" para chamada UPC na API Rest ;
  • Desenvolvimento da UPC.


Construção de API REST para tela customizada:

Para que possamos customizar uma tela HTML construída em PO-UI, necessitamos que o Back-End nos retorne qual o metadado e os valores da tela em questão através de uma API Rest.

Sendo assim essa API deve conter no mínimo dois endpoints básicos:

  • Endpoint que retornará o metadados da tela;
  • Endpoint para retornar os valores da tela;


Cadastro da API Rest no Cadastro de Programas (men012aa) com a respectiva UPC:

Tendo criado a API REST que retorna os dados básicos para a tela, partimos para o segundo o passo, que é preparação da API para customização.

Esta API deverá ser cadastrada no cadastro de programas (MEN012AA), onde poderemos também especificar a UPC que será utilizada.

Na técnica de construção de APIs REST informa que é necessario a utilização da include "utp/ut-api.i", pois além do que ela se propõem, ela também está identificando se a API possui uma UPC cadastrada ou não.


Informações
titleIMPORTANTE

IMPORTANTE: A UPC para APIs REST possui um formato diferenciado das UPCs Padrões e de Ponto Estratégico, pois um dos parâmetros utilizados é um JsonObject.


Utilizar a include "include/i-epcrest.i" para chamada UPC na API Rest :

Enfim para chamarmos um programa de customização, criamos uma include que fará esta chamada. Abaixo segue mais detalhes sobre esta include.

Ela encontra-se na pasta include e possui o nome i-epcrest.i, conforme o exemplo abaixo:

Bloco de código
{include/i-epcrest.i &endpoint=<nome_end_point> &event=<nome_do_evento> &jsonVar=<variável_jsonObject_com_conteúdo>}
Informações
titleIMPORTANTE

IMPORTANTE: Não é permitido misturar tipos diferentes de UPCs no mesmo programa, pois as assinaturas são incompatíveis e poderão ocorrer erros de parâmetros.


Pré-Processadores da include i-epcrest.i:

Abaixo temos a lista de pré-processadores que devem ser passados para a include i-epcrest.i:

PreprocessadorDescrição
endpointEspecifica o endpoint que esta sendo chamado pelo HTML. Uma API REST deve possuir 1 ou mais endpoints.
eventÉ o nome do evento que esta ocorrendo antes de chamar a UPC. Exemplo: beforeCreate, getAll, getMetaData, etc.
jsonVarÉ a variável do tipo JsonObject que será passada como INPUT-OUTPUT para a UPC.
Informações
titleIMPORTANTE

IMPORTANTE: Todas as UPCs de API REST deverão importar os seguintes pacotes:

                       USING PROGRESS.json.*.

                       USING PROGRESS.json.ObjectModel.*.

                       USING com.totvs.framework.api.*.


Parâmetros recebidos na UPC da API REST:

ParametroTipoTipo de DadosDescrição
pEndPointINPUTCHARACTERContem o nome do endpoint que está sendo executado.
pEventINPUTCHARACTERContem o nome do evento que está sendo executado.
pAPIINPUTCHARACTERContem o nome da API que está sendo executada.
jsonIOINPUT-OUTPUTJSONObjectContem o JSON com os dados (campos ou valores) que poderão ser customizados.

Front-End PO-UI:

Introdução:

Para termos uma tela dinâmica, de acordo com o que o back-end retorna, precisamos utilizar os componentes dinâmicos ou as templates do PO-UI sendo eles:

Componentes:

  • Dynamic-Form;
  • Dynamic-View;

Templates:

  • Page-Dynamic-Detail;
  • Page-Dymic-Edit;
  • Page-Dynamic-Search;
  • Page-Dynamic-Table;

Comunicando com o Back-End Progress

Basicamente para comunicar com o back-end teremos que ter dois serviços que irão alimentar as informações para tela:

  • Metadado
    • Serviço que irá retornar os campos e as propriedades da tela;
  • Values
    • Serviço que irá retornar os valores dos campos;


04. EXEMPLO DE UTILIZAÇÃO

Back-End Progress

Introdução:

Para exemplificar a técnica citada acima, criamos uma API Rest que irá retornar os dados da tabela de idiomas, chamando uma UPC que acrescenta algumas informações da tabela usuar_mestre.

Cadastro da UPC:

Primeiramente temos que cadastrar a API REST no cadastro de programas (MEN012AA) e também especificar a UPC a ser utilizada, conforme o exemplo abaixo:


Na aba Opções, teremos que especificar o Template como "API REST", conforme o exemplo abaixo:


API Rest com chamada de UPC:

Abaixo temos um exemplo de API REST que possuí duas procedures:

  • pGetMetaData que retorna o metadados do campos em questão;
  • pGetAll que retorna os valores dos campos em questão;

Como podemos essas duas procedures tem chamadas para programas de UPC:

Bloco de código
titleAPI REST
linenumberstrue
PROCEDURE pGetAll:
    DEFINE INPUT  PARAMETER oJsonInput  AS JsonObject NO-UNDO.
    DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject NO-UNDO.

    ...
    
    oIdiomas    = JsonAPIUtils:convertTempTableToJsonArray(TEMP-TABLE ttIdiomas:HANDLE).
    oObj        = new JsonObject().
    oObj:add('root', oIdiomas).
    
    /* realiza a chamada da UPC Progress */
    {include/i-epcrest.i &endpoint=getAll &event=getAll &jsonVar=oObj}
    oIdiomas = oObj:getJsonArray('root').    

    oResponse   = NEW JsonAPIResponse(oIdiomas).
    oJsonOutput = oResponse:createJsonResponse().
    
    ...
    
END PROCEDURE.

PROCEDURE pGetMetaData:
    DEFINE INPUT  PARAMETER oJsonInput  AS JsonObject NO-UNDO.
    DEFINE OUTPUT PARAMETER oJsonOutput AS JsonObject NO-UNDO.

    ...
    
    // monsta-se a lista de campos que aparecerão em tela
    oObj        = new JsonObject().
    oObj:add('root', jAList).
    
    /* realiza a chamada da UPC Progress */
    {include/i-epcrest.i &endpoint=getMetaData &event=getMetaData &jsonVar=oObj}    
    oIdiomas = oObj:getJsonArray('root').    
    
    oResponse   = NEW JsonAPIResponse(oIdiomas).
    oJsonOutput = oResponse:createJsonResponse().
    
    ...
    
END PROCEDURE.

Programa UPC:

Abaixo temos um exemplo de uma UPC criada para a API REST:

Bloco de código
titleUPC da API REST
linenumberstrue
/**************************************************************************
** idiomas_upc.p - Exemplo de epc para Endpoints REST 
***************************************************************************/

USING PROGRESS.json.*.
USING PROGRESS.json.ObjectModel.*.
USING com.totvs.framework.api.*.

DEFINE INPUT        PARAMETER pEndPoint AS CHARACTER  NO-UNDO.
DEFINE INPUT        PARAMETER pEvent    AS CHARACTER  NO-UNDO.
DEFINE INPUT        PARAMETER pAPI      AS CHARACTER  NO-UNDO.
DEFINE INPUT-OUTPUT PARAMETER jsonIO    AS JSONObject NO-UNDO.

DEFINE VARIABLE jAList  AS JsonArray  NO-UNDO.
DEFINE VARIABLE jObj    AS JsonObject NO-UNDO.

DEFINE VARIABLE hBuf    AS HANDLE     NO-UNDO.
DEFINE VARIABLE ix      AS INTEGER    NO-UNDO.
DEFINE VARIABLE iTot    AS INTEGER    NO-UNDO.
DEFINE VARIABLE cType   AS CHARACTER  NO-UNDO.

// carrega as definicoes dos campos da tabela
IF  pEndPoint = "getMetaData"
AND pEvent    = "getMetaData" THEN DO ON STOP UNDO, LEAVE:

    // obtem a lista de campos e valores    
    ASSIGN jAList = jsonIO:getJsonArray('root').

    // cria um buffer da tabela para obter os campos da tabela usuar_mestre
    CREATE BUFFER hBuf FOR TABLE 'usuar_mestre'.
    DO  ix = 1 TO hBuf:NUM-FIELDS:
        // ignora os campos que nao estao nesta lista
        IF  NOT CAN-DO("nom_usuario,cod_usuario,cod_dialet", hBuf:BUFFER-FIELD(ix):NAME) THEN
            NEXT.
        
        // monta a formatacao do item 
        ASSIGN jObj = NEW JsonObject().
        jObj:add('property', JsonAPIUtils:convertToCamelCase(hBuf:BUFFER-FIELD(ix):NAME)).
        jObj:add('label', hBuf:BUFFER-FIELD(ix):Label).
        jObj:add('visible', TRUE).
        
        // ajusta o tipo
        ASSIGN cType = JsonAPIUtils:convertAblTypeToHtmlType(hBuf:BUFFER-FIELD(ix):type).
        jObj:add('type', cType).
        
        // adiciona o objeto na lista
        jAList:add(jObj).
    END.
    hBuf:BUFFER-RELEASE().
    DELETE OBJECT hBuf.
    
    // retorna a nova lista com os campos adicionados
    jsonIO:Set("root", jAList).
END.

// carrega os valores dos campos da tabela
IF  pEndPoint = "getAll"
AND pEvent    = "getAll" THEN DO ON STOP UNDO, LEAVE:
    // obtem a lista de campos e valores    
    ASSIGN jAList = jsonIO:getJsonArray('root').
    
    FIND FIRST usuar_mestre NO-LOCK NO-ERROR.

    // quardado o tamanho da lista em variavel para evitar LOOP devido a adicionar novos itens na lista
    ASSIGN iTot = jAList:length.

    DO  ix = 1 TO iTot:
        ASSIGN jObj = jAList:GetJsonObject(ix).
        
        // alimenta os novos dados
        IF  AVAILABLE usuar_mestre THEN DO:
            jObj:add('codUsuario', usuar_mestre.cod_usuario) NO-ERROR.
            jObj:add('nomUsuario', usuar_mestre.nom_usuario) NO-ERROR.
            jObj:add('codDialet', usuar_mestre.cod_dialet) NO-ERROR.
        END.
        
        // adiciona o objeto na lista
        jAList:add(jObj).
        
        FIND NEXT usuar_mestre NO-LOCK NO-ERROR.
    END.

    // devolve para o json ROOT a lista nova com novos objetos 
    jsonIO:Set("root", jAList).
END.

IF  pEndPoint = "getOne"
AND pEvent    = "getOne" THEN DO ON STOP UNDO, LEAVE:
    // nao implementado
END.

IF  pEndPoint = "create"
AND pEvent    = "afterCreate" THEN DO ON STOP UNDO, LEAVE:
    // nao implementado
END.

/* fim */

Resultado ao chamar ao API tendo uma UPC cadastrada:

Ao fazer as requisições, virão os seguintes resultados na UPC.

Bloco de código
linenumberstrue
Busca do METADADOS onde foram adicionados os novos campos codUsuario, nomUsuario e codDialet:

GET - http://localhost:8180/dts/datasul-rest/resources/prg/trn/v1/idiomas/metadados

{
    "total": 9,
    "hasNext": false,
    "items": [
        {
            "visible": true,
            "property": "codIdioma",
            "label": "Idioma",
            "type": "string"
        },
        {
            "visible": true,
            "property": "desIdioma",
            "label": "Descrição",
            "type": "string"
        },
        {
            "visible": true,
            "property": "codIdiomPadr",
            "label": "Idioma Padrão",
            "type": "string"
        },
        {
            "visible": true,
            "property": "codUsuarUltAtualiz",
            "label": "Usuário Ult Atualiz",
            "type": "string"
        },
        {
            "visible": true,
            "property": "datUltAtualiz",
            "label": "Última Atualização",
            "type": "string"
        },
        {
            "visible": true,
            "property": "hraUltAtualiz",
            "label": "Hora Última Atualiz",
            "type": "string"
        },
        {
            "visible": true,
            "property": "codUsuario",
            "label": "Usuário",
            "type": "string"
        },
        {
            "visible": true,
            "property": "nomUsuario",
            "label": "Nome",
            "type": "string"
        },
        {
            "visible": true,
            "property": "codDialet",
            "label": "Dialeto",
            "type": "string"
        }
    ]
}

Busca dos dados onde foram adicionados novos valores:

GET - http://localhost:8180/dts/datasul-rest/resources/prg/trn/v1/idiomas

{
    "total": 3,
    "hasNext": false,
    "items": [
        {
            "codIdioma": "12345678",
            "desIdioma": "12345678901234567890",
            "hraUltAtualiz": "08:35:00",
            "datUltAtualiz": "2010-02-15",
            "codUsuario": "super",
            "nomUsuario": "Super",
            "codDialet": "Pt"
        },
        {
            "codIdioma": "ale",
            "desIdioma": "Alemão",
            "hraUltAtualiz": "15:33:45",
            "datUltAtualiz": "2018-10-23",
            "codUsuario": "Joao",
            "nomUsuario": "Joao da Silva",
            "codDialet": "PT"
        },
        {
            "codIdioma": "ESP",
            "desIdioma": "Espanhol",
            "hraUltAtualiz": "12:20:03",
            "datUltAtualiz": "2004-12-08",
            "codUsuario": "Manoel",
            "nomUsuario": "Manoel da Silva",
            "codDialet": "PT"
        }
    ]
}


Front-End PO-UI

Introdução:

Para este exemplo vamos utilizar o template "Page-Dynamic-Detail", mostrando os dados de acordo com o que o back-end nos retorna.

O desenvolvimento do front-end utilizando este campo componente se divide basicamente em três partes:

  • Routes:
    • Na definição da rota é onde vamos colocar qual o caminho da API que vai retornar os dados o Metadados;
  • HTML
    • No HTML basta colocarmos o componente, pois o metadados irá retornar o que precisamos para renderizar o componente;
  • TypeScript
    • No Typescript do componente vamos basicamente informar qual será a API que retornará os dados de acordo com metadado;

Routes:

Abaixo segue exemplo de como ficará  o arquivo de rotas de nossa aplicação, como podemos perceber temos a tag "data", onde vamos passar os dados da API de Metadados.

Bloco de código
languagejs
titleapp-routing.module.ts
linenumberstrue
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { IdiomaDynamicComponent } from './idioma/idioma-dynamic.component';


const routes: Routes = [

 
  {
    path: 'idioma', component: IdiomaDynamicComponent,
    data: {
      serviceMetadataApi: 'https://6a6a8bb8-09be-496b-aeea-faa3a7052052.mock.pstmn.io/api/trn/v1/idiomas/metadados', // endpoint dos metadados
      serviceLoadApi: 'https://6a6a8bb8-09be-496b-aeea-faa3a7052052.mock.pstmn.io/api/trn/v1/idiomas/metadados' // endpoint de customizações dos metadados
    }
    
  },
  { path: '', redirectTo: '/idioma', pathMatch: 'full' },
  {
    path: '**', component: IdiomaDynamicComponent
  }


];

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


HTML: 

No HTML devemos inserir a tag do componente dinâmico "po-page-dynamic-detail" para renderizar os dados de acordo com o retorno do back-end.

Ponto de atenção é a tag "[p-service-api]="serviceApi" onde devemos apontar qual a váriavel do componente que indica a URL da API de Dados.

Bloco de código
languagexml
titleidioma-dynamic.component.html
linenumberstrue
<div class="po-wrapper">
  <po-toolbar p-title="Datasul - Dynamic - Custom"></po-toolbar>

  <po-page-dynamic-detail p-auto-router p-title="Idiomas" [p-service-api]="serviceApi">
  </po-page-dynamic-detail>

</div>


TypeScript: 

No Typescript devemos informar qual será a API que retornará os dados para a tela através da variável "serviceApi".

Bloco de código
languagejs
titleidioma.component.ts
linenumberstrue
import { Component } from '@angular/core';
import { PoMenuItem } from '@po-ui/ng-components';
import { PoBreadcrumb } from '@po-ui/ng-components';
import { PoPageDynamicDetailActions} from '@po-ui/ng-templates';

@Component({
  selector: 'app-idioma-dynamic',
  templateUrl: './idioma-dynamic.component.html',
  styleUrls: ['./idioma-dynamic.component.css']
})

export class IdiomaDynamicComponent {

  public readonly serviceApi = 'https://6a6a8bb8-09be-496b-aeea-faa3a7052052.mock.pstmn.io/api/trn/v1/idiomas';

  public readonly actions: PoPageDynamicDetailActions = {
    back: '/documentation/po-page-dynamic-table'
  };

  public readonly breadcrumb: PoBreadcrumb = {
    items: [
      { label: 'Home', link: '/' },
      { label: 'People', link: '/documentation/po-page-dynamic-table' },
      { label: 'Detail' }
    ]
  };
}

05. Facilitadores Progress

Para auxiliar no desenvolvimento das API's com chamadas para UPC criamos facilitadores Progress.

Informações
titleIMPORTANTE

IMPORTANTE: Todos os facilitadores estão disponíveis na classe Progress "com.totvs.framework.api.JsonAPIUtils".


convertAblTypeToHtmlType:

Criamos este facilitador para converter os tipos nativos do Progress, para os tipos esperados pelo PO-UI.

Bloco de código
linenumberstrue
    /*------------------------------------------------------------------------------
    Purpose: Converte o tipo de dado Progress em tipo HTML
    ------------------------------------------------------------------------------*/ 
    METHOD PUBLIC STATIC CHARACTER convertAblTypeToHtmlType (INPUT cType AS CHARACTER):
        DEFINE VARIABLE cRet AS CHARACTER NO-UNDO.
        ASSIGN cRet = "string".
        CASE cType:
            WHEN "character" THEN ASSIGN cRet = "string".
            WHEN "integer"   THEN ASSIGN cRet = "number".
            WHEN "decimal"   THEN ASSIGN cRet = "currency".
            WHEN "logical"   THEN ASSIGN cRet = "boolean".
            WHEN "datetime"  THEN ASSIGN cRet = "datetime".
            WHEN "date"      THEN ASSIGN cRet = "date".
        END CASE.
        RETURN cRet.
    END METHOD.


convertToCamelCase:

Criamos este facilitador para converter os nomes dos campos lidos da tabela normalmente com "_" para "camel case" que é o mais comum utilizado em Json's.   

Bloco de código
linenumberstrue
    /*------------------------------------------------------------------------------
     Purpose: Converte uma string "aaa.BBB.ccc" em "aaaBbbCcc"
    ------------------------------------------------------------------------------*/
    METHOD PUBLIC STATIC CHARACTER convertToCamelCase (INPUT cKey AS CHARACTER):
        DEFINE VARIABLE cNKey       AS CHARACTER NO-UNDO.
        DEFINE VARIABLE cTmp        AS CHARACTER NO-UNDO.
        DEFINE VARIABLE ix          AS INTEGER   NO-UNDO.
        /* se vier separador "-" ou "_", substitui para "." para tornar a rotina generica */
        ASSIGN cKey = REPLACE(cKey, "_", ".")
               cKey = REPLACE(cKey, "-", ".").
    
        DO  ix = 1 TO NUM-ENTRIES(cKey, "."):
            ASSIGN cTmp = ENTRY(ix, cKey, ".").
            IF  ix > 1 THEN
                ASSIGN cTmp = upper(substr(cTmp, 1, 1)) + lower(substr(cTmp, 2, LENGTH(cTmp))).
            ASSIGN cNKey = cNKey + cTmp.
        END.
        RETURN cNKey.
    END METHOD.

07. Links Úteis

Documentação API Datasul:

PO-UI:

GIT Projeto:



HTML
<!-- esconder o menu --> 


<style>
div.theme-default .ia-splitter #main {
    margin-left: 0px;
}
.ia-fixed-sidebar, .ia-splitter-left {
    display: none;
}
#main {
    padding-left: 10px;
    padding-right: 10px;
    overflow-x: hidden;
}

.aui-header-primary .aui-nav,  .aui-page-panel {
    margin-left: 0px !important;
}
.aui-header-primary .aui-nav {
    margin-left: 0px !important;
}
</style>