Desenvolvendo no Web Framework

Este manual possui o objetivo de ensinar o desenvolvimento de aplicações utilizando o Web Framework da Plataforma Nginstack. Nele serão apresentados conceitos e as funcionalidades básicas do Framework. Recursos avançados serão explicados em outras documentações.

O ambiente do usuário

Antes de começarmos a explicar como desenvolver no Web Framework, é importante conhecermos o ambiente do Web Framework, onde as telas e relatórios criados serão apresentados ao usuário final do sistema.

O ambiente pode ser acessado da mesma forma que um site da internet. Para isto, precisaremos saber apenas qual o endereço do servidor Engine que iremos acessar. Por exemplo: um Engine instalado no próprio computador, poderá estar acessível no endereço http://127.0.0.1.

Se o usuário desejar, e o servidor possuir os certificados de segurança instalados, o ambiente pode ser acessado através de uma conexão segura utilizando o prefixo https:// ao invés do http://.

Ao acessar o endereço do servidor em um navegador Web, será apresentada uma interface de login do sistema. Você deverá ter um usuário no sistema criado pelo administrador. Após o login, você será redirecionado para o ambiente do Web Framework.

Alguns conceitos importantes do Web Framework:

  • Menu do ambiente: sempre será exibido e sua configuração padrão exibe as opções a seguir:

    1. Menu principal (configurável): exibe uma árvore com todos os processos e relatórios visíveis para o usuário, permitindo ao mesmo iniciar os mesmos.
    2. Imprimir: imprime as informações exibidas pelo processo selecionado. Este botão de impressão deve ser utilizado ao invés do nativo do navegador Web, pois o do navegador irá imprimir todo o ambiente do Web Framework, incluindo o meu do ambiente e barra de processos, o que normalmente não é desejado.
    3. Pendências: exibe a quantidade pendências do usuário conectado. Esta informação é obtida do módulo Tarefas, que pode ser utilizado pelo desenvolvedor, usufruindo do conceito de tarefas pendentes e de fluxo de atividades. Este é um recurso muito poderoso, cuja documentação pode ser obtida diretamente no módulo de Tarefas do Sistema.
    4. Ajuda: exibe a ajuda do objeto que está em foco. Caso nenhum objeto esteja em foco, será exibida a ajuda do processo. É importante que desenvolvedor sempre documente o processo através da propriedade “help” dos componentes visuais utilizados.
    5. Buscar: realiza uma pesquisa em todo o sistema.
    6. Fechar a sessão: finaliza a sessão do usuário e retorna para a tela de login.
  • Barra de processos: onde são exibidos todos os processos abertos pelo usuário, sendo destacado o processo em uso no momento.

  • Barra de botões: local onde são exibidos os botões definidos pelo processo. Os dois primeiros botões são fixos e não podem ser removidos pelo desenvolvedor. Eles são utilizados para retroceder ou avançar o histórico de interações com o usuário.

  • Área de trabalho do processo: logo abaixo da barra de botões, podemos observar a área onde é exibido o conteúdo escrito pelo processo. Na imagem acima, podemos observar que foi escrita apenas uma grade de variáveis.

Durante o desenvolvimento de um processo, o desenvolvedor é responsável apenas pela configuração da barra de botões e o conteúdo da área de trabalho do processo. Todo o restante do ambiente é automaticamente controlado pelo Web Framework.

Para criar um processo, o desenvolvedor deve criar um arquivo com extensão “.ip” dentro de um diretório da Virtual File System (VFS), o sistema de arquivos virtual do sistema.

Agora que conhecemos o IDE, podemos criar nosso primeiro processo.

Processos

No Web Framework o desenvolvedor cria interfaces com os usuários através de processos. Um processo no Web Framework é uma aplicação executada dentro do ambiente do sistema, em um contexto isolado e seguindo uma API que garanta a padronização visual do sistema.

Um processo do Web Framework é desenvolvido com a construção de interações com o usuário ou atividades. Cada interação com o usuário é responsável por:

  • Obter ou alterar dados;
  • Definir ou alterar botões da barra de botões;
  • Escrever o conteúdo a ser exibido na área de trabalho do processo.

Uma atividade é semelhante a uma interação, com a diferença de que, ao final da sua execução, o conteúdo da área de trabalho do processo não é alterado. Seu uso é indicado para processamentos que não necessitem de interação com o usuário, ou para alterar propriedades dos componentes visuais escritos pela última interação.

