Árvore de páginas

Todo arquivo .war que está dentro do EAR do TOTVS11 é um arquivo passível de ser enxergado pelo menu do HTML Framework. Dentro do war da aplicação, deverá ser respeitada a estrutura de diretórios para que a aplicação seja devidamente reconhecida pelo Menu do HTML Framework. A estrutura deverá ser:

  • assets: pasta onde residem os recursos visuais da aplicação, como CSS, imagens, fontes e demais dependências (jQuery, bootstrap, etc)
  • html: 
    • subpasta "menu": obrigatória para que a aplicação seja reconhecida pelo Menu do HTML Framework;
    • subpastas das rotas: deverá existir uma página para cada rota definida no arquivo "menu.html". Esta pasta conterá tanto os arquivos HTML como JavaScript. No caso da rota "mapping" citada neste exemplo é necessário haver uma pasta de mesmo nome contendo os arquivos que lidam com as entidades envolvidas. Note, apesar de existir o "list", "view" e "edit" do HTML, não existe um controller para cada HTML, tudo está centralizado no "mapping.js" (vide figura abaixo);

  • i18n/uncompressed: deverá conter um arquivo "translations.js" contendo um JSON com as strings de tradução. Vide exemplo mais abaixo.
  • jsp: contém o arquivo de login e de customização do erro 404, conforme citado aqui;
  • Os "services" e "factories" da aplicação AngularJS deverão estar em alguma pasta referenciada pelo arquivo "menu/menu.js". Nos exemplos citados aqui e aqui, estes arquivos correspondem aos caminhos "/totvs-pnk/js/totvs-pnk-services.js" e "/totvs-pnk/js/totvs-pnk-factories.js" referenciados na cláusula "define";

Definição dos itens e rotas de menu

Dentro do WAR deverá existir uma pasta "html/menu" e dentro da pasta "webapp", com os seguintes arquivos:

  • menu.html: arquivo HTML que utiliza diretivas do AngularJS e contém todos os itens de menu da aplicação e suas respectivas rotas;

 

menu.html
<div class="well">
<h1>{{'totvs-pnk-menu' | i18n}}</h1>
<div class="row" ng-controller="totvs-pnk.menu.menuPnkCtrl">

	<ul class="nav nav-sidebar">
		<li><a href="#/totvs-pnk/mapping">{{'nav-editor' | i18n}}</a></li>
	</ul>
	
</div>
	
</div>	
  • menu.js: arquivo Javascript que instacia a aplicação (no exemplo abaixo "TOTVS PNK"), seu respectivo controller (no exemplo abaixo "totvs-pnk.menu.menuPnkCtrl") e carrega as dependências necessárias na diretiva "define" do RequireJS;

menu.js
define(['index', '/totvs-pnk/js/totvs-pnk-services.js', '/totvs-pnk/js/totvs-pnk-factories.js'], function(index) {
	menuPnkCtrl.$inject = ['loadedModules'];
	function menuPnkCtrl(loadedModules) {		
		loadedModules.startModule("TOTVS PNK");
	} // function menuCimatecCtrl(loadedModules) 
	
	index.register.controller('totvs-pnk.menu.menuPnkCtrl', menuPnkCtrl);	
	
});

 

Ao final, o resultado desta configuração será a tela abaixo. Ao clicar no item de menu "Visual Editor" a aplicação vai ser redirecionada para a URL "totvs-pnk/mapping", que foi informada anteriormente no arquivo "menu.html".

 

 

Definição dos serviços

A aplicação deverá registrar serviços para cada entidade acessar as factories (similares a DAO´s no Javascript) de cada entidade. Este registro pode ser feito em somente um arquivo para todo o módulo ou em vários (ex: um por entidade), o importante é registrar a respectiva dependência quando for necessário utilizar determinado serviço.

Cada serviço registrado deverá possuir o método "getFactory" implementado, retornando a factory correspondente, que efetuará as transações no banco de dados.

 

