Árvore de páginas

Versões comparadas

Chave

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

...

Introdução:

Para este exemplo vamos utilizar o template "Page-Dynamic-Detail", mostrando criar um CRUD com template dinâmico, onde serão mostrados os dados de acordo com o que o back-end nos retornabackend retornar.

O desenvolvimento do front-end frontend 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 Metadadosdefinir todos os caminhos dos componentes;
  • HTML
    • No HTML basta colocarmos o componenteos componentes, 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 realizar uma pequena lógica para o tratamento dos dados de acordo com metadado;


Abaixo vamos mostrar como ficaram a parte de Listagem, Edição e Detalhe do nosso CRUD dinâmico.

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

Bloco de código
languagejs
titleapp-routing.module.ts
linenumberstrue
collapsetrue
import { NgModule } from '@angular/core';
import { RoutesRouterModule, RouterModuleRoutes } from '@angular/router';

import { IdiomaDynamicComponentIdiomaDetailComponent } from './idioma/detail/idioma-dynamicdetail.component';

constimport routes:{ RoutesIdiomaEditComponent = [
 
  {
    path: 'idioma} from './idioma/edit/idioma-edit.component';
import { IdiomaListComponent } from './idioma/list/idioma-list.component';

const routes: Routes = [
  { path: 'idiomas/create', component: IdiomaEditComponent 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: 'idiomas/edit/:id', component: IdiomaEditComponent },
  { path: 'idiomas/detail/:id', component: IdiomaDetailComponent },
  { path: 'idiomas', component: IdiomaListComponent },
  { path: '', redirectTo: '/idiomaidiomas', pathMatch: 'full' },
  {
    path: '**', component: IdiomaDynamicComponent
 IdiomaListComponent }

];

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

...

Listagem:

...

É a tela inicial da nossa aplicação e mostra a lista de dados da tabela Idioma, onde foram adicionados através de customização três campos da tabela usuar_mestre. Esta tela dará acesso às outras funcionalidades como edição e detalhamento.

Bloco de código
languagexml
titleListagem - idioma-list.componente

...

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-loading-overlay
  [hidden]="!showLoading">
</po-loading-overlay>

<po-page-dynamic-detailtable
  p-auto-router
  [p-title]="IdiomascTitle"
  [p-service-apiactions]="actions"
  [p-breadcrumb]="serviceApibreadcrumb">
  </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".

[p-fields]="fields"
  [p-service-api]="serviceApi">
</po-page-dynamic-table>
Bloco de código
languagejs
titleListagem - idioma-lista.component.ts
linenumberstrue
import { Component, OnInit } from '@angular/core';
import { PoMenuItemRouter } from '@po-ui/ng-components@angular/router';
import { PoBreadcrumb } from '@po-ui/ng-components';
import { PoPageDynamicTableActions PoPageDynamicDetailActions} from '@po-ui/ng-templates';

import { IdiomaService } from './../resources/idioma.service';

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

export class IdiomaListComponent implements IdiomaDynamicComponentOnInit {
  // Definicao das variaveis utilizadas
  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' }
    ]
  };
}
cTitle = 'Manutenção de Idiomas';
  public serviceApi: string;
  public fields: Array<any> = [];
  public showLoading = false;

  public readonly actions: PoPageDynamicTableActions = {
    new: '/idiomas/create',
    detail: '/idiomas/detail/:id',
    edit: '/idiomas/edit/:id',
    remove: true,
    removeAll: true
  };

  public readonly breadcrumb: PoBreadcrumb = {
    items: [
      { label: 'Home', link: '/' },
      { label: 'Idiomas'}
    ]
  };

  // Construtor da classe
  constructor(
    private service: IdiomaService,
    private route: Router
  ) { }

  // Load do componente
  public ngOnInit(): void {
    this.fields = [];
    this.serviceApi = this.service.getUrl();
    this.showLoading = true;
    this.service.getMetadata().subscribe(resp => {
      this.fields = resp['items'];
      this.service.setFieldList(this.fields);
      this.showLoading = false;
    });
  }
}