Ao selecionar um processo no menu principal, a primeira interação ou atividade declarada no processo será executada. No final da execução de uma interação, a interface é escrita e o sistema passa a aguardar a próxima ação do usuário. A partir do acionamento dos botões e atalhos disponibilizados para o usuário, outras interações e atividades poderão ser executadas. Ao definir os botões e atalhos visíveis, uma interação também define quais são as próximas interações e atividades disponíveis para o usuário, criando implicitamente um mapa de navegação do processo.

Ao contrário de uma interação, uma atividade não gera uma interface para o usuário, portanto após a sua execução o Web Framework executa automaticamente a próxima interação ou atividade. Poderão ser executadas várias atividades até que por fim seja executada uma interação, definindo uma nova interface e devolvendo o controle para o usuário.

A seguir iremos apresentar exemplos que tornarão mais claros os conceitos de interações, atividades e do fluxo de navegação do usuário.

Criando um processo

Um processo no Web Framework é um arquivo na VFS com a extensão “.ip” (application/x-process). Iremos agora criar um processo de testes. Para isto, abra o seu diretório de testes no IDE e crie um arquivo chamado “Hello World.ip” com o conteúdo abaixo:

this.help = "Explicação para o usuário final do funcionamento e objetivo do processo.";
this.interaction("main", function () {
  var label = this.label("Hello World!");
  label.write();
});

Clique no botão Browser do IDE, realize o login e abra o processo recém criado no módulo de Testes. Se todos os passos foram seguidos, você deverá ver o texto “Hello World!” escrito na tela. Observe que o processo não possui nenhum botão definido além dos botões padrões de Voltar e Avançar, que estarão desabilitados. Pressione a tecla Alt + F1 e será aberta uma janela exibindo uma ajuda para o processo. Fora isto, a única opção disponível será fechar o processo através do botão existente na aba do processo.

Parabéns! Você acabou de criar o seu primeiro processo no Web Framework. Ele ainda não tem grandes utilidades, mas iremos evoluí-lo durante o restante deste manual. Voltemos ao código do processo.

O primeiro ponto a ser compreendido é que “this” neste código é o processo que está sendo desenvolvido. Ele é um objeto que herda as propriedades de Process. Todos os métodos e propriedades documentados na API de Process são válidos para this. Com base nesta informação, iremos analisar o código.

Na primeira linha, informamos a ajuda do processo através da propriedade “help”. Esta ajuda é acessível pelo usuário através do botão Ajuda ou do atalho de teclado ALT + F1. O texto informado deve ser simples e objetivo, explicando a finalidade do processo em uma linguagem compreensível para o usuário final do processo.

Na segunda linha, temos a declaração de uma interação do processo através da execução do método Process.interaction. Ele está recebendo dois parâmetros: o nome da interação que está sendo criada e uma função que será invocada quando a interação for exibida para o usuário. Esta função será responsável por gerar o conteúdo a ser exibido na área de trabalho do processo e é invocada como se fosse um método do processo, portanto “this” dentro desta função também será o processo.

A função da interação executa o método Process.label, retornando um objeto Label com a finalidade de escrever textos na interface. Em sua forma mais simples de uso, o primeiro parâmetro é o texto a ser exibido e a aparência é definida apenas pelo tema do usuário. Em seguida é executado o método Label.write, indicando que ele deve ser escrito na interface do usuário.

Diálogos e fluxo de navegação

Nosso próximo processo será um divisor de números. Para realizarmos a divisão precisaremos que o usuário nos informe o valor do dividendo e do divisor, um momento oportuno para conhecermos os diálogos.

Crie um processo chamado “Divisor.ip” com o conteúdo abaixo:

includeOnce -1898142280 /* FormDialog.ijs */

this.help = "Este processo divide números.";

this.interaction("Iniciar", function () {
  this.visibleButtons = ["Dividir"];
  var label = this.label("Pressione o botão acima.");
  label.write();
});

this.activity("Dividir", function () {
  var dialog = new FormDialog(this);
  dialog.title = "Informe os parâmetros da divisão";

  var fld = dialog.addField("dividendo", "number");
  fld.label = "Dividendo";
  fld.help = "Informe o dividendo da divisão.";

  var fld = dialog.addField("divisor", "number");
  fld.label = "Divisor";
  fld.help = "Informe o divisor da divisão.";

  if (dialog.show()) {
    var dividendo = dialog.field("dividendo").value;
    var divisor = dialog.field("divisor").value;
    if (divisor !== 0){
      this.resultado = "Resultado: " + (dividendo/ divisor) + ".";
    } else {
      this.resultado = "Divisão por zero não é permitida.";
    }
  } else {
    this.resultado = "Operação cancelada pelo usuário.";
  }
});

