PO UI — Como utilizar componentes de formulário e consumir serviços Rest

Ricardo Mansano Godoi
TOTVS Developers
Published in
10 min readNov 4, 2019

O PO-UI é a biblioteca de componentes open source da TOTVS, reconhecendo a importância do desenvolvimento colaborativo para evolução de suas ferramentas.

Neste post vou demonstrar principalmente como utilizar os componentes de formulário, e também como consumir serviços Rest mas tem muito mais informação sobre o PO UI em seu site, recomendo que acessem para conhecer melhor a biblioteca.

Link para o vídeo: https://youtu.be/v-tas7P5n9s

Hoje o PO UI é compatível com o Angular, porém sua interface (CSS) foi escrita de maneira modular, sendo possível estendê-la a outros frames, como o React e o Vue.

Momento Jabá:
Acesse developers.totvs.com pra ficar antenado às mais diversas tecnologias utilizadas na TOTVS, e também nossas vagas, meetups, vídeos, podcasts e muito material bacana compartilhado por colegas e parceiros.

Criando um projeto do zero

Todos os passos para iniciar um novo projeto estão descritos no link abaixo:

https://po-ui.io/guides/getting-started

Pré-requisitos para utilizar o exemplo:

# Clone o projeto
git clone https://github.com/ricardomansano/NiverPortinariAngular.git
# Acesse o diretório
cd NiverPortinariAngular
# Instale as dependências
npm install
# Inicie o projeto
ng serve --open

Componentes

Vamos começar falando um pouco sobre os componentes de formulário.

Importante: Quem acompanhou a série Angular e React pra quem vem do AdvPL poderá se guiar pelas mesmas Tags, como a [*Form no trecho abaixo:

<! — [*Form]
<form #niverForm=”ngForm”>
<div class=”po-row”>
<po-input…

<po-input>

https://po-ui.io/documentation/po-input

#####################
# nivers.component.ts
#####################
# Variável f_person que será usada no model do componenteexport class NiversComponent implements OnInit {
nivers = Array(); // Array vazio de aniversarios
f_person: string = '';
# No submit inserimos o item preenchido no array nivers e limpamos seu valor para novo preenchimento// [*Submit]
onClickSubmit(form){
this.nivers.push({ list: [this.f_birthday, this.f_person] } )
form.reset()
this.f_person = ''
}
# A função validForm será usada para habilitar o botão inserir, verificando se f_person foi preenchido, falaremos mais sobre issovalidForm(){
return (this.f_person !== '')
}
#######################
# nivers.component.html
#######################
# Na criação do componente no arquivo HTML usamos a variável f_person como ngModel e name.<!-- [*Form] -->
<form #niverForm="ngForm">
<div class="po-row">
<po-input
class="po-md-5" # Preenche 5 de 12 espaços
name="f_person" # Nome do componente
[(ngModel)]="f_person" # Model
p-icon="po-icon-user" # Icone ao lado esquerdo
p-clean # Habilita a limpeza do campo com o X
p-require="true" # Requerido
p-label="Person(po-input)" # Descrição
p-maxlength="10" # Tamanho máximo
(p-enter)="personEnter('Focus in person field')" # Função disparada ao entrar no campo
></po-input>

Layout do formulário

Aproveitando que falamos sobre formulários, vou abrir um parenteses pra explicar como funciona o layout, o form abaixo foi criado com o trecho HTML explicado na sequência.

Ao criar o formulário, terá 12 posições para cada linha. Assim, usando a classe po-md-1, preencherá uma dessas posições, po-md-2 preencherá duas, e assim por diante…

O <div class=”po-row”> fará a inserção de uma nova linha no formulário.

<form>
<div class="po-row">
<po-input class="po-md-1"></po-input>
<po-input class="po-md-2"></po-input>
<po-input class="po-md-3"></po-input>
<po-input class="po-md-6"></po-input>
</div>
<div class="po-row">
<po-input class="po-md-6"></po-input>
<po-input class="po-md-1"></po-input>
<po-input class="po-md-2"></po-input>
<po-input class="po-md-3"></po-input>
</div>
</form>

<po-datepicker>

https://po-ui.io/documentation/po-datepicker

O componente po-datepicker, como os demais apresentados, devem ter seu Model (aqui o f_birthday) declarados no arquivo nivers.component.ts. Desta forma, seus valores podem ser facilmente acessados.

