Home

Pablo Santiago Sanchez

WirePhrame is a PHP framework based on JsWebGets for GUI, POP for Objects, COSA for WebService implementation, and RePHPort for reports generation.


Project Members:

'''ATENÇÃO''' Estão pendentes no documento, os seguintes itens:

* 5 Como se dá a comunicação e construção do sistema - Falta Figura
* 17 Campos editáveis em listview com callback para onchange
* 18 Trabalhando com Treeviews
* 20 Enviando Arquivos ao Servidor

A partir de 2008, os sistemas da ENAP passam a ter uma estrutura organizacional mais coesa e focada no desenvolvimento com o paradigma [http://en.wikipedia.org/wiki/Service-oriented_architecture SOA - Services Oriented Applications] - com o uso de [http://en.wikipedia.org/wiki/Role-based_access_control RBAC - Role-Based Access Control] - administrado via [[WebCentral]].

Para atingir estes objetivos, uma estrutura foi criada para sistemas permitindo a reutilização de código no controle de fluxo da aplicação através do consumo de WebServices XML com a utilização do padrão [[COSA]], o compartilhamento das interfaces entre aplicações e prototipação acelerada com a ferramenta [[JsDesigner]], e aceleração e redução de erros na interação com o banco de dados através da biblioteca de persistência [[POP]].

Esta estrutura implementa o Padrão de Projetos [http://en.wikipedia.org/wiki/Model-view-controller MVC] de maneira bastante modularizada, permitindo separar a lógica da aplicação (Model), da interface do Usuário (View) e do fluxo da aplicação (Controller) de maneira bastante simples e produtiva. Sistemas simples podem ser desenvolvidos em prazos recordes de 3 a 7 dias, enquanto sistemas mais complexos não demorariam mais que 3 meses até terem sua primeira versão final no ar.

O principal objetivo do framework é retirar das mãos do programador a preocupação de lidar com elementos repetitivos e simples de programação como criação de SQLs, validação de dados em relação ao banco de dados (tipo, comprimento, etc), filtragem de ataques de SQL Injection, recepção e envio de dados entre Interfaces e Controles, construção de chamadas XML para os WebServices, etc.

== Componentes do WirePhrame ==

Para atingir seus propósitos de ser um framework MVC de fácil utilização, são utilizadas as seguintes bibliotecas:

1 - [[POP]] - Camada de Modelo e Persistência

2 - [[COSA]] - Camada de Comunicação entre Controle e Interface

3 - [[JsWebGets]] - Camada de Interface

4 - [[RePHPort]] - Engine de Templates para Relatórios

Qualquer uma destas libs pode ser substituída por outra que melhor convier ao programador sem prejuízo organizacional do código, porém, no âmbito da ENAP, estas são as três oficiais.

== Estrutura Física da Aplicação com WirePhrame ==

Originalmente, uma aplicação feita com o WirePhrame apresentará a seguinte estrutura

Sistema
    ├ visual (elementos visuais da aplicação)
    │    ├ css
    │    └ images
    └ includes (todos os elementos necessários para a aplicação)
         ├ js
         │    ├ custom (funções e bibliotecas customizadas para a aplicação)
         │    └ libs (bibliotecas de terceiros)
         └ php
              ├ libs (bibliotecas de terceiros)
              ├ control (controle da aplicação, recebe Objetos e retorna XML)
              ├ model (Objetos da aplicação)
              └ view (Interfaces da aplicação)

A visão acima não foca em quais tecnologias serão utilizadas, permitindo o uso dessa estrutura para qualquer sistema, e a utilização de quaisquer outras bibliotecas (Smarty, Lumine, etc), sem prejuízo na organização. WirePhrame visa antes de ser um framework, ser uma proposta de wireframe de aplicação.

Se focarmos a organização baseado nas libs que são utilizadas oficialmente, a distribuição será como abaixo:

Sistema
    ├ [[index.php]] (inclui sysincludes.php e InterfaceBase.php)
    ├ [[webservice.php]] (inclui sysincludes.php e cria serviço baseado na COSA)
    ├ visual
    │    ├ css
    │    │    └ JsThemes (Temas da JsWebGets)
    │    │         ├ default
    │    │         │    ├ css (folha de estilos do tema)
    │    │         │    └ images (imagens do tema)
    │    │         └ default_black
    │    │              ├ css (folha de estilos do tema)
    │    │              └ images (imagens do tema)
    │    └ images (imagens da aplicação)
    └ includes (todos os elementos necessários para a aplicação)
         ├ js
         │    ├ custom
         │    │    ├ Interface 
         │    │    │    └ [[sysinterfacebase.js]] (funções base da Interface)
         │    │    ├ JsMainWindow
         │    │         └ [[JsMainWindow.ui.js]] (objeto base da Interface, extende a JsApplication, carregando métodos para tratamento das interfaces e objetos de comunicação JsWebServiceConnector e JsDataConnector)
         │    │    └ [[listviewEditFunctions.js]] (funções que servem para acelerar o uso do JsListView para edição)
         │    └ libs
         │         └ [[JsWebGets]] (biblioteca de Interface JsWebGets)
         │              ├ Data
         │              │    ├ [[JsDataConnector.js]]
         │              │    ├ [[JsHTTPRequest.js]]
         │              │    ├ [[JsWebServiceConnector.js]]
         │              │    └ [[JsXML.js]]
         │              ├ Widget
         │              │    ├ Form
         │              │    │    ├ Button
         │              │    │    │    ├ [[JsBoxButton.js]]
         │              │    │    │    ├ [[JsCheckBox.js]]
         │              │    │    │    ├ [[JsIcon.js]]
         │              │    │    │    ├ [[JsImageButton.js]]
         │              │    │    │    ├ [[JsMiniToolBarButton.js]]
         │              │    │    │    ├ [[JsPushButton.js]]
         │              │    │    │    ├ [[JsRadioButton.js]]
         │              │    │    │    ├ [[JsRadioButtonItem.js]]
         │              │    │    │    └ [[JsToolBarButton.js]]
         │              │    │    ├ Field
         │              │    │    │    ├ [[JsCNPJEdit.js]]
         │              │    │    │    ├ [[JsCodeEdit.js]]
         │              │    │    │    ├ [[JsColoPicker.js]]
         │              │    │    │    ├ [[JsComboBox.js]]
         │              │    │    │    ├ [[JsCPFEdit.js]]
         │              │    │    │    ├ [[JsDateEdit.js]]
         │              │    │    │    ├ [[JsIPEdit.js]]
         │              │    │    │    ├ [[JsLineEdit.js]]
         │              │    │    │    ├ [[JsLineEditAdv.js]]
         │              │    │    │    ├ [[JsListBox.js]]
         │              │    │    │    ├ [[JsMoneyEdit.js]]
         │              │    │    │    ├ [[JsRichTextEdit.js]]
         │              │    │    │    ├ [[JsRichTextField.js]]
         │              │    │    │    ├ [[JsSpinBox.js]]
         │              │    │    │    ├ [[JsTableBuilder.js]]
         │              │    │    │    ├ [[JsTextEdit.js]]
         │              │    │    │    ├ [[JsTimeEdit.js]]
         │              │    │    │    ├ [[JsUpload.js]]
         │              │    │    │    └ [[JsURLBuilder.js]]
         │              │    │    └ [[JsInput.js]]
         │              │    ├ Visual
         │              │    │    ├ Container
         │              │    │    │    ├ [[JsBox.js]]
         │              │    │    │    ├ [[JsDialog.js]]
         │              │    │    │    ├ [[JsDock.js]]
         │              │    │    │    ├ [[JsFieldSet.js]]
         │              │    │    │    ├ [[JsTab.js]]
         │              │    │    │    ├ [[JsToolBox.js]]
         │              │    │    │    ├ [[JsWebWrapper.js]]
         │              │    │    │    ├ [[JsWidgetGrid.js]]
         │              │    │    │    ├ [[JsWidgetStack.js]]
         │              │    │    │    └ [[JsWindow.js]]
         │              │    │    ├ Display
         │              │    │    │    ├ [[JsCalendar.js]]
         │              │    │    │    ├ [[JsImage.js]]
         │              │    │    │    ├ [[JsLabel.js]]
         │              │    │    │    ├ [[JsLine.js]]
         │              │    │    │    ├ [[JsMenu.js]]
         │              │    │    │    ├ [[JsMenuBar.js]]
         │              │    │    │    ├ [[JsMenuItem.js]]
         │              │    │    │    ├ [[JsMiniToolBar.js]]
         │              │    │    │    └ [[JsToolBar.js]]
         │              │    │    └ View
         │              │    │         ├ [[JsDataView.js]]
         │              │    │         ├ [[JsListView.js]]
         │              │    │         └ [[JsListViewItem.js]]
         │              │    └ [[JsWidget.js]]
         │              ├ [[include_clean_jswebgets.php]]
         │              ├ [[JsApplication.js]]
         │              ├ [[JsGeneral.js]]
         │              ├ [[JsObject.js]]
         │              └ [[JsTranslation.js]]
         └ php
              ├ libs 
              │    ├ [[COSA]]
              │    │    ├ [[cosa.php]]
              │    │    └ [[cosa.wsdl]]
              │    ├ [[POP]]
              │    │    ├ [[PArrayOf.php]]
              │    │    ├ [[PDate.php]]
              │    │    ├ [[PDatetime.php]]
              │    │    ├ [[Persist.php]]
              │    │    ├ [[PFloat.php]]
              │    │    ├ [[PInteger.php]]
              │    │    ├ [[POP.php]]
              │    │    ├ [[PText.php]]
              │    │    ├ [[PTime.php]]
              │    │    ├ [[PTypeBase.php]]
              │    │    └ [[PVarchar.php]]
              │    └ [[RePHPort]]
              ├ control 
              │    ├ [[ControlInterface.php]]
              │    ├ [[ControlLogin.php]]
              │    └ [[ControlReport.php]]
              ├ model 
              ├ view
              │    ├ [[InterfaceBase.php]]
              │    └ [[InterfaceLogin.php]]
              ├ [[sysincludes.php]] (inclui tudo que é necessário para funcionamento da aplicação)
              ├ [[sysconf.php]] (traz toda a configuração da aplicação, banco de dados, filesystem, etc)
              ├ [[syscosa.php]] (implantação da COSA de acordo com as necessidades da aplicação)
              ├ [[syscontrols.php]] (inclui todos os controles)
              └ [[sysmodels.php]] (inclui todos os modelos)

Este modelo é muito útil para aplicações que não compartilharão nada além de seus webservices com outras aplicações.

== Implantação na ENAP ==

No âmbito da ENAP, isto não acontece assim, sendo a distribuição dos arquivos levemente diferente para permitir um compartilhamento mais facilitado de controles e interfaces.

A pasta do Sistema em questão, e dos includes fica no mesmo nível, bem como a pasta images, pois para o compartilhamento mais facilitado das interfaces, controles e objetos, é mais rápido e menos problemático mantê-los juntos. Para detalhamento dos itens já referenciados, utilize os links acima. Novos links estão mapeados abaixo.

Assim, a distribuição real atual é a seguinte:

Sistema1
     [[index.php]]
     [[webservice.php]]
Sistema2
     [[index.php]]
     [[webservice.php]]
images (imagens compartilhadas pelos sistemas)
includes (todos os elementos necessários para a aplicação)
          js
              custom (funções e bibliotecas customizadas para a aplicação)
              libs (bibliotecas de terceiros)
                   JsThemes
                   JsWebGets
          php
               libs (bibliotecas de terceiros)
                   [http://adodb.sf.net adodb]
                   [[COSA]]
                   [http://www.fpdf.org fpdf]
                   gui_elements
                   jpgraph
                   phphtmlparser
                   [[POP]]
                   [[RePHPort]]
                   [[PDO_ADODB.php]]
               controls (controles das aplicações)
                   publico (controles compartilhados)
                       [[ControleApoio.php]](fornece dados de apoio às Interfaces)
                   Sistema1
                   Sistema2
               interfaces (interfaces das aplicações)
                   publico (interfaces compartilhadas)
                   Sistema1
                   Sistema2
               objs (objetos das aplicações)
                   publico (objetos compartilhados)
                   Sistema1
                   Sistema2
               reports (relatórios das aplicações)
               [[sysincludes.php]] (inclui tudo que é necessário para funcionamento das aplicações)
               [[sysconf.php]] (traz toda a configuração da aplicação, banco de dados, filesystem, etc)
               [[syscosa.php]] (implantação da COSA de acordo com as necessidades da aplicação)
               [[syscontrols_sistema1.php]] (inclui todos os controles para sistema1)
               [[syscontrols_sistema2.php]] (inclui todos os controles para sistema2)
               [[sysdatahora.php]] (funções para tratar Data e Hora)
               [[sysdefines.php]] (Definições de aplicação)
               [[sysgeneralfunctions.php]] (funções genéricas compartilhadas pelas aplicações)
               [[sysobjs.php]] (inclui todos os modelos)

== Roteiro para criação de novos Sistemas ==

Antes de iniciar qualquer sistema, certifique-se que as [[Etapas_de_um_projeto]] já estão todas atendidas, a fim de evitar problemas futuros em relação ao planejamento e execução do projeto.

Para criar um novo sistema utilizando a estrutura supra-citada, é preciso

1 - Criar diretório para o novo sistema, no mesmo nível dos outros, com o nome do sistema

2 - Acrescentar os arquivos [[index.php]] e [[webservice.php]] conforme descrito na documentação dos mesmos. Lembrar de definir a variável global "sistema" no webservice.php, para ser utilizada pelo [[syscosa.php]]. Esta variável é importantíssima e serve para evitar que um sistema interfira na sessão de outro sistema por acidente. Se a mesma não for definida, haverá problemas de todo o gênero e de difícil identificação.

3 - Criar um arquivo syscontrols_nomedosistema.php para ser incluso pelo arquivo [[webservice.php]]. Neste arquivo coloque todos os controles que serão utilizados pelo sistema.

4 - Criar registro do Sistema, Perfis de Acesso e Permissões no sistema [[WebCentral]].

5 - Criar na pasta de interfaces um diretório para o sistema, com os arquivos [[InterfaceBase.php]] e [[InterfacePrincipal.php]]

6 - Criar na pasta de controles um diretório para o sistema

7 - Criar na pasta de objetos um diretório para o sistema

Estes passos criarão a estrutura básica de um sistema, já fazendo controle de login com autenticação através do sistema WebCentral.

Para a criação de Interfaces, Controles e Objetos, veja respectivamente a documentação das bibliotecas [[JsWebGets]], [[COSA]] e [[POP]].

O framework WirePhrame utiliza-se destas bibliotecas para construção da interface, controle do fluxo da aplicação, comunicação entre interface e webservices, e comunicação entre aplicação e banco de dados. Abaixo, estão apresentados os itens de maneira resumida. Para compreensão completa do framework, veja a documentação isolada de cada biblioteca.

== Como se dá a comunicação e construção do sistema ==

O ponto de entrada em qualquer sistema é o arquivo [[index.php]], que já possui os includes necessários para o correto funcionamento do PHP do lado do servidor. Se houver algum erro, o mesmo já será visível nesta páginas.

Este arquivo tem um include para o [[InterfaceBase.php]] correspondente da aplicação. O arquivo [[InterfaceBase.php]] inclue as bibliotecas javascript necessárias e cria uma instância de [[JsMainWindow.js]] nomeada '''app''', além de definir a variável interface_principal_sistema, que é a interface que será carregada logo após o usuário executar o login com sucesso. Também serão definidos o tema da JsWebGets, Título da Aplicação, Título da Janela da Aplicação e logotipo a ser apresentado no canto direito superior da tela.

Ao final, será utilizado o objeto [[JsWebServiceConnector.js]] instanciado dentro de [[JsMainWindow.js]] com a referência '''app.ws''' para então realizar o bind, ou ligação, ao [[webservice.php]] da aplicação. Será feita uma varredura e todas as operações definidas no [[cosa.wsdl]] do [[webservice.php]] estarão disponíveis para uso pela interface, sendo logo em seguida disparado o callback initInterface definido em [[sysinterfacebase.js]], que solicitará o diálogo de login. Sistemas que não vão ter este diálogo, como o WebPonto, podem sobrescrever este callback redefinindo-o dentro de seu próprio [[InterfaceBase.php]]. A solicitação do diálogo de login é feito agora através do método criado dinamicamente a partir do [[cosa.wsdl]] chamado loadInterface. Todas as operações do [[cosa.wsdl]] estão amarradas dentro do '''app.ws''' podendo ser chamadas a qualquer momento como mostrado abaixo:

app.ws.getData(nodocontrole, parametros, callback);

Note que todas as operações do [[webservice.php]] serão chamadas tal qual definidas no WSDL, porém acrescidas de um novo parâmetro, que é um callback que pode ou não ser definido para processar automaticamente a resposta do WebService.

O fluxo da inicialização do sistema fica então da seguinte forma:

!!!!inserir figura aqui!!!!

== Depuração - Checando envio e recepção de mensagens entre cliente e servidor ==

Com o uso da classe JsWebServiceConnector e JsDataConnector, há uma nova forma de depurar o que é enviado e recebido do servidor. Uma variável global na JsWebGets chamada jsDebug é a responsável por isso, e definindo-a como true ou false a depuração é ativada.

Para uma maior conveniência essa variável pode ser ativada e desativada "on-the-fly" através do uso das teclas CTRL+ALT+D. Isso definirá a variável como true ou false e apresentará imediatamente a janela de depuração do WebService ou DataConnector que está sendo utilizado.

'''Nota importante:''' O arquivo [[include_clean_jswebgets.php]] contém a declaração de uma variável chamada js_ambient, que quando definida para "devel" força o recarregamento automático das interfaces ao serem solicitadas, evitando a necessidade de recarregar todo o sistema. Adapte o arquivo para que a variável seja carregada com valores diferentes em ambientes diferentes.

== Programação Client-Side ==

Abaixo, seguem exemplos de como trabalhar a comunicação de dados do lado do Cliente do WirePhrame.

== Criando Interfaces ==

Para a construção de Interfaces, utiliza-se a ferramenta [[JsDesigner]].

Edite o arquivo control.php passando para o método setClassesPath o valor do caminho do diretório das interfaces do sistema sendo editado. Altere também a extensão para .php com o método setExtension.

Isto provavelmente será modificado em versões futuras, permitindo a navegação no filesystem.

== Carregando Interfaces ==

Com a estrutura básica criada, e as interfaces prototipadas com a ferramenta [[JsDesigner]], fica fácil agora ir carregando interfaces sob demanda. Para tal, basta utilizar os métodos da [[JsMainWindow.js]], que são:

'''1 - loadInterface''' - uma interface carregada com este método ocupará a área central da janela, e automaticamente utilizará como callback o método renderInterface. É possível definir outro callback, se necessário. A chamada para carregamento da interface deve ser feita da seguinte forma:

app.loadInterface("sistema/Interface");

Definindo outro callback, o uso seria:

app.loadInterface("sistema/Interface", outrocallback);

'''2 - loadDialog''' - Como o próprio nome já diz, serve para carregar diálogos ao invés de Interfaces. O diálogo é sempre carregado como modal. Da mesma forma que os métodos anteriores, permite 2 parâmetros, a Interface e um callback. Se nenhum callback for definido, será utilizado o callback automático chamado renderDialog.

Ex:

app.loadDialog("sistema/InterfaceDialogSearch");

A utilização destes métodos otimizará o uso das interfaces, evitando o tráfego excessivo entre cliente e servidor, uma vez que após instanciar a interface a mesma é guardada durante a execução para reutilização posterior quando reconvocada a mesma interface.

== Carregando Sub-Interfaces ==

Há casos nos quais você pode desejar ter um Toolbar que chame subinterfaces de uma interface maior. Essa prática é boa para permitir uma maior granularização das interfaces, permitindo um maior compartilhamento das mesmas. Neste caso, o botão do toolbar chamará um método que disparará o app.loadSubInterface.

'''1 - loadSubInterface''' - similar ao método acima, loadInterface, porém não chamará o método renderInterface como callback automático. Este método deve ser utilizado para carregar subInterfaces da Interface que estiver carregada atualmente, e o callback provavelmente será um método da Interface atual, e não de [[JsMainWindow.js]]. O callback é obrigatório e deve ser implementado pelo programador na Interface que carregará a subinterface. Neste caso, a implementação do callback pode variar bastante de uma interface para outra, de acordo com a área que for destinada a apresentar uma interface contextual. Um exemplo disso seria a interface de Módulos Didáticos ou de Turmas do sistema WebCEF, onde apenas uma pequena região é atualizada com uma nova interface, mantendo constante a apresentação do treeview.

Ex:

app.loadSubInterface("sistema/InterfaceDetalhes", interfaceatual.addInterface);

A implementação do callback addInterface pode ser feita conforme ilustrado abaixo. ''Note o controle de js_ambient para forçar o recarregamento da interface em ambiente de desenvolvimento.''

    self.UIcallbacks.addInterface = function (jsEvent)
    {
        if (!Interfaces[newSubInterfaceName] || js_ambient == "devel")
        {
            code_str = app.getResponse();
            eval("newInterface = " + code_str);
            Interfaces[newSubInterfaceName] = new newInterface;
        }

        if (self.childNodes[1])
        {
            if (self.childNodes[1].bkpXMLData)
                self.childNodes[1].bkpXMLData();
            self.removeChild(self.childNodes[1]);
        }

        self.addItem(Interfaces[newSubInterfaceName]);

        if (Interfaces[newSubInterfaceName].type == "JsWindow")
            Interfaces[newSubInterfaceName].showWindow();

        if (Interfaces[newSubInterfaceName].type == "JsDialog")
            Interfaces[newSubInterfaceName].showDialog();
    }

Note que há uma linha chamando o método getXMLData descrito em [http://www2.enap.gov.br/wikienap/index.php/WirePhrame#Obtendo_o_XML_da_Interface "Obtendo o XML da Interface"]. Essa linha é importante no caso de subinterfaces, pois garante a integridade e manutenabilidade dos dados editados na interface, enquanto alternando a mesma. Porém, isto exige que o programador coloque uma chamada para o initInterface de forma a verificar de onde os dados devem ser lidos.

O método bkpXMLData copia as alterações feitas dentro da interface, para a referência XML sendo editada. Ao remover uma subinterface, esses dados devem ser mantidos de forma automático, por isso a linha no callback.

Na SubInterface, o tratamento para correta seleção da fonte de dados pode ser feito como apresentado abaixo:

        //verifica se deve usar o XML da subinterface ou da interface
        //quando um objeto é carregado pela interface pai, é colocado no app_xml
        //neste caso, usamos o CPF como chave para verificar se deve ou não
        //usar o da aplicação.
        if (self.xml_data)
        {
            var selfcpf = self.xml_data.getElementsByTagName("cpf")[0].text;
            var appcpf = app.xml_data.getElementsByTagName("cpf")[0].text;

            if(selfcpf != appcpf)
                self.xml_data = app.xml_data;
        }

        if (!self.xml_data)
            self.xml_data = app.xml_data;

        self.UIcallbacks.loadXMLData(self.xml_data);

Neste caso específico, o campo de valor único era o CPF da pessoa, porém, outros itens podem ser utilizados, desde que sejam únicos.

== Inicializando uma Interface ==

Após carregar uma interface, muitas vezes é necessário inicializá-la. O método que deve ser criado para isso chama-se '''initInterface'''. Este é um método que será chamado automaticamente após a renderização da interface na tela.

Crie este método conforme sua necessidade. De maneira geral, um bom initInterface deve fazer o seguinte:

1 - Resetar a interface - se você estava editando algo antes e carrega um novo objeto para edição, é uma boa remover as informações dele antes de iniciar a edição.

2 - Definição dos relacionamentos dos listviews para edição

3 - Carregar dados de Apoio - carregamento dos comboboxes, listas, etc, que são necessários para a edição dos dados.

4 - Carregar dados na Interface para edição.

Veja abaixo como realizar cada um dos passos acima citados.

== Carregando Dados do Servidor ==

Para carregar dados do servidor, deve ser utilizado o método getData do Webservice da aplicação.

Você deve ainda saber:

'''1 - Qual Controle deve ser invocado.'''

'''2 - Qual o método a ser executado.'''

'''3 - Quais os parâmetros aceitos pelo método chamado.'''

Estas informações podem ser obtidas na documentação do respectivo sistema, encontrada na página de [[Sistemas]] do Wiki.

Dessa forma, deve ser informada a string com o nome do Controle, e construída uma string XML contendo os 2 itens subsequentes, como ilustrado abaixo. No exemplo, acrescentamos um callback, método ou função que será executado após receber os dados do servidor com sucesso. O callback para o getData é obrigatório, pois os dados não serão tratados automaticamente pela aplicação, cabendo ao programador a implantação do tratamento e apresentação dos dados ao usuário. Há ainda a possibilidade de implantação de um fallback, que é um método ou função chamados quando há falha na resposta do webservice, ou seja, foi retornado um SoapFault. O objeto JsWebServiceConnector já tem um tratamento automático para estes casos, porém, em alguns momentos, o programador pode querer tratar a exceção lançada de forma distinta na interface. Nestes casos, basta implementar um fallback.

var dados = "<metodo>metodoExemplo</metodo>";
dados += "<parametro1>valor1</parametro1>";
dados += "<parametro2>valor2</parametro2>";
dados += "<parametro3>valor3</parametro3>";

app.ws.getData("ControleExemplo", dados, callback);

O callback, pode ser construído de formas distintas, dependendo do tipo de resultado que for recebido pelo servidor.

Exemplos de callbacks:

Resposta XML (é retornado um objeto XML):

function callback()
{
    var xml = app.getResponseXML();
    alert(xml.xml);
}

Resposta JSON (a resposta é interpretada como código Javascript - não há retorno):

function callback()
{
    app.getResponseJSON();
}

Resposta Texto (a resposta é texto puro):

function callback()
{
    txt = app.getResponse();
    alert(txt);
}

'''ATENÇÃO:''' Os retornos devem ser preferencialmente sempre no formato XML, segundo determinação do [https://www.governoeletronico.gov.br/acoes-e-projetos/e-ping-padroes-de-interoperabilidade e-Ping], que especifica o formato XML como o padrão para interoperabilidade de sistemas.

JSON e Texto devem ser utilizados apenas para Controles que tenham relação direta com o funcionamento operacional da aplicação, como por exemplo o ControleInterface. Métodos que recebem ou enviam dados e tenham regras de negócio, tais como ControleInscricao, devem ser sempre em XML. Evitar JSON usar pois representa mistura do código da Interface com o código de Controle, o que poderá causar maior dificuldade na manutenção do código.

Sobre como tratar XML com Javascript na JsWebGets, veja a seção [[JsXML.js]]

Sobre uma maneira mais otimizada para carga de dados do Servidor na Interface, veja [http://www2.enap.gov.br/wikienap/index.php/WirePhrame#Carregamento_autom.C3.A1tico_de_dados_na_Interface_a_partir_de_XML Carregamento automático de dados na Interface a partir de XML]

== Carregando Dados de Apoio na Interface ==

O conceito utilizado para Dados de Apoio refere-se aos dados que estejam em tabelas paralelas ao objeto principal do contexto, que sirvam para enumerar valores específicos. Exemplos de Dados de Apoio seriam as tabelas de Status de AlunoTurma, Tipo de Turma, Tipo de Colaborador, Tipo de Endereço, Tipo de Email, Tipo de Telefone, etc.

Para Maior comodidade do programador, foi implantado um Controle especial chamado [[ControleApoio.php]]. O ControleApoio possui apenas um método chamado carregarApoio, que aceitará n parâmetros que devem ser enviados como um nó xml chamado parametros e cada item como um subnó chamado item. O valor de cada item deve ser o nome da classe de apoio que deve ser chamada para carregamento da lista.

A solicitação deve ser sempre feita com o uso de um callback que tratará os dados de apoio recebidos colocando-os nas respectivas combos ou listas.

Por padronização, o método de callback para o ControleApoio deve ser sempre chamado '''carregarDadosApoio'''

Ex:

var dados = "<metodo>carregarApoio</metodo>";
dados += "<parametros>";
dados += "<item>TipoTelefone</item>";
dados += "<item>TipoEmail</item>";
dados += "<item>TipoEndereco</item>";
dados += "</parametros>";

app.ws.getData("ControleApoio", dados, callback);

O Retorno sempre será um XML no seguinte formato:

<response>
  <TipoTelefone>
    <result>
      <item>
        n campos... (cada classe tem um tipo de retorno distinto)
      </item>
      <item>
        n campos... (cada classe tem um tipo de retorno distinto)
      </item>
    </result>
  </TipoTelefone>
  <TipoEmail>
    <result>
      <item>
        n campos... (cada classe tem um tipo de retorno distinto)
      </item>
      <item>
        n campos... (cada classe tem um tipo de retorno distinto)
      </item>
    </result>
  </TipoEmail>
  <TipoEndereco>
    <result>
      <item>
        n campos... (cada classe tem um tipo de retorno distinto)
      </item>
      <item>
        n campos... (cada classe tem um tipo de retorno distinto)
      </item>
    </result>
  </TipoEndereco>
</response>

Sobre como tratar XML com Javascript na JsWebGets, veja a seção [[JsXML.js]] para maiores detalhes.

Um exemplo do callback carregarDadosApoio seria como abaixo. Note que foi inclusa uma verificação para os ver se os dados de apoio j[a não foram carregados. Isto serve para otimizar o carregamento da interface, reduzindo ainda mais a comunicação cliente servidor. Note também as linhas referentes ao comentário "//será utilizado para edição dos itens do listview". Elas são a associação dos dados daquele ponto do XML com aquela coluna específica do JsListView ou do JsDataView. Mais detalhes sobre o porque destas linhas podem ser encontrados na seção [http://www2.enap.gov.br/wikienap/index.php/WirePhrame#Carregamento_autom.C3.A1tico_de_dados_na_Interface_a_partir_de_XML Carregamento automático de dados na Interface a partir de XML]

    self.UIcallbacks.carregarDadosApoio = function (jsEvent)
    {
        if (!self.xml_support)
        {
            self.xml_support = app.getResponseXML();

            //carrega lista estadocicivil
            self.UIcomponents.estado_civil.clearData();
            self.UIcomponents.estado_civil.addItem('', '[--Selecione abaixo--]');

            var estciv = self.xml_support.getElementsByTagName("PEstadoCivil");
            estciv = estciv[0].getElementsByTagName("item");

            for (i=0; i< estciv.length; i++)
                self.UIcomponents.estado_civil.addItem(estciv[i].text);

            //carrega lista sexo
            self.UIcomponents.sexo.clearData();
            self.UIcomponents.sexo.addItem('', '[--Selecione abaixo--]');

            var sexo = self.xml_support.getElementsByTagName("PSexo");
            sexo = sexo[0].getElementsByTagName("item");

            for (i=0; i< sexo.length; i++)
                self.UIcomponents.sexo.addItem(sexo[i].text);

            //carrega lista ufs
            self.UIcomponents.uf_origem.clearData();
            self.UIcomponents.endereco_uf.clearData();

            self.UIcomponents.uf_origem.addItem('', '[--Selecione abaixo--]');
            self.UIcomponents.endereco_uf.addItem('', '[--Selecione abaixo--]');

            var ufs = self.xml_support.getElementsByTagName("PUF");
            ufs = ufs[0].getElementsByTagName("item");

            //será utilizado para edição dos itens do listview
            self.UIcomponents.enderecos.lvheaderdiv.childNodes[4].xml_data = ufs;

            for (i=0; i< ufs.length; i++)
            {
                var id = ufs[i].getElementsByTagName("id")[0].text;
                var nome = ufs[i].getElementsByTagName("nome")[0].text;

                self.UIcomponents.uf_origem.addItem(id, nome);
                self.UIcomponents.endereco_uf.addItem(id, nome);
            }

            //carrega lista países
            self.UIcomponents.pais_origem.clearData();
            self.UIcomponents.endereco_pais.clearData();

            self.UIcomponents.pais_origem.addItem('', '[--Selecione abaixo--]');
            self.UIcomponents.endereco_pais.addItem('', '[--Selecione abaixo--]');

            var pais = self.xml_support.getElementsByTagName("PPais");
            pais = pais[0].getElementsByTagName("item");

            //será utilizado para edição dos itens do listview
            self.UIcomponents.enderecos.lvheaderdiv.childNodes[5].xml_data = pais;

            for (i=0; i< pais.length; i++)
            {
                var id = pais[i].getElementsByTagName("id")[0].text;
                var nome = pais[i].getElementsByTagName("nome")[0].text;

                self.UIcomponents.pais_origem.addItem(id, nome);
                self.UIcomponents.endereco_pais.addItem(id, nome);
            }

            //carrega lista tipo
            self.UIcomponents.endereco_tipo.clearData();
            self.UIcomponents.telefone_tipo.clearData();
            self.UIcomponents.email_tipo.clearData();

            self.UIcomponents.endereco_tipo.addItem('', '[--Selecione abaixo--]');
            self.UIcomponents.telefone_tipo.addItem('', '[--Selecione abaixo--]');
            self.UIcomponents.email_tipo.addItem('', '[--Selecione abaixo--]');

            var tipo = self.xml_support.getElementsByTagName("PTipo");
            tipo = tipo[0].getElementsByTagName("item");

            //será utilizado para edição dos itens do listview
            self.UIcomponents.enderecos.lvheaderdiv.childNodes[0].xml_data = tipo;
            self.UIcomponents.telefones.lvheaderdiv.childNodes[0].xml_data = tipo;
            self.UIcomponents.emails.lvheaderdiv.childNodes[0].xml_data = tipo;

            for (i=0; i< tipo.length; i++)
            {
                var id = tipo[i].getElementsByTagName("id")[0].text;
                var nome = tipo[i].getElementsByTagName("nome")[0].text;

                if (tipo[i].getElementsByTagName("categoria")[0].text =="endereco")
                    self.UIcomponents.endereco_tipo.addItem(id, nome);
                if (tipo[i].getElementsByTagName("categoria")[0].text =="telefone")
                    self.UIcomponents.telefone_tipo.addItem(id, nome);
                if (tipo[i].getElementsByTagName("categoria")[0].text =="email")
                    self.UIcomponents.email_tipo.addItem(id, nome);
            }
        }

        self.UIcallbacks.loadXMLData(app.xml_data);
    }

== Identificando itens com o Controlde de Apoio ==

Há situações na qual não podemos carregar todos os itens de apoio por ser uma lista muito extensa. Nestes casos, podemos carregar apenas uma listagem de alguns itens que queremos identificar (vide Tela de Áreas de Atuação e Interesse do sistema WebSCO).

Neste caso, utilizamos o método '''identificarItens''' do [[ControleApoio.php]]

var dados = "<metodo>identificarItens</metodo>";
dados += "<parametros>";
dados += "<item>";
dados += "<tipo>PArea</tipo>";

dados += "<valor>1000</valor>";
dados += "<valor>1001</valor>";
dados += "<valor>1002</valor>";

dados += "</item>";
dados += "</parametros>";

Isto nos retornará um array similar ao retornado anteriormente pelo método '''carregarApoio'''. O tratamento do retorno pode ser feito da mesma forma.

== Carregamento automático de dados na Interface a partir de XML ==

Após a obtenção dos dados do servidor, é preciso apresentá-los ao cliente na Interface. Para facilitar esta tarefa, foi implementado um método chamado loadXMLData que permite que a Interface leia diretamente os dados do XML enviado pelo WebService para carregamento e apresentação dos mesmo na interface.

Para que isto seja possível, algumas regras devem ser observadas.

1 - Os nomes dos atributos e dos referidos campos devem ser idênticos para que a interface possa fazer o carregamento.

2 - Dados múltiplos (PArrayOf) devem ter o mesmo para o atributo e para o listview ou dataview, e o nome deve ser sempre no plural (ex: enderecos, telefones, emails, etc).

2.a - Dados não editáveis devem ser carregados em JsDataViews

2.b - Dados editáveis devem ser carregados em JsListViews. Neste caso, é preciso definir o método onEdit para o JsListView no initInterface. Este método será chamado quando o ListViewItem for clicado, para permitir a edição do item.

3 - No caso de dados múltiplos, a ordem das colunas deve ser idêntica à dos subobjetos sendo carregados.

4 - Não é possível carregar estruturas hierárquicas (treeviews) com este método.

5 - Se algum dos campos for o id de algum item carregado a partir do ControleApoio, você poderá fazer com que a nomenclatura correta seja apresentada, no lugar do id, definindo o xml_data para aquele item na coluna. Para maiores detalhes sobre como fazer isto, veja a definição do callback na seção [http://www2.enap.gov.br/wikienap/index.php/WirePhrame#Carregando_Dados_de_Apoio_na_Interface Carregando Dados de Apoio na Interface]

Para ter o método implantado na Interface correspondente, basta incluir o seguinte código na mesma. Este código pode ser adaptado conforme necessidades específicas do objeto ou da interface.

    self.UIcallbacks.loadXMLData = function ()
    {
        for (var i in self.UIcomponents)
        {
            var contents = app.xml_data.getElementsByTagName(i);

            if (self.UIcomponents[i].input)
            {
                if (
                    contents.length &&
                    self.UIcomponents[i].type != "JsCheckBox"
                    )
                    self.UIcomponents[i].setValue(app.xml_data.getElementsByTagName(i)[0].text);
                else if (
                    self.UIcomponents[i].type != "JsBoxButton" &&
                    self.UIcomponents[i].type != "JsPushButton" &&
                    self.UIcomponents[i].type != "JsIcon" &&
                    self.UIcomponents[i].type != "JsImageButton" &&
                    self.UIcomponents[i].type != "JsMiniToolBarButton" &&
                    self.UIcomponents[i].type != "JsToolBarButton" &&
                    self.UIcomponents[i].type != "JsColoPicker" &&
                    self.UIcomponents[i].type != "JsTableBuilder" &&
                    self.UIcomponents[i].type != "JsURLBuilder" &&
                    self.UIcomponents[i].type != "JsCheckBox"
                    )
                    self.UIcomponents[i].setValue('');
            }
            else
            {
                if (self.UIcomponents[i].type == "JsListView")
                {
                    self.UIcomponents[i].clearData();

                    if (contents.length)
                    {
                        for (var j=0; j < contents.length; j++) //>
                        {
                            for (var k=0; k < contents[j].childNodes.length; k++) //>
                            {
                                if (contents[j].childNodes[k].nodeName == self.UIcomponents[i].xmlNodeName)
                                {
                                    var lstitem = new JsListViewItem();
                                    self.UIcomponents[i].addItem(lstitem);

                                    if (self.UIcomponents[i].onEdit)
                                        lstitem.setEvent("click",self.UIcomponents[i].onEdit);

                                    lstitem.pid = contents[j].childNodes[k].getAttribute("id");

                                    for (var x=0; x < contents[j].childNodes[k].childNodes.length; x++)//>
                                        if (x < self.UIcomponents[i].lvheaderdiv.childNodes.length)//>
                                        {
                                            if(self.UIcomponents[i].lvheaderdiv.childNodes[x].xml_data)
                                            {
                                                var support_xml = self.UIcomponents[i].lvheaderdiv.childNodes[x].xml_data;
                                                var rid = contents[j].childNodes[k].childNodes[x].text;
                                                var nome = "";
                                                for (var y=0; y< support_xml.length; y++)//>
                                                {
                                                    var id = support_xml[y].getElementsByTagName("id")[0].text;
                                                    if (rid == id)
                                                    {
                                                        nome = support_xml[y].getElementsByTagName("nome")[0].text;
                                                        break;
                                                    }
                                                }
                                                lstitem.addItem(nome);
                                                lstitem.cells[x].js_realvalue = rid
                                            }
                                            else
                                            {
                                                //relations para JsDateEdit, JsCheckBox e JsRadioButton
                                                if (self.UIcomponents[i].relations[x][0].type == "JsDateEdit")
                                                {
                                                    var data = "";
                                                    if (contents[j].childNodes[k].childNodes[x].text)
                                                        var data = formataData(contents[j].childNodes[k].childNodes[x].text);
                                                    lstitem.addItem(data);
                                                    lstitem.cells[x].js_realvalue = contents[j].childNodes[k].childNodes[x].text
                                                }
                                                else if (self.UIcomponents[i].relations[x][0].type == "JsCheckBox")
                                                {
                                                    if (contents[j].childNodes[k].childNodes[x].text == "1")
                                                        var valor = "Sim";
                                                    else
                                                        var valor = "Não";

                                                    lstitem.addItem(valor);
                                                    lstitem.cells[x].js_realvalue = contents[j].childNodes[k].childNodes[x].text
                                                }
                                                else if (self.UIcomponents[i].relations[x][0].type == "JsRadioButton")
                                                {
                                                    valor = self.UIcomponents[i].relations[x][0].getLabelForValue(contents[j].childNodes[k].childNodes[x].text);
                                                    lstitem.addItem(valor);
                                                    lstitem.cells[x].js_realvalue = contents[j].childNodes[k].childNodes[x].text
                                                }
                                                else
                                                    lstitem.addItem(contents[j].childNodes[k].childNodes[x].text);
                                            }
                                        }
                                }
                            }
                        }
                    }
                }
                else if (self.UIcomponents[i].type == "JsDataView")
                {
                    self.UIcomponents[i].clearData();
                    if (contents.length)
                    {
                        var newData = new Array();
                        for (var j=0; j < contents.length; j++) //>
                        {
                            for (var k=0; k < contents[j].childNodes.length; k++) //>
                            {
                                newData[k] = new Array();

                                for (var x=0; x < contents[j].childNodes[k].childNodes.length; x++)//>
                                    if (x < self.UIcomponents[i].dtheaderdiv.childNodes.length)//>
                                    {
                                        if(self.UIcomponents[i].dtheaderdiv.childNodes[x].xml_data)
                                        {
                                            var support_xml = self.UIcomponents[i].dtheaderdiv.childNodes[x].xml_data;
                                            var rid = contents[j].childNodes[k].childNodes[x].text;
                                            var nome = "";
                                            for (var y=0; y< support_xml.length; y++)//>
                                            {
                                                var id = support_xml[y].getElementsByTagName("id")[0].text;
                                                if (rid == id)
                                                {
                                                    nome = support_xml[y].getElementsByTagName("nome")[0].text;
                                                    break;
                                                }
                                            }
                                            newData[k][x] = nome;
                                        }
                                        else
                                            newData[k][x] = contents[j].childNodes[k].childNodes[x].text;
                                    }
                            }
                        }
                        self.UIcomponents[i].loadData(newData);
                    }
                }
            }
        }
    }

== Enviando Dados ao Servidor ==

O envio de dados para o Servidor é idêntico à solicitação de dados, porém, por uma questão semântica, deve ser utilizado o método postData do Webservice da aplicação. Todo o aplicado ao getData também pode ser aplicado, sem distinção ao postData. Apenas solicita-se do programador que utilize o getData para obter dados que serão apresentados ao usuário na interface, e o postData para salvar os dados no servidor.

Você deve ainda saber:

'''1 - Qual Controle deve ser invocado.'''

'''2 - Qual o método a ser executado.'''

'''3 - Quais os parâmetros aceitos pelo método chamado.'''

Estas informações podem ser obtidas na documentação do respectivo sistema, encontrada na página de [[Sistemas]] do Wiki.

Dessa forma, deve ser informada a string com o nome do Controle, e construída uma string XML contendo os 2 itens subsequentes, como ilustrado abaixo. No exemplo, acrescentamos um callback, método ou função que será executado após receber os dados do servidor com sucesso. O callback para o postData é opcional, porém é recomendado que sempre seja criado um callback para dar ao usuário algum feedback sobre a execução do comando. Há ainda a possibilidade de implantação de um fallback, que é um método ou função chamados quando há falha na resposta do webservice, ou seja, foi retornado um SoapFault. O objeto JsWebServiceConnector já tem um tratamento automático para estes casos, porém, em alguns momentos, o programador pode querer tratar a exceção lançada de forma distinta na interface. Nestes casos, basta implementar um fallback.

var dados = "<metodo>metodoExemplo</metodo>";
dados += "<parametro1>valor1</parametro1>";
dados += "<parametro2>valor2</parametro2>";
dados += "<parametro3>valor3</parametro3>";

app.ws.postData("ControleExemplo", dados, callback);

O callback, pode ser construído de formas distintas, dependendo do tipo de resultado que for recebido pelo servidor.

Exemplos de callbacks:

Resposta XML (é retornado um objeto XML):

function callback()
{
    var xml = app.getResponseXML();
    alert(xml.xml);
}

Resposta JSON (a resposta é interpretada como código Javascript - não há retorno):

function callback()
{
    app.getResponseJSON();
}

Resposta Texto (a resposta é texto puro):

function callback()
{
    txt = app.getResponse();
    alert(txt);
}

'''ATENÇÃO:''' Os retornos devem ser preferencialmente sempre no formato XML, segundo determinação do [https://www.governoeletronico.gov.br/acoes-e-projetos/e-ping-padroes-de-interoperabilidade e-Ping], que especifica o formato XML como o padrão para interoperabilidade de sistemas.

JSON e Texto devem ser utilizados apenas para Controles que tenham relação direta com o funcionamento operacional da aplicação, como por exemplo o ControleInterface. Métodos que recebem ou enviam dados e tenham regras de negócio, tais como ControleInscricao, devem ser sempre em XML. Evitar JSON usar pois representa mistura do código da Interface com o código de Controle, o que poderá causar maior dificuldade na manutenção do código.

Sobre como tratar XML com Javascript na JsWebGets, veja a seção [[JsXML.js]]

== Obtendo o XML da Interface ==

Da mesma forma que é possível fazer o carregamento direto de um XML na interface, é possível obter o XML da mesma.

O XML retornado será um pouco diferente do recebido anteriormente, pois devido à edição de sub-objetos (PArrayOf) é necessário informar ao controle os ids dos itens que foram removidos da interface. Estes ids serão informados dentro do nó referente ao Listview, na tag <removed>. Cada item removido estará definido com a tag <item>. Os campos utilizados para editar o Listview provavelmente também estarão no XML, com valor em branco. Durante o processamento no controle, basta ignorar estes campos.

O método chama-se bkpXMLData. Para ter o método implantado na Interface correspondente, basta incluir o seguinte código na mesma:

    self.UIcallbacks.bkpXMLData = function ()
    {
        //verifica se deve usar o XML da subinterface ou da interface
        //quando um objeto é carregado pela interface pai, é colocado no app_xml
        //neste caso, usamos o CPF como chave para verificar se deve ou não
        //usar o da aplicação.
        if (self.xml_data)
        {
            var selfcpf = self.xml_data.getElementsByTagName("cpf")[0].text;
            var appcpf = app.xml_data.getElementsByTagName("cpf")[0].text;

            if(selfcpf != appcpf)
                return;
        }

        for (var i in self.UIcomponents)
        {
            if (self.UIcomponents[i].input)
            {
                if (
                    self.UIcomponents[i].type != "JsBoxButton" &&
                    self.UIcomponents[i].type != "JsPushButton" &&
                    self.UIcomponents[i].type != "JsIcon" &&
                    self.UIcomponents[i].type != "JsImageButton" &&
                    self.UIcomponents[i].type != "JsMiniToolBarButton" &&
                    self.UIcomponents[i].type != "JsToolBarButton" &&
                    self.UIcomponents[i].type != "JsColoPicker" &&
                    self.UIcomponents[i].type != "JsTableBuilder" &&
                    self.UIcomponents[i].type != "JsURLBuilder"
                    )
                {
                    var xml = app.xml_data.getElementsByTagName(i);
                    if (xml[0])
                        xml[0].text = self.UIcomponents[i].getValue();
                }
            }
            else
            {
                if (self.UIcomponents[i].type == "JsListView")
                {
                    var values = self.UIcomponents[i].childList;

                    var newTreeNode = app.xml_data.getElementsByTagName(i)[0];

                    var temp_removed = null;
                    var counter = newTreeNode.childNodes.length;
                    for (var j=0; j < counter; j++) //>
                    {
                        var node = newTreeNode.removeChild(newTreeNode.childNodes[0]);
                        if (node.nodeName == "removed")
                            temp_removed = node;
                    }

                    for (var j=0; j<values.length;j++) //>
                    {
                        var newNode = app.xml_data.createElement(self.UIcomponents[i].xmlNodeName);

                        for (var x=0; x<values[j].cells.length;x++) //>
                        {
                            var newSubNode = app.xml_data.createElement(self.UIcomponents[i].relations[x][2]);

                            if (values[j].cells[x].js_realvalue != undefined)
                                newSubNode.text = values[j].cells[x].js_realvalue;
                            else
                                newSubNode.text = values[j].cells[x].getValue();

                            newNode.appendChild(newSubNode);
                        }

                        if (values[j].pid)
                        {
                            newNode.setAttribute("id", values[j].pid);
                            var newSubNode = app.xml_data.createElement("id");
                            newSubNode.text = values[j].pid;
                            newNode.appendChild(newSubNode);
                        }

                        newTreeNode.appendChild(newNode);
                    }

                    //itens removidos do listview
                    if ((self.UIcomponents[i].removedItens && self.UIcomponents[i].removedItens.length) || temp_removed)
                    {
                        var newNode = app.xml_data.createElement("removed");

                        for (var j=0; j<self.UIcomponents[i].removedItens.length;j++) //>
                        {
                            var newSubNode = app.xml_data.createElement("item");
                            newSubNode.text = self.UIcomponents[i].removedItens[j];
                            newNode.appendChild(newSubNode);
                        }

                        self.UIcomponents[i].removedItens = new Array();

                        if(temp_removed)
                        {
                            counter = temp_removed.childNodes.length
                            for (var j=0; j< counter;j++) //>
                            {
                                newNode.appendChild(temp_removed.childNodes[0]);
                            }
                        }

                        newTreeNode.appendChild(newNode);
                    }
                }
            }
        }
    }

Note que para o correto funcionamento deste código, é necessário que vc defina o valor do atributo xmlNodeName no listview, com o nome do tipo de objeto que é apresentado nas linhas. Isto pode ser declarado no método initInterface da seguinte forma.

    self.UIcomponents.emails.xmlNodeName = "PEmail";

Note também que há um backup do XML obtido dentro da interface carregada. Isso é feito para os casos de explicitados em [http://www2.enap.gov.br/wikienap/index.php/WirePhrame#Carregando_Sub-Interfaces "Carregando Sub-Interfaces"]

== Limpando uma interface ==

Para limpar uma interface para reutilização, basta criar o método resetInterface e chamá-lo, preferencialmente no método initInterface. A implementação deve ser igual a abaixo:

    self.UIcallbacks.resetInterface = function ()
    {
        for (var i in self.UIcomponents)
        {
            if (self.UIcomponents[i].input)
            {
                if (
                    self.UIcomponents[i].type != "JsBoxButton" &&
                    self.UIcomponents[i].type != "JsPushButton" &&
                    self.UIcomponents[i].type != "JsIcon" &&
                    self.UIcomponents[i].type != "JsImageButton" &&
                    self.UIcomponents[i].type != "JsMiniToolBarButton" &&
                    self.UIcomponents[i].type != "JsToolBarButton" &&
                    self.UIcomponents[i].type != "JsColoPicker" &&
                    self.UIcomponents[i].type != "JsTableBuilder" &&
                    self.UIcomponents[i].type != "JsURLBuilder" &&
                    self.UIcomponents[i].type != "JsCheckBox"
                )
                {
                    if(self.UIcomponents[i].type == "JsLineEditAdv")
                        self.UIcomponents[i].setValue("", "");
                    else
                        self.UIcomponents[i].setValue("");
                }
                else if (self.UIcomponents[i].type == "JsCheckBox")
                {
                    self.UIcomponents[i].setChecked(false);
                }
            }
            else
            {
                if (self.UIcomponents[i].type == "JsListView" || self.UIcomponents[i].type == "JsDataView")
                {
                    self.UIcomponents[i].clearData();
                    if (self.UIcomponents[i].type == "JsListView")
                        self.UIcomponents[i].removedItens = new Array();
                }
            }
        }
    }

== Editando itens de um listview ==

Para edição de itens em um listview através de outros campos relacionados, são necessários os seguintes passos:

1 - Criação do Listview

2 - Criação de um botão para Adicionar/Salvar e outro botão para Remover itens do Listview

3 - Criação de um campo para cada coluna do Listview

Após isto feito, é preciso associar os seguintes elementos ao listview:

1 - a função externa listviewLoadData ao atributo onEdit (isto será utilizado no carregamento automático a partir de XML apresentado anteriormente).

2 - os relacionamentos, através da criação de um array bidimensional, com 3 itens na segunda dimensão.

2.a - A ordem dos itens associados deve refletir a ordem das colunas no listview.

2.b - O primeiro item de cada registro do array deve ser a referência para o objeto que será utilizado para editar o valor da coluna, e o segundo é um booleano que marca a obrigatoriedade ou não do preenchimento do campo. O terceiro deve ser o nome do atributo do objeto, para que o método getXMLData possa ser processador de forma correta posteriormente.

3 - Associar a função externa listviewSaveData ao botão de Adicionar/Salvar. Pode-se adicionar outra função ou método da interface no lugar desta, porém, para a execução automática conforme aqui descrito, deve-se pelo menos chamar a função listviewSaveData ao final da execução da outra função.

4 - Definir o atributo listview do botão Adicionar/Salvar como uma referência para o listview sobre o qual o mesmo editará os elementos.

5 - Associar a função externa listviewDelData ao botão de Remover. Pode-se adicionar outra função ou método da interface no lugar desta, porém, para a execução automática conforme aqui descrito, deve-se pelo menos chamar a função listviewDelData ao final da execução da outra função.

6 - Definir o atributo listview do botão Remover como uma referência para o listview sobre o qual o mesmo editará os elementos.

7 - Como elemento adicional, é possível incluir um código de validação do que está sendo colocado no Listview antes, definindo o atributo validacao do mesmo com a função que deverá ser executada para validação dos campos.

Segue abaixo exemplo do código necessário, conforme implantado na InterfaceDetalhesPessoaFisica para o listview de emails. Este código pode ser colocado no initInterface ou ao final do método buildInterface da Interface.

    self.UIcomponents.emails.onEdit = listviewLoadData;
    self.UIcomponents.emails.xmlNodeName = "PEmail";
    self.UIcomponents.email_addbt.setEvent("click",listviewSaveData);
    self.UIcomponents.email_addbt.listview = self.UIcomponents.emails;
    self.UIcomponents.email_delbt.setEvent("click",listviewDelData);
    self.UIcomponents.email_delbt.listview = self.UIcomponents.emails;

    self.UIcomponents.emails.relations = new Array();
    self.UIcomponents.emails.relations[0] = new Array();
    self.UIcomponents.emails.relations[0][0] = self.UIcomponents.email_tipo;
    self.UIcomponents.emails.relations[0][1] = true;
    self.UIcomponents.emails.relations[0][2] = "tipo";
    self.UIcomponents.emails.relations[1] = new Array();
    self.UIcomponents.emails.relations[1][0] = self.UIcomponents.email_email;
    self.UIcomponents.emails.relations[1][1] = true;
    self.UIcomponents.emails.relations[1][2] = "email";

== Campos editáveis em listview com callback para onchange ==

== Trabalhando com Treeviews ==

== JsLineEditAdv - Campo Autocomplete com WebServices ==

O objeto JsLineEditAdv é um componente que permite a pesquisa de registros em banco de dados enquanto a pessoa digita no mesmo, apresentando uma lista abaixo de opções pré-existentes que podem ser selecionadas com as teclas de seta do teclado.

Este objeto possui em sua estrutura interna tanto um JsDataConnector quanto um JsWebServiceConnector.

Neste exemplo, vamos ilustrar como utilizar este campo com um WebService.

Para que seja possível utilizar o JsLineEditAdv com Webservices, são necessários 3 passos simples para conexão do mesmo. Primeiro, a realização do bind com o webservice, depois a definição do Controle que será chamado, e por fim a definição do método que será executado pelo controle quando chamado para pesquisa. Isto pode ser feito com as instruções abaixo:

self.UIcomponents.rg.bind("webservice.php");
self.UIcomponents.rg.setWSControl("ControleAcessoPedestres");
self.UIcomponents.rg.setWSMethod("pesquisarPedestres");

Um callback deve então ser definido para tratar o retorno dos dados do Controle, e carregamento do mesmo no objeto JsLineEditAdv para exibição da lista de opções.

A definição do callback se dá da seguinte forma:

self.UIcomponents.rg.setCallback("self.UIcallbacks.listarRGs");

Caso a interface tenha sido desenvolvida no [[JsDesigner]], a definição do callback deve ser colocada no array UIcallbacksHandlers da seguinte forma:

self.UIcallbacksHandlers[0] = new Array();
self.UIcallbacksHandlers[0][0] = "callback";
self.UIcallbacksHandlers[0][1] = self.UIcomponents.rg;
self.UIcallbacksHandlers[0][2] = self.UIcallbacks.listarRGs;

A definição deste callbacks deve ser feita consultando diretamente o ws dentro do JsLineEditAdv. Cada Controle pode retornar um formato distinto de XML, não havendo uma regra exata para seu formato. O callback deverá então tratar isso e fazer a melhor apresentação possível. No caso citado, os nós chamados "item" apresentam 3 subnós que são concatenados para a parte de texto, mas só um tem valor real, como apresentado abaixo no loop:

self.UIcallbacks.listarRGs = function()
{
    var xml = new JsXML(self.UIcomponents.rg.ws.httprequest.responseText);
    var code = xml.getElementsByTagName("cosa_message");
    var code_str = "";

    for (var sys_i=0;sys_i < code[0].childNodes.length;sys_i++) //>
        code_str += code[0].childNodes[sys_i].nodeValue;

    xml = new JsXML(code_str);

    var itens = xml.getElementsByTagName("item");

    self.UIcomponents.rg.clearData();

    for (var i=0; i< itens.length; i++)//>
    {
        var rg = itens[i].getElementsByTagName("rg");
        var org = itens[i].getElementsByTagName("org");
        var nome = itens[i].getElementsByTagName("nome");
        self.UIcomponents.rg.addItem(rg[0].text, rg[0].text + " | " + org[0].text + " | " + nome[0].text);
    }
}

Caso a listagem sirva para realizar uma nova pesquisa no banco para resgatar dados mais completos para preenchimento de outros campos, pode-se definir um método para o evento onchange do JsLineEditAdv, como mostrado abaixo:

self.UIcallbacksHandlers[1] = new Array();
self.UIcallbacksHandlers[1][0] = "change";
self.UIcallbacksHandlers[1][1] = self.UIcomponents.rg;
self.UIcallbacksHandlers[1][2] = self.UIcallbacks.carregarPedestre;

Neste caso, o callback segue as regras normais apresentadas acima.

Veja também [http://www2.enap.gov.br/wikienap/index.php/WirePhrame#Cria.C3.A7.C3.A3o_do_Controle_para_trabalhar_com_JsLineEditAdv Criação do Controle para trabalhar com JsLineEditAdv] para detalhes sobre como criar o Controle da forma correta.

== Enviando Arquivos ao Servidor ==

== Gerando Relatórios ==

A geração de relatórios dentro da nova estrutura de sistemas tenta ser a mais simples o possível. Embora sua implementação seja complexa, o desenvolvimento com a mesma encontra-se bastante facilitado.

Para que seu sistema gere relatórios, basta carregar a interface pública '''InterfaceRelatorios'''. Esta interface indagará automaticamente a um controle específico do sistema (ControlReport) quais são os relatórios disponíveis para aquele sistema, e o Controle filtrará a lista de acordo com o usuário em questão. Isso significa já ter cadastrado os relatórios no sistema [[WebCentral]], ter associado os mesmos ao Sistema, ter associado aos perfis, ou ter associado ao usuário.

Após a lista ser carregada, o usuário seleciona o relatório que deseja emitir. Ao selecionar o relatório, a InterfaceRelatorios irá novamente solicitar ao Controle a Interface específica de parâmetros daquele relatório. A interface será carregada, e a codificação da mesma é livre para que o programador faça a implementação que achar necessária, lembrando apenas que os componentes da subinterface em questão devem ter o nome dos parâmetros que serão repassados ao relatório para sua geração.

A InterfaceRelatorios já possui um botão chamado "Gerar", que irá então submeter os parâmetros informados para o ControlReport, no método loadReport.

Esse WebService receberá a quantidade de parâmetros que for necessária para a correta consulta dos dados no banco, e o Controle gerará um XML com formato definido o qual será repassado à engine de templates de relatório.

Este template é então repassado junto com o conjunto de dados consultados pelo Controle à engine, informando ainda o formato de saida dos dados como HTML, Excel ou PDF. A engine [[RePHPort]] então processará todo o conjunto e gerará o relatório no formato apropriado.

Os dados repassados devem seguir o formato do XML abaixo:

<report>
    <header>
        ...
    </header>
    <footer>
        ...
    </footer>
    <data>
        ...
    </data>
</report>

O template é criado utilizando HTML padrão, com folha de estilo definida sempre em Centímetros. O HTML, após carregado com os dados, é então repassado à engine que converterá o documento para o formato solicitado. Se PDF, será convertido de HTML para DOMPDF diretamente. Se Excel, será o HTML enviado com cabeçalho informando que o mesmo é um Excel.

O arquivo é então salvo no sistema de arquivos do servidor, e é retornado um javascript que abrirá um pop-up apontando para o arquivo. Dependendo do formato, ele será apresentado no navegador ou solicitado que se faça o download do mesmo.

Eis um exemplo de como a chamada ao relatório pode ser feita a partir da interface.

var dados = "<id_prelatorio>iddorelatorio</id_prelatorio>";
dados += "<formato>pdf</formato>";
dados += "<parametro1>valor1</parametro1>";
dados += "<parametro2>valor2</parametro2>";
dados += "<parametro3>valor3</parametro3>";

app.ws.loadReport(dados, app.getResponseJSON);

Caberá ao programador então:

1 - Criar a SubInterface com o JsDesigner para definição dos parâmetros do relatório, lembrando que os campos deverão ter o mesmo nome do parâmetro.

2 - Criar o Controle e Método que extraírão os dados e retornaram um XML

3 - Criar o Template em HTML para apresentação do Relatório

4 - Cadastrar no WebCentral o Nome do Relatório, Interface, Controle, Método e Template, e associá-lo a um Perfil e a um Sistema

== Programação Server-Side ==

Abaixo, seguem exemplos de como trabalhar a comunicação de dados do lado do Servidor do WirePhrame.

== Criação de um Controle em PHP com ¡COSA! ==

Para exemplo da criação de um Controle em PHP utilizando o padrão ¡COSA!, vamos usar o [[ControleApoio.php]], por ser um controle simples e com apenas um método, conforme ilustrado abaixo.

<?php

class ControleApoio
{
    public function carregarApoio($params)
    {
        $return = "";

        if (is_array($params->parametros->item))
        {
            foreach ($params->parametros->item as $value)
            {
                $obj = new $value();
                $return .= "<".$value.">".$obj->search(null, null, "XML")."</".$value.">";
            }
        }
        else
        {
            $value = $params->parametros->item;
            $obj = new $value();
            $return .= "<".$value.">".$obj->search(null, null, "XML")."</".$value.">";
        }

        return utf8_encode($return);
    }
}

?>

Note que o método criado recebe apenas um parâmetro, chamado $params. $params será um objeto stdClass, criado a partir do XML enviado pela interface. Neste caso o XML enviado tem o seguinte formato:

<params>
    <metodo>carregarApoio</metodo>
    <parametros>
        <item>TipoTelefone</item>
        <item>TipoEmail</item>
        <item>TipoEndereco</item>
    </parametros>
</params>

O nó chamado "metodo" já foi utilizado pelo syscosa.php para chamar o método correto do Controle. Este nó continua disponível como um atributo do objeto $params, mas pode ser ignorado pois não tem função dentro do próprio método.

Os valores dos nós "item" podem então ser acessados da seguinte forma:

$params->parametros->item[0];
$params->parametros->item[1];
$params->parametros->item[2];

Note que neste caso, foi enviado um grupo de nós "item" dentro de "parametros". O controle, possui uma condicional que verifica se o nó "item" é um array ou não. Isso é necessário em casos de múltiplos nós, pois o PHP não saberá se o que foi enviado é múltiplo ou não. Assim, o seguinte XML traria um objeto stdClass um pouco diferente.

<params>
    <metodo>carregarApoio</metodo>
    <parametros>
        <item>TipoTelefone</item>
    </parametros>
</params>

Neste caso o acesso deveria ser feito da seguinte forma:

$params->parametros->item;

O PHP não tratará o item como um array, mas como um nó único de acesso direto. Essa distinção é extremamente importante, e deve ser sempre analisada com cuidado.

O retorno do Controle deverá ser uma string XML. Não inclua quebras de linhas ou tabulações entre os nós, pois o navegador Mozilla/Firefox criará nós fantasmas.

== Criação do Controle para trabalhar com JsLineEditAdv ==

A criação do método no Controle utilizando COSA para pesquisa no banco de dados em campos JsLineEditAdv deve seguir algumas pequenas regras básicas.

O caso apresentado é o que foi implementado no sistema [[WebAccess]].

O JsLineEditAdv, ao fazer a consulta no WebService, enviará para o mesmo o parâmetro "searchkey". Como estamos falando no padrão ¡COSA!, o mesmo virá contido dentro um objeto stdClass. No casoi abaixo, $params. Com esta informação, bastaria criar a cláusula SQL ou repassar o valor ao objeto POP com o método search para ter então uma resposta válida. Como dito anteriormente, a resposta pode ser qualquer XML em qualquer formato, porém, para manter compatibilidade com o formato de retorno da POP para o método search, mmarcaremos sempre como <item> cada tupla da resposta SQL dada.

public function pesquisarPedestres($params)
{
    //verifica se existe algum registro em aberto
    $sql = "select rg, org, nome from acessopedestre " .
        "where " .
        "    rg like '%".$params->searchkey."%' " .
        "group by rg, org, nome, tipo, destino";

    $rs = $GLOBALS["pop_db"]->query($sql);

    $result = $rs->fetchAll();

    $rs->closeCursor();

    $dados = "";

    foreach($result as $row)
    {
        $dados .= "<item>" .
                "<rg>".utf8_encode($row["rg"])."</rg>" .
                "<org>".utf8_encode($row["org"])."</org>" .
                "<nome>".utf8_encode($row["nome"])."</nome>" .
                "</item>";
    }

    return $dados;
}

Veja também [http://www2.enap.gov.br/wikienap/index.php/WirePhrame#JsLineEditAdv_-_Campo_Autocomplete_com_WebServices JsLineEditAdv - Campo Autocomplete com WebServices] para detalhes sobre como receber e tratar os dados na Interface da forma correta.