Histórico da Página
...
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||
---|---|---|---|---|
|
...
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 | ||||||
---|---|---|---|---|---|---|
| ||||||
<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 | ||||||
---|---|---|---|---|---|---|
| ||||||
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 | ||||||
---|---|---|---|---|---|---|
| ||||||
<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 | ||||||
---|---|---|---|---|---|---|
| ||||||
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 | ||||||
---|---|---|---|---|---|---|
| ||||||
<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 | ||||||
---|---|---|---|---|---|---|
| ||||||
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 | ||||
---|---|---|---|---|
|
View file | ||||
---|---|---|---|---|
|
...
Método | Descrição | Assinatura/Exemplo |
---|---|---|
convertAblTypeToHtmlType | Converte 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. |
convertToCamelCase | Converter 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. |
getIdField | Retorna 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:
...