#######################
# nivers.component.html
#######################
<po-datepicker
class="po-md-2"
name="f_birthday"
[(ngModel)]="f_birthday"
p-clean
p-label="Niver(po-datepicker)"
></po-datepicker>
#######################
# nivers.component.ts
#######################
# A data de aniversário também ira compor o array nivers.// [*Submit]
onClickSubmit(form){
this.nivers.push({ list: [this.f_birthday, this.f_person] } )

<po-switch>

https://po-ui.io/documentation/po-switch

<po-switch
class="po-md-1"
name="f_switch"
[(ngModel)]="f_switch"
p-label="po-switch"
p-label-on=" " # Texto exibido quando estiver ON
p-label-off=" " # Texto exibido quando estiver OFF
></po-switch>

<po-combo>

https://po-ui.io/documentation/po-combo

O componente po-combo permite a digitação para facilitar a busca de um item.

Os itens são preenchidos à partir da variável selectOptions, que é um array de itens do tipo PoSelectOption, definindo a descrição e valor de cada item.

#######################
# nivers.component.ts
#######################
// Itens do po-select
public readonly selectOptions: Array<PoSelectOption> = [
{ value: 'vermelho', label: 'vermelho' },
{ value: 'azul', label: 'azul' },
{ value: 'amarelo', label: 'amarelo' }
];
#######################
# nivers.component.html
#######################
<po-combo
class="po-lg-2"
name="f_combo"
[(ngModel)]="f_combo"
p-clean
p-label="po-combo"
[p-options]="selectOptions"
></po-combo>

<po-select>

https://po-ui.io/documentation/po-select

Já o po-select não permite a digitação, apenas a seleção do item. Nele, é possível utilizar o mesmo array criado para o componente po-combo.

<po-select
class="po-lg-2"
name="f_select"
[(ngModel)]="f_select"
p-clean
p-label="po-select"
[p-options]="selectOptions"
></po-select>

<po-password>

https://po-ui.io/documentation/po-password

<po-password
class="po-lg-2"
name="f_password"
[(ngModel)]="f_password"
p-label="po-password"
p-clean
></po-password>

<po-number>

https://po-ui.io/documentation/po-number

Como o nome já diz, este componente permite apenas números.

<po-number
class="po-lg-2"
name="f_number"
[(ngModel)]="f_number"
p-label="po-number"
p-clean
></po-number>

<po-email> e <po-url>

https://po-ui.io/documentation/po-email
https://po-ui.io/documentation/po-url

Este dois componentes permitem a inclusão de E-mail e URL, fazendo automaticamente as validações. Veja que o e-mail está informando o preenchimento incorreto.

<po-email
class="po-lg-4"
name="f_email"
[(ngModel)]="f_email"
p-label="po-email"
p-clean
></po-email>
<po-url
class="po-lg-4"
name="f_website"
[(ngModel)]="f_website"
p-label="po-url"
p-clean
></po-url>

<po-upload>

https://po-ui.io/documentation/po-upload

O po-upload permite a seleção de um ou mais arquivos para upload.

<po-upload
class="po-lg-2"
name="f_upload"
[(ngModel)]="f_upload"
p-label="po-upload"
p-clean
p-url="https://thf.totvs.com.br/sample/api/uploads/addFile" # URL que deve ser feita a requisição com os arquivos selecionados.
></po-upload>

<po-radio-group> e <po-checkbox-group>

https://po-ui.io/documentation/po-radio-group
https://po-ui.io/documentation/po-checkbox-group

Os itens do radio e do checkbox são preenchidos respectivamente à partir das variáveis:

  • radioOptions: array do tipo PoRadioGroupOption;
  • checkboxOptions: array do tipo PoCheckboxGroupOption.
#######################
# nivers.component.ts
#######################
// Itens do po-radio-group
public readonly radioOptions: Array<PoRadioGroupOption> = [
{ label: 'Opção 1', value: 1 },
{ label: 'Opção 2', value: 2 },
{ label: 'Opção 3', value: 3 }
];
// Itens do po-checkbox-group
public readonly checkboxOptions: Array<PoCheckboxGroupOption> = [
{ value: '1', label: 'Opção 1' },
{ value: '2', label: 'Opção 2' },
{ value: '3', label: 'Opção 3' }
];
#######################
# nivers.component.html
#######################
<po-radio-group
class="po-md-5"
name="f_radio"
[(ngModel)]="f_radio"
p-columns="4"
p-label="po-radio-group"
[p-options]="radioOptions" # Itens
></po-radio-group>
<po-checkbox-group
class="po-md-5"
name="f_checkbox"
[(ngModel)]="f_checkbox"
p-columns="4"
p-label="po-checkbox-group"
[p-options]="checkboxOptions" # Itens
></po-checkbox-group>

<po-multiselect>

https://po-ui.io/documentation/po-multiselect

O po-multiselect permite a seleção de múltiplos itens, bem como a fácil visualização dos selecionados. Ele recebe um array do tipo PoMultiselectOption

#######################
# nivers.component.ts
#######################
// Item do po-multiselect
public multiselectOptions: Array<PoMultiselectOption> = [
{ value: '1', label: 'vermelho' },
{ value: '2', label: 'azul' },
{ value: '3', label: 'amarelo' },
{ value: '4', label: 'verde' },
{ value: '5', label: 'laranja' },
];
#######################
# nivers.component.html
#######################
<po-multiselect
class="po-md-6"
name="f_multiselect"
[(ngModel)]="f_multiselect"
p-label="po-multiselect"
[p-options]="multiselectOptions" # Itens
></po-multiselect>

<po-textarea>

https://po-ui.io/documentation/po-textarea

<po-textarea
class="po-md-6"
name="f_textarea"
[(ngModel)]="f_textarea"
p-label="po-textarea"
></po-textarea>

<po-button>

https://po-ui.io/documentation/po-button

No componente, po-button define a ação de clique através do p-click, e a condição para habilitar o botão, através do p-disabled, instanciando recursivamente a função validForm.

Desta forma, o botão sera habilitado somente quando estiver preenchido o campo que atribui valor à variável this.f_person.

#######################
# nivers.component.ts
#######################
// [*Submit]
onClickSubmit(form){
this.nivers.push({ list: [this.f_birthday, this.f_person] } )
form.reset()
this.f_person = ''
}
// [*Form: Essa funcao vai ser responsável por
// habilitar o botão submit quando nome for preenchido]
validForm(){
return (this.f_person !== '')
}
#######################
# nivers.component.html
#######################
<po-button
class="po-md-2"
p-label="INSERIR"
p-type="primary"
(p-click)="onClickSubmit(niverForm)"
[p-disabled]="!validForm()"
></po-button>
Aqui o formulário preenchido, com um item já inserido, localizado no rodapé.

O serviço Rest

Aqui vou partir do principio que você já conhece o Rest e seus verbos…

O consumo de serviços Rest independe do PO UI, é um “trabalho” pro Angular. Criei um back-end muito simples em JavaScript utilizando o Express, com:

  • Um POST;
  • Dois GET, o primeiro para listar todos os itens e o segundo apenas do ID desejado;
  • E um DELETE;
  • Deixei o PUT e o PATCH para uma outra oportunidade.

O serviço pode ser iniciado através do arquivo sample_ret/index.js, mas não se esqueça de instalar as dependências:

# A partir da raiz do seu projeto 
cd sample_rest
# Instale as dependências (uma unica vez)
npm install
# Inicie o serviço
node index.js

Quando você consome um serviço, ele precisa ser declarado como confiável no back-end. No caso do NodeJS, fazemos isso declarando o endereço de origem (endereço + porta do projeto Angular que irá consumir o Rest) no setHeader ‘Access-Control-Allow-Origin’.

Aproveitamos e definimos também os verbos que iremos habilitar através do setHeader ‘Access-Control-Allow-Methods’.

// [*Rest: Para habilita o CORS, evitando o erro: 
// has been blocked by CORS policy: No 'Access-Control-Allow-Origin'
app.use(function (req, res, next) { // Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin',
'http://localhost:4200');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods',
'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers',
'X-Requested-With,content-type');
// Set to true if you need the website to include
// cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials',
true);
// Pass to next layer of middleware
next();
});
./sample_rest/index.js