Edição: 

Esta tela permite a inclusão de um novo registro na tabela Idioma e também a alteração de registros já existentes.

Bloco de código
languagexml
titleEdição: idioma-edit.component.html
linenumberstrue
<po-loading-overlay
  [hidden]="!showLoading">
</po-loading-overlay>

<po-page-edit
  [p-title]="cTitle"
  [p-breadcrumb]="breadcrumb"
  [p-disable-submit]="formEdit.form.invalid"
  (p-cancel)="cancelClick()"
  (p-save)="saveClick()">
  <po-dynamic-form
    #formEdit
    p-auto-focus="string"
    [p-fields]="fields"
    [p-value]="record">
  </po-dynamic-form>
</po-page-edit>
Bloco de código
languagejs
titleEdição - idioma-edit.component.ts
linenumberstrue
import { Component, OnInit, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { PoBreadcrumb, PoDialogService, PoNotificationService } from '@po-ui/ng-components';

import { IdiomaService } from './../resources/idioma.service';

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

export class IdiomaEditComponent implements OnInit {
  // Define as variaveis a serem utilizadas
  public cTitle: string;
  public currentId: string;
  public record = {};
  public fields: Array<any> = [];
  public isUpdate = false;
  public showLoading = false;

  public breadcrumb: PoBreadcrumb;

  // Obtem a referencia do componente HTML
  @ViewChild('formEdit', { static: true })
  formEdit: NgForm;

  // Construtor da classe com os servicos necessarios
  constructor(
    private service: IdiomaService,
    private activatedRoute: ActivatedRoute,
    private route: Router,
    private poDialog: PoDialogService,
    private poNotification: PoNotificationService
  ) { }

  // Load do componente
  public ngOnInit(): void {
    this.isUpdate = false;
    this.showLoading = true;

    // Carrega o registro pelo ID
    this.activatedRoute.params.subscribe(pars => {
      this.currentId = pars['id'];

      // Se nao tiver o ID definido sera um CREATE
      if (this.currentId === undefined) {
        this.isUpdate = false;
        this.cTitle = 'Inclusão de Idioma';
      } else {
        this.isUpdate = true;
        this.cTitle = 'Alteração de Idioma';
      }

      // Atualiza o breadcrumb de acordo com o tipo de edicao
      this.breadcrumb = {
        items: [
          { label: 'Home', action: this.beforeRedirect.bind(this) },
          { label: 'Idiomas', action: this.beforeRedirect.bind(this) },
          { label: this.cTitle }
        ]
      };

      // Se for uma alteracao, busca o registro a ser alterado
      if (this.isUpdate) {
        this.service.getById(this.currentId).subscribe(resp => {
          Object.keys(resp).forEach((key) => this.record[key] = resp[key]);

          // Em alteracao temos que receber o registro para depois buscar a lista de campos
          this.getMetadata();
        });
      } else {
        // Se for create, pega a lista de campos
        this.getMetadata();
      }
    });
  }

  // Retorna a lista de campos
  private getMetadata() {
    let fieldList: Array<any> = [];

    // Carrega a lista de campos, trabalhando com um cache da lista de campos
    fieldList = this.service.getFieldList(this.isUpdate);
    if (fieldList === null || fieldList.length === 0) {
      this.service.getMetadata().subscribe(resp => {
        this.service.setFieldList(resp['items']);
        this.fields = this.service.getFieldList(this.isUpdate);
        this.showLoading = false;
      });
    } else {
      this.fields = fieldList;
      this.showLoading = false;
    }
  }

  // Redireciona via breadcrumb
  private beforeRedirect(itemBreadcrumbLabel) {
    if (this.formEdit.valid) {
      this.route.navigate(['/']);
    } else {
      this.poDialog.confirm({
        title: `Confirma o redirecionamento para ${itemBreadcrumbLabel}`,
        message: `Existem dados que não foram salvos ainda. Você tem certeza que quer sair ?`,
        confirm: () => this.route.navigate(['/'])
      });
    }
  }

  // Grava o registro quando clicado no botao Salvar
  public saveClick(): void {
    this.showLoading = true;
    if (this.isUpdate) {
      // Altera um registro ja existente
      this.service.update(this.currentId, this.record).subscribe(resp => {
        this.poNotification.success('Idioma alterado com sucesso');
        this.showLoading = false;
        this.route.navigate(['/idiomas']);
      });
    } else {
      // Cria um registro novo
      this.service.create(this.record).subscribe(resp => {
        this.poNotification.success('Idioma criado com sucesso');
        this.showLoading = false;
        this.route.navigate(['/idiomas']);
      });
    }
  }

  // Cancela a edicao e redireciona ao clicar no botao Cancelar
  public cancelClick(): void {
    this.poDialog.confirm({
      title: 'Confirma cancelamento',
      message: 'Existem dados que não foram salvos ainda. Você tem certeza que quer cancelar ?',
      confirm: () => this.route.navigate(['/'])
    });
  }
}


Detalhe:

Esta tela apresenta os detalhes de um registro de Idioma, com suas customizações.


Bloco de código
languagexml
titleDetalhe: idioma-detail.component.html
linenumberstrue
<po-loading-overlay
  [hidden]="!showLoading">
</po-loading-overlay>

<po-page-detail
  [p-title]="cTitle"
  [p-breadcrumb]="breadcrumb"
  (p-edit)="editClick()"
  (p-back)="goBackClick()">
  <po-dynamic-view 
    [p-fields]="fields"
    [p-value]="record">
  </po-dynamic-view>
</po-page-detail>
Bloco de código
languagejs
titleDetalhe: idioma-detail.component.ts
linenumberstrue
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { PoBreadcrumb } from '@po-ui/ng-components';

import { IdiomaService } from './../resources/idioma.service';

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

export class IdiomaDetailComponent implements OnInit {
  // definicao das variaveis utilizadas
  public cTitle = 'Detalhe do Idioma';
  public currentId: string;
  public fields: Array<any> = [];
  public record = {};
  public showLoading = false;

  public readonly breadcrumb: PoBreadcrumb = { items: [
      { label: 'Home', link: '/' },
      { label: 'Idiomas', link: '/idiomas' },
      { label: 'Detail' } ]
  };

  // construtor com os servicos necessarios
  constructor(
    private service: IdiomaService,
    private activatedRoute: ActivatedRoute,
    private route: Router
  ) { }

  // load do componente
  public ngOnInit(): void {
    this.activatedRoute.params.subscribe(pars => {
      this.showLoading = true;

      // carrega o registro pelo ID
      this.currentId = pars['id'];
      this.service.getById(this.currentId).subscribe(resp => {
        Object.keys(resp).forEach((key) => this.record[key] = resp[key]);

        // carrega a lista de campos somente apos receber o registro a ser apresentado
        this.fields = this.service.getFieldList(false);
        if (this.fields === null || this.fields.length === 0) {
          this.service.getMetadata().subscribe(data => {
            this.fields = data['items'];
            this.service.setFieldList(this.fields);
            this.showLoading = false;
          });
        }
        this.showLoading = false;
      });
    });
  }

  // Redireciona quando clicar no botao Edit
  public editClick(): void {
    this.route.navigate(['/idiomas', 'edit', this.currentId]);
  }

  // Redireciona quando clicar no botao Voltar
  public goBackClick(): void {
    this.route.navigate(['/idiomas']);
  }
}



Abaixo temos em anexo um Projeto CRUD frontend em PO-UI e a sua parte backend em Progress:

View file
namedatasul-dynamic.zip
height250
View file
namecustomizacao_api_rest-backend-progress.zip
height250

...

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.

getIdFieldRetorna um campo do tipo ID para ser adicionado na lista de campos do Metadata. Este campo serve como chave do registro nos tratamentos de CRUD na parte HTML.

Assinatura:

getIdField()

Exemplo:

oIdiomas:add( JsonAPIUtils:getIdField() ).


07. Links Úteis

Documentação API Datasul:

...