Exemplo de definição de serviços
define(['index', 'framework-services'], function (index) {

	serviceMapping.$inject = ['html-framework.generic.Service', 'html-framework.generic-quick-search.Service', 'html-framework.generic-typeahead.Service', 'html-framework.generic-zoom.Service', 'totvs-pnk.mapping.Factory', 'html-framework.generic-crud.Service', '$timeout'
							  ];
	function serviceMapping(genericService, genericQuickSearch, genericTypeaheadService,
		genericZoomService, factoryMapping, genericCRUDService, $timeout) {
		var service = {
			getFactory: function () {
				return factoryMapping;
			},
			/**
			 * zoomName - é o nome que aparece no titulo do zoom, normalmente é o nome externo da tabela.
			 */
			zoomName: 'l-mappings',
			quickSearchProperties: 'name',
			/**
			 * propertyFields - e um array de objetos com as propriedades label e property que definem
			 *                  os campos que deverão aparecer na opção do filtro do zoom.
			 */
			propertyFields: [{
					label: 'l-mapping',
					property: 'id'
				},
				{
					label: 'l-description',
					property: 'name'
				},
				{
					label: 'l-site',
					property: 'establishmentErpCode'
				}],
			/**
			 * tableHeader - é um array de objetos com as propriedades label e property que definem
			 *               as colunas que aparecem na tabela do zoom.
			 */
			tableHeader: [{
					label: 'l-mapping',
					property: 'id'
				},
				{
					label: 'l-description',
					property: 'name'
				},
				{
					label: 'l-site',
					property: 'establishmentErpCode'
				}],
			/**
			 * returnValue - é um metodo que será chamado na confirmação do registro selecionado no zoom,
			 *               esse metodo é opcional, o resultado deste metodo irá ser colocado no model do
			 *               campo de zoom. Se o metodo não for definida o valor colocado no model será o
			 *               conteudo de 'selected'.
			 */
			returnValue: function () {
				console.info("returnValue", this.selected);
				return this.selected.id;
			},
			/**
			 * getObjectFromValue - esse metodo é chamado pelo validator do campo de zoom, se não retornar o
			 *                      objeto o validater com a key "zoom" será inválido.
			 */
			getObjectFromValue: function (value) {
				console.info("getObjectFromValue", value);
				return factoryMapping.get({
					id: value
				});
			},
			zoomSelected: function (sel) {
				console.info("zoomSelected", sel);
			},
			/**
			 * applyFilter - é um metodo de carregar os dados do zoom, recebe como parametro um objeto
			 *               com as sequines propriedades:
			 *               init - contem o objeto informado no atributo zoom-init do zoom.
			 *               selectedFilter - contem o objeto do campo selecionado na pesquisa.
			 *               selectedFilterValue - contem o valor informado no campo de pesquisa.
			 *               more - valor logico informando se deve carregar mais dados no resultado
			 *                      ou iniciar uma nova pesquisa.
			 */
			applyFilter: function (parameters) {
				/**
				 * A propriedade zoomResultList deve receber os dados que irão aparecer no zoom.
				 */
				var that = this;
				this.zoomResultList = factoryMapping.query({}, function (result) {
					/**
					 * A propriedade resultTotal deve receber a quantidade de registros total da pesquisa
					 * não apenas o tamanho atual da lista de resultados.
					 * Utilizar o $timeout, porque o o $timeout atualiza o scopo da tela, e faz o contador
					 * atualizar o valor na tela. $scope não funciona, primeiro porque não pode ser
					 * injetado em um serviço, e neste momento já está ocorrendo uma operação de Digest do
					 * angular.
					 */
					$timeout(function () {
						/**
						 * Como o retorno deste sreviço é um Return (no java) é sempre retornado um objeto.
						 * O transformResponse utilizado no menu, faz que para os serviços sempre seja retornado
						 * a propriedade 'data' do Return e a propriedade 'length' é copiada para o primeiro mapping
						 * da lista que está em data como '$length'.
						 */
						that.resultTotal = result[0].$length;
					}, 0);
				});
			},
			cloneMapping: function (subj, callback) {
				this.getFactory().clone({}, subj, callback);
			},
			publishMapping: function (subj, callback) {
				this.getFactory().publish(subj, callback);
			}
		};

		angular.extend(service, genericService);
		angular.extend(service, genericQuickSearch);
		angular.extend(service, genericZoomService);
		angular.extend(service, genericCRUDService);
		angular.extend(service, genericTypeaheadService);


		return service;
	}


	index.register.service('totvs-pnk.mapping.Service', serviceMapping);
});

 

Definição das factories

A aplicação deverá registrar factories para disponibilizar à aplicação AngularJS os serviços REST das entidades JAVA. Este registro pode ser feito em somente um arquivo para todo o módulo ou em vários (ex: um por entidade), o importante é registrar a respectiva dependência quando for necessário utilizar determinado serviço registrado.

 

Exemplo de definição de factories
define(['index', 'framework-factories', '/totvs-pnk/js/url-services.js'], function (index) {

	factoryMapping.$inject = ['$resource', 'html-framework.generic.Factory', 'html-framework.generic-crud.Factory',
							  'html-framework.generic-quick-search.Factory', 'html-framework.factoryResourceLoader'];
	function factoryMapping($resource, genericFactory, factoryCrud, factoryQuickSearch, factoryResourceLoader) {
		var specificResources = {
			publish: {
				method: 'POST',
				isArray: false,
				params: {
					url: 'publish'
				}
			},
			clone: {
				method: 'PUT',
				isArray: false,
				params: {
					url: 'clone'
				}
			}
		};
		var factory = factoryResourceLoader.loadSpecificResources(URL.mappingService + '/:url/:id', specificResources);
		angular.extend(factory, genericFactory);
		angular.extend(factory, factoryCrud);
		angular.extend(factory, factoryQuickSearch);
		return factory;
	} // function factoryMapping ($resource, genericFactory, factoryCrud)


	// ########################################################
	// ### Register
	// ########################################################
	// Application Factories
	index.register.factory('totvs-pnk.mapping.Factory', factoryMapping);
});

Arquivo de tradução

A aplicação deverá prover um arquivo contendo um JSON com as strings de tradução para os idiomas suportados. Eis um exemplo:

 

translations.js
[{
	"t-remove-flow-confirmation": {
		"pt": "Tem certeza que deseja remover o fluxo?",
		"en": "Are you sure you want to remove the flow?",
		"es": "¿Está seguro que desea eliminar el flujo?"
	},
	"l-filtered-by": {
		"pt": "Filtrado por",
		"en": "Filtered by",
		"es": "Filtrado por"
	},
	"l-cell-detail": {
		"pt": "Detalhe da célula",
		"en": "Cell detail",
		"es": "Detalle de la célula"
	},
	"l-sku": {
		"pt": "SKU",
		"en": "SKU",
		"es": "SKU"
	},
	"l-invalid-msg": {
		"pt": "Os campos obrigatórios só podem ser inteiros maiores que zero.",
		"en": "Required fields must be integer values greater than zero.",
		"es": "Los campos obligatorios deben ser enteros mayores que cero."
	},
	"success-update-cell": {
		"pt": "Célula editada com SUCESSO!",
		"en": "Cell successfully edited!",
		"es": "Célula editada con SUCCESO!"
	}
}]

 

 

  • Sem rótulos