Consumindo o Rest

Na arquitetura do Angular todo acesso a dados é delegado a um Service.

Para criar um component no Angular você utiliza o comando:
ng generate component component_name

E para criar um service:
ng generate service service_name

Importante: Ao criar um service, terá que inseri-lo manualmente no arquivo app.module.ts, exemplo:

# Crie o service, exemplo: newservice
ng generate service services/newservice

# No arquivo app.module.ts , importe o novo service
import { NewserviceService } from ‘./services/newservice.service’;

# Insira sua chamada no providers, ainda no arquivo app.module.ts
providers: [ CustomerService, NewserviceService ],

Em nosso exemplo, iremos utilizar um service para consumir o Rest.

Abaixo descrevo o fluxo para o verbo POST, inserindo um novo registro:

  1. A função newCustomer no arquivo customer.component.ts é instanciada à partir do botão Novo da camada HTML;
  2. Que instancia a função de mesmo nome no arquivo customer.service.ts, executando um POST a partir do método this.http.post, passando um array com ID e Name como parâmetros;
  3. O Rest (sample_rest/index.js), insere um novo item no JSon datae retorna o statusOK.
  4. Ao final deste processamento, a função definida no método subscribe , no arquivo customer.component.ts , é disparada, no nosso exemplo, executando a função getCustomers, que irá exibir os itens contidos no JSon data.

Os outros verbos utilizados no exemplo estão descritos abaixo:

Executando o exemplo

Como utilizar o tema da TOTVS no PO UI?

O PO UI tem sua própria identidade visual, mas você pode aplicar o tema no padrão da TOTVS, da seguinte maneira:

1. Faça uma cópia do arquivo angular.json por segurança (na raiz do projeto);2. Instale o tema:
npm i --save @totvs/po-theme
3. Atualize o arquivo angular.json para utilizar o novo tema
“styles”: [
"node_modules/@totvs/po-theme/css/po-theme-default.min.css"
],
4. Reinicie o serviço do Angular:
ng serve --open
Tema padrão da TOTVS

Como sempre, espero que aproveitem o material, até a próxima…

--

--

Ricardo Mansano Godoi
TOTVS Developers

Chief engineer of Front-end and Development Tools on TOTVS, developing software since 88, plugged to the brand new technologies and Nerd to the bone.