this.interaction("Exibir resultado", function () {
  this.visibleButtons = [];
  var label = this.label(this.resultado);
  label.write();
});

Abra o seu processo no Web Framework e observe que este processo possui um botão chamado “Dividir”. Pressione este botão para exibir um diálogo, informando 2 números e confirmando no botão Ok. O resultado da divisão será exibido no corpo do processo. Podemos pressionar o botão voltar para retornar à tela inicial.

Agora vamos interpretar o código fonte do exemplo.

Na primeira linha observamos um includeOnce seguido de uma chave e um comentário com a URL de um arquivo. O includeOnce é uma extensão do JavaScript realizada pela Nginstack e tem finalidade semelhante ao “import” de outras linguagens, como o Java. Ele inclui o fonte do arquivo informado, caso ele nunca tenha sido incluído antes. Utilizamos o includeOnce para incluir bibliotecas utilizadas pelo processo, no caso o FormDialog. Os comandos includeOnce sempre deve ser executados no início do código.

Na interação “Iniciar” definimos um botão visível para o usuário através da propriedade Process.visibleButtons. Observe que informamos o nome da atividade declarada logo abaixo. Esta forma de uso cria implicitamente uma instância de Button com os parâmetros “name” e “target” sendo o nome da interação ou atividade informada. Uma alternativa seria criar explicitamente um botão através do método Process.button e utilizar o nome do botão ao invés do nome da atividade. As duas formas são equivalentes, mas pela simplicidade, prefira a criação implícita de botões.

Na atividade “Dividir” criamos um formulário com dois campos e o exibimos através do método show. Sem concluir a execução da atividade, será exibido um diálogo para o usuário, que poderá ser confirmado ou cancelado. Se for confirmado, o show irá retornar true, caso contrário retornará false.

Tipos de campo suportados pelos Dialogs: string, memo, combo, radio, date e password. Os tipos combo e radio tem o funcionamento parecido, sendo suas opções aquelas declaradas em um array. Segue o exemplo de utilização de um campo radio (e, consequentemente, de um campo do tipo combo):

var ds = new DataSet();
ds.createField("optionField", "string");
ds.create();

var opt = [
  ["Opção 1", 0],
  ["Opção 2", 1],
  ["Opção 3", 2]
];

var grid = this.grid("dialogInsert", ds);
grid.onDefineFields.set(function (grid) {
  var fld = grid.field('optionField', 'combo');
  fld.label = "Opções";
  fld.help = "Preencha esse campo utilizando o botão 'Inserir Registro (formDialog)' ";
  fld.options = opt;
});

grid.onAfterPost.set(function (grid) {
  ds.next();
});

var button = grid.button("Inserir Registro (formDialog)", function (bt) {
  var gr = bt.parent;
  var pr = gr.process;
  var dialog =  new uwi.dialogs.Form(pr);

  var fld = dialog.addField('radioField', 'radio');
  fld.options = opt;

  if (dialog.show()) {
    var ds = gr.ds;

    ds.append();
    ds.setField('optionField', dialog.field('radioField').value);
    ds.post();
  }
});

É importante perceber que a grade não suporta campos do tipo radio. No código acima o campo declarado na grade é do tipo combo, que pode ser preenchido, de forma opcional, por um campo do tipo radio com origem em um formDialog.

Observe que apesar de uma atividade não poder escrever componentes de interface, como um Label, ela pode fazer uso dos diálogos, que são exibidos sobre a interface definida pela última interação.

O valor calculado pela atividade “Dividir” precisa ser preservado para ser exibido na próxima interação “Exibir resultado”. Por convenção, criamos propriedades no processo para armazenar dados que são utilizados por mais de uma interação ou atividade. No exemplo, criamos a propriedade “resultado”.

Logo após a execução da atividade “Dividir”, será executada a próxima interação “Exibir resultado”. Ela retira a visibilidade do botão “Dividir” e exibe o resultado do cálculo.

Na última interação o único botão disponível será o Voltar, definido automaticamente pelo processo. Ao ser pressionado a interação anterior “Iniciar” será executada. Podemos perceber que a atividade “Dividir” não foi executada, pois o diálogo não foi exibido. Ao retroceder para a interação “Iniciar”, o botão Avançar será habilitado automaticamente. Ao ser pressionado, a interação “Exibir resultado” será executada, novamente sem ocorrer a execução da atividade “Dividir”.

