Camada de Visualização
Conforme citado na seção de contextualização, a arquitetura proposta visa eliminar os problemas identificados em diversos projetos já executados, onde foi possível identificar a falta de padronização para realizar tarefas comuns no processo de desenvolvimento. Assim, temos como objetivo principal desta seção o detalhamento do projeto Commons UI que provê soluções para que seja possível estabilizar, o que era previamente uma atividade caótica, em uma atividade produtiva e padronizada. Em busca deste equilíbrio, foram estudadas soluções e padrões de mercado que balizam o desenvolvimento de software atualmente.
O framework AngularJS foi a opção escolhida após compararmos as soluções disponíveis. A possibilidade de extensão do framework nos proporciona a flexibilidade necessária para criar componentes personalizáveis que atendem plenamente as nossas necessidades. Além do benefício da extensibilidade, o framework possui uma documentação detalhada, comunidade participativa e componentes genéricos já desenvolvidos e inclusos na solução. Para maiores detalhes, consulte o guia para desenvolvedores do AngularJS.
Tomando como base os motivos citados acima, criamos o projeto commons-ui. Este projeto compõe a camada de apresentação. Ao iniciar um projeto é possível ter o commons-ui como dependência para ter acesso a suas funcionlidades e compartilhar a padronização através de componentes como: directives, services, providers, factories, controllers, scopes, partials, etc.
Esta seção será dividida em três grandes tópicos: estrutura, detalhamento da estrutura e melhorias futuras. O primeiro tópico aborda a organização do projeto e responsabilidade de cada componente, descrevendo breviamente o comportamento dos componentes que compõem a estrutura do projeto. O tópico de detalhamento mostra detalhadamente o funcionamento dos componentes, a comunicação entre diferentes componentes e sua utilização pelo desenvolvedor. E por fim, o tópico de melhorias, no qual irá listar diversas melhorias que podem ser realizadas pela equipe de arquitetura para facilitar na construção de novos componentes e manter a atual estrutura da arquitetura estável.
O diagrama abaixo ilustra, de forma macro, a comunicação e organização dos componentes na atual versão da arquitetura.
| Itens | Nome | Descrição |
|---|---|---|
| 1 | Recipients | Services, factories e providers são componentes disponibilizados pela arquitetura que expoem métodos públicos e são passíveis de injeção de dependências. |
| 2 | Directives | Directives são como uma extensão do HTML na forma de atributos anexados nestes elementos. Diretivas utilizam-se de services, factories, providers e eventos para comunicar-se e realizar suas funções. |
| 3 | Partials | Partials são páginas HTML que podem ser incorporadas em outras páginas através da directive ng-include. Todas as páginas de list e detail da arquitetura são partials que compoem a estrutura HTML da aplicação. |
| 4 | Controllers | Controllers interagem com as partials e controlam itens de escopo e diretivas. Métodos e regras de negócio são expostos para as partials através de controllers. |
| 5 | Scopes | Scopes formam a cola entre controllers e directives. Para expor um método para uma diretiva, este método deve estar declarado dentro de um escopo. Cada controller possui um escopo. |
Recipients
Recipients é um termo utilizado na documentação do framework AngularJS para referir-se a um agrupador contendo os componentes: service, factory e provider. Os componentes deste agrupador são abordado no decorrer desta seção.
O que é um Service?
Atenção
A arquitetura não faz uso de services, apenas factories e providers. Apesar de não ser utilizado internamente na arquitetura, registrar e fazer uso de services é um conceito importante que pode ser aplicado nesta camada.
São objetos que compartilham funcionalidades através de APIs públicas para toda aplicação que faz uso do AngularJS. Services fornecem injeção de dependência e padronização quanto a criação de APIs públicas.
Services são registrados a partir da Module API. Ao registrar um service, dá-se um nome no qual será utilizado para injeção de dependências em quaisquers outros componentes do AngularJS. Abaixo temos um exemplo da utização de services para manter estados de um dialog.
// aqui o service com nome DateUtil (primeiro parâmetro na chamada da função service()) é registrado no módulo treelayer.commons.ui.util
angular.module('treelayer.commons.ui.util').service( 'DateUtil', function() {
this.format = function( date ) {
new Date(date).format('dd-mm-yyyy');
};
});
// em um modulo que depende do commons (shieldApp no caso da aplicação Shield) o componente DateUtil é injetado
// em um controller através do array de string (segundo argumento da função controller()) onde este argumento
// deve conter o nome que o componente foi registrado, ou seja, 'DateUtil'
shieldApp.controller( 'PeriodController', [ 'DateUtil', '$scope', function( DateUtil, scope ) {
// ...
// metodo chamado para formatar a data escolhida em um widget calendar
scope.formatCurrentDate = function(date) {
scope.formattedDate = DateUtil.format(date); // utilização do service DateUtil
};
}]);
Note a forma que declaramos a função format no service acima. Internamente, ao declarar um service, a função retornada será instanciada apenas quando necessário (lazy). Esta instanciação dá-se chamando construtor da função através do operador new. Services são estruturas simples, expoem métodos declarados no contexto (variável implícita this) e serão instanciados pelo AngularJS a partir de um construtor padrão. São utilizados quando não há necessidade de manter estado nos objetos e/ou criar objetos que necessitam de parâmetros para sua instanciação.
O que é um Factory?
Assim como o service, factories também são objetos que compartilham interfaces públicas de API e são passíveis a injeção de dependências.
Factories são registradas através da Module API . Ao registrar uma factory, dá-se um nome no qual será utilizado para injeção de dependências em quaisquers outros componentes do AngularJS. Abaixo temos um exemplo de como registrar uma factory e como utiliza-la através da injeção de dependências.
// aqui a factory com nome $dialog(primeiro parâmetro na chamada da função service()) é registrada no módulo treelayer.commons.ui
angular.module('treelayer.commons.ui').factory('$dialog', function() {
var Dialog = function(isOpen, operation) {
this.isOpen = isOpen ? isOpen : false;
this.operation = operation ? operation : '';
};
Dialog.prototype.close = function() {
this.isOpen = false;
this.operation = '';
};
Dialog.prototype.open = function(op) {
this.isOpen = true;
if (op) this.operation = op;
};
return {
'new': function(isOpen, operation) {
return new Dialog(isOpen, operation);
}
};
});
// em um modulo que depende do commons (shieldModule no caso da aplicação Shield) o componente $dialog é injetado
// em um controller através do array de string (segundo argumento da função controller()) onde este argumento
// deve conter o nome que o componente foi registrado, ou seja, '$dialog'
shieldApp.controller( 'PeriodCtrl', ['$dialog', '$scope', function ( Dialog, scope ) {
// ...
$scope.view = {
detailDialog: Dialog.new() // utilização da factory $dialog
};
// ...
}]);
Note a forma que declaramos a função foo na factory acima. Internamente, ao declarar uma factory, a função foo já esta instanciada e apenas será executada, diferentemente do service onde uma nova função será instanciada através do operador new. A factory $dialog oferece um método público new que constroi uma nova instância do objeto Dialog. Factories fornecem mais flexibilidade na sua instanciação, possibilitando a construção de objetos que possuam parametro, por isso, em casos como mostrado no exemplo acima são preferíveis ao invés de services.
Código Atualizado
Acesse o código-fonte da factory $dialog e do controller PeriodCtrl .
O que é um Provider?
Providers, assim como services e factories provem interfaces públicas e são passíveis de dependência. Diferentemente destes outros componentes, providers são expostos antes da inicialização da aplicação. Este atributo fornece aos providers um mecanismo de configuração, tornando-os uteis onde o comportamento de seus métodos públicos diferem conforme a configuração realizada em um bloco de inicialização do AngularJS.
São registrados através da Module API . Ao registrar um provider, dá-se um nome (primeiro parâmetro na chamada de função angular.module('treelayer.commons.ui').provider) no qual será utilizado para injeção de dependências em qualquer outro componente do AngularJS. Abaixo temos um exemplo de como registrar um provider e como utiliza-lo através da injeção de dependências.
angular.module('treelayer.commons.ui').provider('$message', function () {
// ...
this.baseUrl = '';
// ...
this.$get = ['$q', '$http', function ($q, $http) {
return this.$message($q, $http);
}];
// ...
});
Todo provider deve possuir um método $get. Este será invocado pelo AngularJS para instanciar o provider e deixa-lo disponível para o bloco de configuração da aplicação. Internamente, services e factories são instancias de providers, onde o método $get é injetado automaticamente pelo framework para facilitar o desenvolvimento.
A injeção de dependências de providers mudam conforme a localidade que estão sendo injetados. Como podemos perceber no bloco de código acima, o provider de exemplo foi registrado com o nome fooProvider. Para injetarmos providers em controllers, retira-se o prefixo provider, restando apenas $message, como pode ser visto no bloco de configuração da aplicação Shield. Em blocos de configuração, mantem-se o prefixo como no bloco de exemplo abaixo.
shieldApp.config(['$messageProvider',
'$routeProvider',
'partialsPath',
function ($messageProvider, $routeProvider, partialsPath) {
// ...
$messageProvider.baseUrl = AJS.contextPath() + '/rest/shield/latest';
// ...
}]);
Código Atualizado
Acesse o código-fonte do provider $messageProvider e do bloco de inicialização da aplicação Shield.
O que é uma Directive?
Diretivas são marcadores (podem ser atributos, classes ou novos elementos) em nodos da árvore DOM, como uma extensão da especificação construída pela W3C. Em sua inicialização, o framework AngularJS percorre a árvore HTML, compila e executa as directives declaradas. Durante sua execução directives são capazes de anexar novos comportamentos e funcionalidades que não existiam, até então, na estrutura especificada pela W3C.
O framework AngularJS disponibiliza, por padrão, um conjunto de directives.Assim como você pode criar controllers e services, directives também podem ser customizadas. A partir desta extensibilidade foram criadas directives que atendem as necessidades de padronização, como por exemplo a directive tl-key.
Abaixo temos um exemplo da utilização da directive tl-key e ng-click que ilustra como utilizar a directive customizada e uma directive empacotada com o AngularJS.
<!-- ... -->
<div class="inline-field-container" tl-ref="common">
<button class="aui-button align-right" ng-click="view.detailDialog.open('create')" tl-key="button.add"></button>
</div>
<!-- ... -->
shieldApp.controller( 'PeriodCtrl', ['$scope', '$http', '$property', '$resourceLocator', '$dialog', function ( $scope, $http, $property, $resourceLocator, $dialog ) {
// ...
$scope.view = {
// ...
detailDialog: $dialog.new()
// ...
};
// ...
}]);
No primeiro blocos acima temos o exemlo do uso da directive tl-key e ng-click; um partial, de onde retiramos um fragmento do HTML. A directive tl-key, insere os labels de forma internacionalizada a partir de uma chave. A directive ng-click, invoca o método open do objeto view.detailDialog contigo no controller PeriodCtrl.
Nestes exemplos podemos ver como é dada a interação entre controllers, scopes, partials e directives, onde o conjunto destes itens implica em uma interface dinâmica para o usuário final, agregando qualidade e padronização no processo de desenvolvimento.
Código Atualizado
Acesse o código-fonte do partial period-list.html e do controller PeriodCtrl.
O que é um Partial?
Partials são páginas HTML que podem ser incorporadas em outras páginas através da directive ng-include. Todas as páginas de listagem e detailhes da arquitetura são partials que compoem a estrutura HTML da aplicação.
Abaixo temos um exemplo que da utilização da directive ng-include para incorporar o dialog de detalhe na página de períodos da aplicação Shield.
<!-- ... --> <div ng-include="view.detailDialogUrl.url" /> <!-- ... -->
<div tl-object="period" tl-context="detail" tl-dialog tl-opened="$parent.view.detailDialog.isOpen"> <!-- ... --> <!-- ... --> </div>
Código Atualizado
Acesse o código-fonte dos partials period-list.html e period-detail.html.
O que é um Controller?
De acordo com a Wikipedia, um controller pode enviar comandos para sua visão associada para alterar a apresentação da visão do modelo (por exemplo, percorrendo um documento). Ele também pode enviar comandos para o modelo para atualizar o estado do modelo (por exemplo, editando um documento).
Adaptando a citação para o contexto do AngularJS, o controller interage com directives (presentes em elementos HTML nos partials) através do escopo, podendo modificar o comportamento na apresentação de conteúdos ou regras para o usuário final.
O exemplo abaixo mostra como o controller PeriodCtrl preenche uma combo-box na partial period-list.html através da directive ng-option.
<!-- ... --> <select class="select field-short" ng-model="model.enabled" tl-key="enabled" ng-options="key.bool() as value for (key, value) in view.status"> <option value=""></option> </select> <!-- ... -->
shieldApp.controller( 'PeriodCtrl', ['$scope', '$http', '$property', '$resourceLocator', '$dialog', function ( $scope, $http, $property, $resourceLocator, $dialog ) {
// ...
$scope.view = {
// ...
status: $property.range({object: 'period', attribute: 'enabled'}),
// ...
};
// ...
}]);
Código Atualizado
Acesse o código-fonte do partial period-list.html e do controller PeriodCtrl.
O que é um Scope?
É um link entre partials e controllers, onde tudo que declaramos em scopes é exposto para ser utilizado em partials, por directives. Contudo, scopes possuem outras funcionalidades, como por exemplo a função $watch que fornece ao desenvolvedor uma forma de observer valores contidos no scope e realizar ações conforme a interação do usuário com a UI.
Exemplo de código onde uma variável declarada em um escopo do controller PeriodCtrl é exposta para a directive ng-options.
<!-- ... --> <select class="select field-short" ng-model="model.enabled" tl-key="enabled" ng-options="key.bool() as value for (key, value) in view.status"> <option value=""></option> </select> <!-- ... -->
shieldApp.controller( 'PeriodCtrl', ['$scope', '$http', '$property', '$resourceLocator', '$dialog', function ( $scope, $http, $property, $resourceLocator, $dialog ) {
// ...
$scope.view = {
// ...
status: $property.range({object: 'period', attribute: 'enabled'}),
// ...
};
// ...
}]);
No exemplo acima, acessamos o scope do controller PeriodCtrl através de injeção de dependencias no construtor do controller (variável declarada como $scope). Valores são expostos para partials e directives através do operator . (dot). No exemplo, um objeto de nome view é criado no scope, e internamente este objeto possui um atributo status. A partir daí, a partial pode referenciar o objeto status através da atributo view do scope; como vemos na delcaração view.status.
Código Atualizado
Acesse o código-fonte do partial period-list.html e do controller PeriodCtrl.
Não deixe de ler a documentação oficial, funcionalidades como: prototypal inheretance, listeners, observers, data-biding, etc, são importantes no desenvolvimento da camada de apresentação.
Estrutura do Projeto Commons UI
Conforme a imagem abaixo, o projeto é estruturado por pastas, cada qual refletindo a responsabilidade de seus componentes. Como este projeto é inteiramente composto de arquivos Javascript, não existem dependencias de outros projetos.
| Pasta | Descrição |
|---|---|
| Directives | Contém todas as diretivas customizadas que estão disponíveis para uso dos desenvolvedores. Estas diretivas foram criadas para atender os requisitos da arquitetura e não interferem nas diretivas do AngularJS. |
| Filters | Contém filtros customizados que estão disponíveis para uso dos desenvolvedores. Estes filtros foram criadas para atender os requisitos da arquitetura e não interferem nas diretivas do AngularJS. |
| Lang | Contém funções utilitárias, não disponibilizadas pela API Javascript nativa dos navegadores. Podemos citar como exemplo as funções: each e curry. |
| Providers | Contém providers utilizados internamente pela arquitetura para evitar alto acoplamento entre as diretivas. |
| Services | Contém serviços declarados através da API do AngularJS que estão disponíveis para injeção e podem ser utilizados pelos desenvolvedores para padronizar e agilizar o processo de desenvolvimento de novas funcionalidades. |
| Style | Contém arquivos .css para padronização de elementos de UI. |
| Vendor | Contém as bibliotecas previamente citadas neste documento já minificadas. |
Além das pastas que agrupam os componentes por responsabilidade, o projeto possui o arquivo treelayer-bootstrap que provê um módulo onde estão listadas todas as dependencias. Este arquivo existe para que quando necessário, projetos especifiquem como dependência apenas este módulo e todos os componentes estarão automaticamente associados ao projeto.
Providers Disponibilizados pela Arquitetura
Providers são objetos expostos para toda a aplicação, passíveis de injeção, oferecem funções públicas e podem ser utilizados dentro de um bloco de configuração do AngularJS. Para maiores informações leia a documentação oficial sobre providers.
Message Provider
É o componente responsável por disponibilizar mensagens, descrições e validações e controlar o fluxo de execução das diretivas que fazem uso destes itens. Este componente é apoiado por endpoints RESTful que fornecem estas informações de forma dinâmica. Para realizar estas chamadas, em um bloco de configuração do AngularJS é possível configurar as diferentes URLs através de injeção de dependências. Abaixo temos uma imagem do projeto Shield, onde é configurado as URLs utilizadas pelo MessageProvider.
As funções disponíveis publicamente são:
| Nome | Descrição | Utilização |
|---|---|---|
whenMessageLoads | Fornece uma forma de se registrar a este provider e, quando o back-end retornar, o provider irá alertar os componentes registrados. | |
getRawMessages | Retorna um objeto contendo todas as mensagens, validações e descrições. Este objeto não sofreu nenhuma modificação por directives, esta no formato raw. | |
getValidations | Retorna um objeto contendo todas as validações. | |
createReference | Usado principalmente pela directive tl-ref, na qual cria o cache de referências. Este método é público mas não deve ser utilizado, foi criado para estabelecer a comunicação entre a directive e o cache de referências. | |
findMessagesByKey | Busca uma determinada mensagem pela chave contida no arquivo property. |
Busca de Mensagens
A partir de uma chamada da principal directive, tl-object, é disparada uma requisitção HTTP para um método genérico da arquitetura para buscar mensagens internacionalizadas. Esta funcionalidade baseia-se no nome do objeto passado para a directive tl-object para buscar as mensagens. Mensagens são compostas de labels, descriptions, validations, etc.
As URLs previamente configuradas ao inicializar um aplicação AngularJS servem como endpoint para esta funcionalidade.
Armazenamento de Mensagens
Após a resposta do servidor, se ocorrida com sucesso, todas as mensagens são armazenadas em cache no formato raw, ou seja, sem alteração nenhuma. Assim que possível outras diretivas irão processar estas mensagens e preencher outras áreas de cache deste provider.
Controle de Fluxo
Logo após a busca de mensagens e a resposta do servidor, todas as diretivas que se registraram neste provider serão avisadas que há mensagens não processadas. A directive tl-ref iniciará sua execução preenchendo o cache referenceMessages a com uma nova estrutura, oriunda do cache no formato raw. Este cache é disponibilizado para outras diferitvas através da função pública exposta findMessagesByKey.
Se você possui dúvidas quanto a funcionalidade das directives citadas nesta seção, leia as seções de directives Object e Ref.
Services Disponibilizados pela Arquitetura
Services são objetos compostos por funções, passíveis de injeção de dependências e inicializados . Um serviço no framework AngularJS é tratado como um singleton e pode ser injetado através de seu nome, bastando declara-lo como parametro em seus componentes (no caso, um controller, por exemplo). Para maiores informações acesse a documentação oficial sobre services.
Dialog Service
É um serviço declarado através do framework AngularJS que possui unicamente a funcionalidade de retornar um objeto que controla estados de um dialog. Mencionamos na introdução de services que estes não possuem estado, porém o que citamos na anteriormente foi que o Dialog Service tem como unica funcionalidade retornar um objeto com estados. Como isto se dá? Basicamente este serviço possui uma única função, a função responsável por criar o objeto dialog, ou seja, o Dialog Service não possui estado, mas sim objeto obtido no retorno desta chamada.
As funções disponíveis publicamente são:
| Nome | Descrição | Utilização |
|---|---|---|
new | Cria um objeto do tipo Dialog para que o usuário possa controlar as operações e a visibilidade de um determinado dialog. |
|
Dialog
Dialog é um objeto definido internamente no service DialogService e tem como responsabilidade controlar os estados de um dialog. O estado é basicamente quando o dialog está aberto e a operação que esta ocorrendo no momento. Para obter um objeto do tipo Dialog basta realizarmos uma chamada para a função new no DialogService.
Property Service
É um serviço declarado através do framework AngularJS que expõe funções públicas para trabalhar com propriedades, encapsulando a complexidade de realizar requisições HTTP para o desenvolvedor. Para maiores informações, leia a documentação de resources.
As funções disponíveis publicamente são:
| Nome | Descrição | Utilização |
|---|---|---|
properties | A partir do nome de objeto, busca todas as propriedades deste objeto no back-end. | |
range | A partir de um objeto e um atributo, busca a série se existente no arquivo de internacionalização. |
|
Submit Service
Que expõe funções que mapeiam os métodos genéricos CRUD da arquitetura back-end. Internamente as requisições HTTP são tratadas e processadas, tornando-se transparente para o desenvolvedor.
As funções disponíveis publicamente são:
| Nome | Descrição | Utilização |
|---|---|---|
create | Realiza uma chamada HTTP POST para a URL configurada no Após sua execução, este método delega ao back-end a responsabilidade de inserção e recebe como resposta, no caso de sucesso, um objeto contendo o id do objeto gerado pelo back-end ou, caso de falha, os erros gerados pelo back-end na inserção. | |
update | Realiza uma chamada HTTP PUT para a URL configurada no Após sua execução, este método delega ao back-end a responsabilidade de atualização de um objeto já persistido. Se a operação ocorrer com sucesso e houver um callback registrado, este é executado e não recebe nenhum parametro . Em caso de falha, se houver callback de falha registrado, é executado e recebe os erros gerados pelo back-end. |
|
delete | Realiza uma chamada HTTP DELETE para a URL configurada no Após sua execução, este método delega ao back-end a responsabilidade de deleção de um objeto já persistido. Se a operação ocorrer com sucesso e houver um callback registrado, este é executado e não recebe nenhum parametro . Em caso de falha, se houver callback de falha registrado, é executado e recebe os erros gerados pelo back-end. | |
retrieve | Realiza uma chamada HTTP GET para a URL configurada no Após sua execução, este método delega ao back-end a responsabilidade de busca de um objeto já persistido. Se a operação ocorrer com sucesso e houver um callback registrado, este é executado e recebe o objeto encontrado ou nulo, caso não foi possível encontrar um objeto através do identificador informado. Em caso de falha, se houver callback de falha registrado, é executado e recebe os erros gerados pelo back-end. | |
find | Realiza uma chamada HTTP GET para a URL configurada no Após sua execução, este método delega ao back-end a responsabilidade de deleção de um objeto já persistido. Se a operação ocorrer com sucesso e houver um callback registrado, este é executado e recebe um array de objetos encontrados, ou um array vazio, caso não foi possível encontrar objetos persistidos. Em caso de falha, se houver callback de falha registrado, é executado e recebe os erros gerados pelo back-end. |
Todos os métodos executados retornam um $promise. Promises são promessas de execução, em algum momento no futuro, da operação desejada (ex.: execução de uma chamada HTTP PUT). Algumas directives conseguem trabalhar com promises e com isso eliminamos a necessidade de callbacks. Para maiores informações, consulte a especificação Promises/A+.
Atenção
Estas funções não realizam nenhuma lógica adicional, apenas encapsulam as operações HTTP para a chamada de endpoints RESTful genéricos. Sendo assim, é responsabilidade da camada de serviços a implementação destas operações e a garantia de sua exeução.
Directives Disponibilizadas pela Arquitetura
O framework AngularJS já possui um conjunto de directives implementadas e disponíveis para construir a camada de apresentação. Contudo, requisitos como: internacionalização, estruturação, padronização, temas, reuso, não foram totalmente satisfeitos com as directives já implementadas. Entretanto, foi possível criar directives customizadas que atendem estes requisitos, graças a extensibilidade do framework. Nesta seção mostraremos as directives disponíveis na arquitetura, como utiliza-las em partials e quais são suas funcionalidades.
Todas as diretivas tem uma namespace como prefixo, como por exemplo ng na API do framework AngularJS. Este prefixo serve para que atributos em elementos HTML definidos pela especificação HTML Spec 4.01 não entrem em conflito com nomes de directives. Afim de seguir as boas práticas e evitar problemas advindos de novas especificações, toda directive criada e disponibilizada pela arquitetura é prefixada com a namespace tl.
TL Object
A directive tl-object é uma das mais importantes directives criadas na arquitetura. Tem como responsabilidade indicar em qual objeto estamos trabalhando em uma determinada UI e em qual contexto que estamos trabalhando, listagem ou detalhe, além de inicializar a busca de mensagens para a internacionalização. Através de herança, outras directives utilizam-se destas duas característica para implementar suas funcionalidades e manipular a UI conforme necessário.
Abaixo temos um exemplo prático da utilização desta directive em dois contextos diferentes; tela de listagem e detalhe de períodos.
<div id="content" tl-object="period" tl-context="list"> <!-- ... --> <!-- body --> <!-- ... --> </div>
Neste exemplo temos um outer div, ou seja, um div que engloba todos os outros elementos presentes na partial, utilizando a directive tl-object. Quando identificada, o seu construtor é executado e a partir dos valores de atributos em elementos HTML, incializa-se a busca de mensagens para a internacionalização.Toda e qualquer funcionalidade executada por outras directives nesta partial, incorporarão dois parâmetros na sua execução: objeto de negócio e contexto de visualização.
| Atributos | Parâmetros | Descrição |
|---|---|---|
tl-object | String | Em tempo de execução, toda a directive declarada abaixo da directive TL Object (nós filhos do nó onde há o atributo Ex.: Ao preencher o atributo |
tl-context | String | Em tempo de execução, toda a directive declarada abaixo da directive TL Object (nós filhos do nó onde há o atributo Ex.: Ao preencher o atributo |
Além das funcionalidades e comportamentos acima, a directive tl-object possibilida que outras directives se registrem (através de callbacks) e sejam alertadas quando o back-end retornar as mensagens internacionalizadas. Esta directive faz uso do MessageProvider para inicializar a busca por mensagens de internacionalização.
TL Key
A directive tl-key foi criada para atender o requisito de internacionalização e a padronização de leiaute para mensagens e validações. Esta directive faz uso da directive tl-object, afim de obter o contexto da partial e então renderizar estes elementos no formato apropriado e, para registrar-se e obter as mensagens de internacionalização assim que a busca for finalizada pelo MessageProvider.
Abaixo temos um exemplo prático da utilização desta directive em dois contextos diferentes, tela de listagem e detalhe de períodos, e a estrutura final destas telas após a execução das directives.
<div id="content" tl-object="period" tl-context="list"> <h2 tl-ref="search.period" tl-key="title"/> <!-- ... --> </div>
Em sua execução, alguns elementos serão acrescentados e/ou modificados baseado no contexto adquirido através da directive tl-object.
TL Ref
A directive tl-ref visa facilitar a vida do desenvolvedor. Em conjunto com a directive tl-key, é possível criar referencias para chaves evitando a repetição de longos prefixos. Internamente, a directive tl-ref, assim como a directive tl-key, faz uso da directive tl-object afim de obter o contexto e objeto da partial. Contudo, a tl-ref não manipula, de forma alguma, a árvore DOM, trabalhando unicamente em conjunto com o MessageProvider.
Abaixo temos um exemplo prático de como esta directive pode ser utilizada para tornar o processo de desenvolvimento mais produtivo, evitando repetições de prefixos em chaves de internacionalização.
<div tl-object="company" tl-context="detail" tl-dialog tl-opened="detailDialog.isOpen">
<!-- ... -->
<!-- Neste elemento é declarado o tl-ref, informando que todos os filhos deste nó referenciarão model.company -->
<div class="dialog-page-body" tl-ref="model.company as model">
<form class="aui">
<div class="dialog-panel-body panel-body">
<div class="field-container">
<!-- Este elemento é filho da div onde declaramos o tl-ref, ou seja, este tl-key agora é mapeado para "model.company.name" eliminando
a necessidade de informar o prefixo model.company -->
<input type="text" class="field-medium" tl-key="name" ng-model="model.0x1.name" />
</div>
<!-- ... -->
<!-- ... -->
</div>
Internamente, no momento de execução do construtor da directive tl-ref, todas as mensagens presentes no MessageProvider, que estão no formato raw, são processadas conforme a referencia informada para a directive e colocadas em cache.
TL Find
A directive tl-find disponibiliza busca genérica sem a necessidade implementar as chamadas HTTP ao back-end. A partir de um objeto preenchido na tela (objeto no qual é passado para a directive), internamente a directive percorre os atributos deste objeto e monta uma consulta padrão. Esta consulta padrão leva em consideração os tipos de dados e é construída através de cláusulas AND (por padrão) e a concatenação dos atributos e valores do objeto. Caso a busca ocorra com sucesso, um segundo atributo chamado tl-result mapeia para qual variável é atribuida a resposta da chamada. Falhas são descartadas silenciosamente pela arquitetura.
Abaixo temos um exemplo prático de como esta directive pode ser utilizada para realizar buscas genéricas de qualquer entidade já persistida no back-end.
<!-- partial que possui a estrutura apresentada para o usuário, com campos que estão mapeados (estes campos estão omitidos para simplicar o exemplo) em um objeto de nome 'model' no controller deste partial --> <div id="content" tl-object="company" tl-context="list"> <!-- ... --> <!-- este botão contem a directive tl-find, na qual recebe o objeto model que contem os valores preechidos pelo usuário --> <!-- também esta mapeado o atributo tl-result, no qual diz que a resposta desta busca deve ser mapeada para o objeto 'list' presente no scope deste controller --> <button class="aui-button align-right" tl-ref="common" tl-key="button.search" tl-find="model" tl-result="list" /> <!-- ... --> </div>














