PO UI — Como utilizar componentes de formulário e consumir serviços Rest
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:
- NPM/NodeJS
- Git
- Angular CLI ( npm install -g @angular/cli )
# 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>
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();});
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_nameE 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:
- A função newCustomer no arquivo customer.component.ts é instanciada à partir do botão
Novo
da camada HTML; - 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;
- O Rest (sample_rest/index.js), insere um novo item no JSon
data
e retorna o statusOK
. - 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:
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-theme3. 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
Como sempre, espero que aproveitem o material, até a próxima…