Conceitos importantes:

  • Apenas interações são re-executadas pelos botões Voltar e Avançar;
  • As interações não podem presumir que as atividades entre elas sempre serão executadas. Interações que necessitem desta garantia devem limpar o histórico de navegação através do método Process.clearHistory.

Grade de variáveis

O principal componente do Web Framework é a grade (Grid). Agora conheceremos a grade em sua versão mais simples, a grade de variáveis. Para exemplificar, segue a definição, em código, de uma grade com dois campos:

var variaveis = this.grid("variaveis");
variaveis.title = "Variáveis";
variaveis.onDefineFields.set( function (grid){
  var fld = grid.field("CPF", "string", 14);
  fld.label = "CPF do Cliente";
  fld.help = "Informe o CPF do cliente no formato 000.000.000-00.";

  fld = grid.field("ULTIMACOMPRA", "date");
  fld.label = "Data da Última Compra";
  fld.help = "Preencha esta variável caso a procura seja por data de última compra."
});

Como pudemos ver no exemplo acima, uma nova instância dessa classe não é feita da forma tradicional, ou seja, algo como: new Grid("variaveis"). Uma nova instância da classe Grid é feita através do método Process.grid(), tal qual foi feito no exemplo acima, onde o “this” é uma referência ao processo em que se está embutido.

Bem, mas esta foi apenas a criação de uma grade. Para que a grade seja visível, precisamos escrevê-la em uma interação. Segue um exemplo para escrever a grade definida logo acima:

this.interaction("Iniciar", function () {
  this.grid("variaveis").write();
});

Com isto, podemos agora pegar o exemplo do processo “Divisor.ip”, definido logo acima na seção “Diálogos e fluxo de navegação”, e adaptá-lo para que a entrada de dados seja feita através de uma grade, ao invés de se utilizar da classe FormDialog. Segue o exemplo completo:

this.help = "Este processo divide números.";

var variaveis = this.grid("variaveis");
variaveis.title = "Variáveis";
variaveis.onDefineFields.set( function (grid){
  var fld = grid.field("DIVIDENDO", "number");
  fld.label = "Dividendo";
  fld.required = true;
  fld.help = "Informe o dividendo da divisão.";

  fld = grid.field("DIVISOR", "number");
  fld.label = "Divisor";
  fld.required = true;
  fld.help = "Informe o divisor da divisão."
});

this.interaction("Iniciar", function () {
  this.visibleButtons = ["Dividir"];
  var label = this.label("Após preencher as variáveis abaixo, pressione o botão Dividir.");
  label.write();
  this.grid("variaveis").write();
});

this.activity("Dividir", function () {
  var grid = this.grid("variaveis");
  var dividendo = grid.field("dividendo").value;
  var divisor = grid.field("divisor").value;
  if (divisor !== 0){
    this.resultado = "Resultado: " + (dividendo/ divisor) + ".";
  } else {
    this.resultado = "Divisão por zero não é permitida.";
  }
});

this.interaction("Exibir resultado", function () {
  this.visibleButtons = [];
  var label = this.label(this.resultado);
  label.write();
});

Nos bastidores do Web Framework

Caso tenha alguma experiência anterior em JavaScript, você pode ter estranhado a forma como definimos um processo. Uma dúvida comum é: por que “this” não é o objeto global, como o “window” em navegadores Web?

Observe o código abaixo:

<script>
  var a = 10
  alert([a, this.a, window.a].join(","))
</script>

Em um navegador web, o script acima exibe o alerta “10,10,10”, pois a variável “this” será o objeto global, exceto quando estamos em uma função invocada como construtor ou como método. No navegador Web, window é o objeto global, portanto as 3 atribuições abaixo são equivalentes quando executadas no escopo global:

  • var a = 10
  • this.a = 10
  • window.a = 10

No caso da definição de um processo “this” não é o objeto global, pois o código não é executado diretamente pelo sistema. Ao solicitar a abertura de um processo, o Web Framework injeta o código fonte de um processo dentro de uma função que será utilizada como construtor do processo.

Portanto o código abaixo:

this.interaction("main", function (){
  var label = this.label("Hello World!")
  label.write()
});

É transformado em algo semelhante a:

var constructor = function HelloWorld(){
  this.interaction("main", function (){
    var label = this.label("Hello World!")
    label.write()
  });
}
constructor.prototype = new Process();

var process = new HelloWorld();

Observe acima que o código fonte de um processo na verdade é executado dentro da uma função invocada como um construtor. Neste caso, “this” é o objeto que está sendo construído e herda as propriedades definidas em seu protótipo Process.