Á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

Temos como objetivo desta técnica apresentar uma forma de customizar as telas HTML através de intervenções no back-end.

No PO-UI, um objeto dinâmico, trabalha recebendo uma lista de campos que serão apresentados em tela e 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.

Abaixo temos um fluxo das informações do PO-UI até a UPC em Progress:



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 do endpoint 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 é informado sobre a utilização da include "utp/ut-api.i", pois este include identificará 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 os metadados do campos em questão;
  • pGetAll que retorna os valores dos campos em questão;

Como podemos verificar essas duas procedures possuem chamadas para o programa 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.

    ...
    
    // monta 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:

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

{
    "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: 'http://localhost:8180/dts/datasul-rest/resources/prg/trn/v1/idiomas/metadata' // endpoint dos metadados
      serviceLoadApi: 'http://localhost:8180/dts/datasul-rest/resources/prg/trn/v1/idiomas/metadata' // 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 variável 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 = 'http://localhost:8180/dts/datasul-rest/resources/prg/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

Criamos dois novos facilitadores para auxiliar no desenvolvimento das API's dentro da classe "com.totvs.framework.api.JsonAPIUtils":

MétodoDescriçãoAssinatura/Exemplo
convertAblTypeToHtmlTypeConverte os tipos nativos do Progress para os tipos esperados pelo PO-UI

Assinatura:

convertAblTypeToHtmlType (INPUT cType AS CHARACTER)

Exemplo:

ASSIGN cType = JsonAPIUtils:convertAblTypeToHtmlType ("integer").

O retorno no cType será "number", que é um formato reconhecido pelo PO-UI.

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

Assinatura:

convertToCamelCase (INPUT cKey AS CHARACTER)

Exemplo:

ASSIGN cField= JsonAPIUtils:convertToCamelCase ("cod_e_mail_usuar").

O retorno no cField será "codEMailUsuar", que é o campo em Camel Case.


06. 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>