Fwd: [DIF2] Novidades

20 views
Skip to first unread message

Pedro Viegas

unread,
Sep 16, 2020, 5:52:23 AM9/16/20
to DIF2 Mailing list
Boa tarde,

Nos últimos tempos tenho feito várias melhorias na DIF/ORM que venho aqui resumir para que passem a utilizar sempre que se justificar:

TextEdit com atributo "suffix" melhorado
Para situações como EUR, %, minutos ou qualquer outro sufixo/unidade num campo de texto deve ser usado o sufixo. No entando este era pobre visualmente.
Abusem dele porque existiam inúmeras situações no passado em que não o usavamos.

image.png


Novos ColumnTypes para as colunas de uma grid (MONEY_EUR e PERCENTAGE)
Para valores percentagem e monetários passam a existir estes dois tipos.
Eles formatam devidamente os valores em render, mantendo o editor numérico já existente.

image.png


Título default das colunas
Tal como as labels dos Fields das Forms, as colunas se não receberem title passam a procurar uma mensagem com o ID igual ao ID da coluna. Só se não existir fazem o CamelCase anterior.



FileUpload column
Quando tivermos uma coluna estilo DOCUMENT_ID, em que o desejado é para esse campo em cada registo fazer a gestão de um ficheiro a armazenar no DocumentRepository, este novo tipo de campo faz todo o processo automaticamente. Não precisa de qualquer desenvolvimento server side. É feito o upload para o document repository e despoletado um update normal ao novvo evento OnAjax da grid com o novo valor para o campo do ID do documento.
<dif:column attribute="<%=Atividade.Fields.DOCUMENTID%>" type="<%=Column.TYPE_DOCUMENT_EDITOR%>"
title="${messages.atividadeDocumento}"/>

image.png



Parâmetros ajaxTimeout
O tempo defult para todos os pedidos AJAX da ExtJS é de 30s.
Passa a ser possível num novo parâmetro do Document definir esse tempo para cada página.
Ex: (60000 = 60 segundos)
<dif:document templateID="diftemplate2Col" ajaxRequestsTimeout="60000">
É ainda possível via API acedendo a um método na tag Document.
O componente ReportAreasEditor já força um valor superior 2m no parâmetro da página onde for usado caso não tenha sido definido nenhum.
this.getDocumentTag().setAjaxRequestsTimeout(120000);
Existe ainda a possibilidade de definição de um novo valor default geral de DIF num parâmetro:

image.png



JOBs da DIF
Foram feitas várias melhorias no interface de gestão dos JOBs:
  1. Indicação da aplicação a que estão associados e agrupamento pela mesma
  2. Nome e descrição de cada JOB que deve ser explícito quando os criamos para ajudar os clientes e nós próprios a saber o que cada JOB faz.
  3. Opção de "acordar" um JOB
  4. Estado de cada JOB nos interfaces onde é usado (Gestão de mails, Eventos, Registos...)
image.png

image.png



Redirects sem o problema do F5 (resubmit anterior indesejado)
No passado foi feita a alteração na DIF para que quando fosse feito um redirect num OnSubmit a página ficasse com o URL da página final e não do submit da form que originou o pedido.
Este comportamento pode e é desejado noutras situações que não originam num submit de form.
Foi por isso adicionada essa capacidade em qualquer redirect da framework via o context.
this.context.redirectTo(this.context.getStage(), "pedidoID=" + this.pedido.getId(), true);
this.context.getRequest().addParameter("newTipoID", null);
this.context.setPreventPreviousSubmitOnRedirection();



Componente ListPicker

Foi criado um novo componente para situações em que o objetivo é apenas mostrar um Dialog com uma Grid dentro para escolha de um registo.

Não precisa de nenhum código Java side, só JSP e tem várias variantes de apresentação através dos seus parâmetros:

<dif:listPicker id="selectTipo" title="${messages.selectNovoTipoPedido}"
dataSetBeanClass="<%=TipoPedidoCreditacao.class%>"
descriptionColumnAttribute="<%=TipoPedidoCreditacao.Fields.NOME%>"
descriptionDetailsColumnAttribute="<%=TipoPedidoCreditacao.Fields.DESCRICAO%>"
width="500" height="350" idColumnVisible="false"
selectElementType="<%=SelectElementType.ACTION_LINK%>"
destinationStageID="<%=EdicaoPedidoCreditacaoUC.class.getSimpleName()%>"
destinationStageParameters="pedidoID=${stage.pedido.id}&newTipoID=$[id]"/>


image.png




Combo Boxes para tabelas sem filtros sem necessidade de OnAjax na stage

A mesma sintaxe do ListPicker foi adicionada à ComboBox:

<dif:comboField id="<%="item"+PedidoCredItem.FK().tipoPedidoCredItem().ID()%>" autocomplete="false"
dataSourceAttribute="<%=PedidoCredItem.FK().tipoPedidoCredItem().ID()%>" width="580"
dataSetBeanClass="<%=TipoPedidoCredItem.class%>"
dataSetDescriptionField="<%=TipoPedidoCredItem.Fields.NOME%>"
dataSetDescriptionDetailField="<%=TipoPedidoCredItem.Fields.DESCRICAO%>"
dataSetOrderByField="<%=TipoPedidoCredItem.Fields.DESCRICAO%>"/>




ORM: Várias melhorias introduzidas com o novo ORM

Algumas ainda a meio mas várias já operacionais:


Obtenção de um DataSet sem passar pela sessão ou factory:

Event.getDataSetInstance().query().asList();

Obtenção de um objeto pelo seu ID mesmo que composto:

Event event = Event.getInstance(1L);

Obtenção de proxy objects (não fazem o select à BD (são lazy loaded) úteis para criação de registos e setar as suas dependências:

PedidoCreditacao pedido = new PedidoCreditacao();
pedido.setDataPedido(new Timestamp(new Date().getTime()));
pedido.setIndividuo(Individuo.getProxy(Long.valueOf(this.getUserPreferences().getCodeIndividuo())));
pedido.setTipoPedidoCreditacao(TipoPedidoCreditacao.getProxy(this.newRecordTipoID));
pedido.setEstadoPedidoCreditacao(EstadosPedidoCreditacaoUC.CRIADO);
pedido = PedidoCreditacao.getDataSetInstance().insert(pedido);

Definição dos campos de um JSONResponseDataSetGrid:

(todos os campos da tabela principal adicionados de 4 campos de relações)

response = new JSONResponseDataSetGrid<PedidoCredItem>(PedidoCredItem.getDataSetInstance());
response.setFields(PedidoCredItem.Fields.values());
response.addField(PedidoCredItem.FK().tipoPedidoCredItem().ID());
response.addField(PedidoCredItem.FK().tipoPedidoCredItem().NOME());
response.addField(PedidoCredItem.FK().estadoPedidoCredItem().ID());
response.addField(PedidoCredItem.FK().estadoPedidoCredItem().DESCRICAO());



Grid com eventos onLoad e onWrite
Foram criados dois atributos na tag grid para colocar JavaScript a ser chamado quando é feito o load da grid ou qualquer alteração de dados da mesma:
(eventualmente no futuro criam-se duas sub tags para o mesmo efeito para quando o código é muito e não queremos criar uma dif:contribution para definir uma função)
<dif:grid id="pedidoItemsGrid" ajaxEvent="pedidoItems" showBorder="false" showResetConfigButton="false"
delRow="true" readonly="${!stage.canEdit}" noDataMessage="${messages.pedidoSemItems}"
groupColumn="<%=PedidoCredItem.FK().tipoPedidoCredItem().NOME()%>"
onWrite="obterCalculoValorfunc({updateTotal:true,showCalculos:false});">



Novas features e melhorias nos summary
O title era obrigatório. Por vezes apenas queremos o icon e não o title. Passa a ser opcional sem que com isso desapareça o icon.
Passa a existir a possibilidade de definir um link associado a cada summaryItem (por JS, URL ou stage):
<dif:summaryItem title="${messages.idTipo}" value="${stage.pedido.tipoPedidoCreditacao.nome}"
actionLabel="${messages.mudarTipo}" actionJS="funcselectTipo()"/>
Passa a ser possível por atributo destacar um summaryItem (com o highlight):
<dif:summaryItem title="${messages.idEstado}" value="${stage.pedido.estadoPedidoCreditacao.descricao}"
newLine="true" highlight="true"/>
Visualização destas funcionalidades:
image.png



Para já é só... :-D

Tudo isto está no Development da DIF e em uso no Development do SigesSrv.


Obrigado.



Com os melhores cumprimentos,



Pedro Viegas
Director Departamento de Desenvolvimento
(Development Manager)
Digitalis Informática Lda
R&D: http://development.digitalis.pt



Pedro Viegas

unread,
Sep 16, 2020, 5:52:47 AM9/16/20
to DIF2 Mailing list
Bom dia,

Segue mais uma lista de novas funcionalidades/melhorias de volto que foram adicionadas à DIF nas últimas semanas.
Mais se seguem nas próximas semanas, que não estão a ponto de partilhar convosco.



ListPicker
Foi adicionado ao listPicker suporte para a dif:requestParameter bem como uma nova Tag adicional column. Desta forma podemos adicionar várias colunas de contexto à janela de escolha.
<dif:listPicker id="selectTipo" title="${messages.selectNovoTipoPedido}"
dataSetBeanClass="<%=TipoPedidoCreditacao.class%>"
descriptionColumnAttribute="<%=TipoPedidoCreditacao.Fields.NOME%>"
descriptionDetailsColumnAttribute="<%=TipoPedidoCreditacao.Fields.DESCRICAO%>"
                width="600" height="350" idColumnVisible="false"
                selectElementType="<%=SelectElementType.ACTION_LINK%>"
destinationStageID="<%=EdicaoPedidoCreditacaoUC.class.getSimpleName()%>"
destinationStageParameters="pedidoID=${stage.pedido.id}&newTipoID=$[id]">
    <dif:additionalColumn attribute="<%=TipoPedidoCreditacao.Fields.VALOR%>"
dataType="<%=ColumnDataType.MONEY_EURO%>" width="100px"/>
</dif:listPicker>



CalcField: TitleDescriptionCalcField
Para conventionar situações em que temos um título e uma descrição para apresentar na mesma coluna como calcField foi criado este calcfield. Para facilitar e conventionar o visual.
// Description calcField
response.addCalculatedField("desc",
new TitleDescriptionCalcField(FileBundleFile.Fields.TITLE, FileBundleFile.Fields.DESCRIPTION,
true));
image.png



Nova Tag SummaryRightContentArea
Para situações em que queremos aproveitar a área a direita de um dif:summary que normalmente desaproveita muito espaço temos agora esta nova tag.
Vejam aqui conjugada com o novo layout disponível no dif:documentDownload
<dif:summaryRightContentArea>
<dif:documentGenerator documentID="${stage.pedido.idDocComprovativo}" description=""
layout="<%=Download.RIGHT_BOX_LAYOUT%>"/>
</dif:summaryRightContentArea>

image.png


Novo parâmetro suffix na dif:column
Para adicionar unidades ou outras necessidades.
Não utilizar para adicionar moeda para campos de currency nem percentagem. Para isso existem tipos específicos de colunas a usar.
<dif:column attribute="<%=FileType.Fields.MAXFILESIZE%>" width="80px" suffix="Kb"/>


JSONResponseDataSetGrid: addDefaultValueForNewRecords
Muitas vezes numa grid que trata dos inserts de forma automática temos que interceptar os inserts no onAjax só para colocar valores obrigatórios nos campos.
Para isso passa a existir este novo método.
O JSONResponse passaráa colocar estes valores de forma automática no registo qando for feito um insert de um novo registo.
response.addDefaultValueForNewRecords(FileBundleFile.FK().fileBundle().ID(), this.fileBundleID);



JSONResponseDataSetGrid: setAfterInsertTrigger
Por vezes precisamos fazer ações após um insert/update/delete serem realizados de forma automática por um JSONResponseDataSetGrid.
Para isso teriamos que interceptar essas ações e manualmente as realizar ou orquestrar a chamada ao DataSetExecutor.
Passa a ser possível definir uma espécie de trigger num pedido específico para essas necessidades
@OnAJAX("fileBundles")
public IJSONRawResponse getFileBundles() throws DataSetException
{
JSONResponseDataSetGrid<FileBundle> response =
new JSONResponseDataSetGrid<FileBundle>(FileBundle.getDataSetInstance());
response.setHandleRESTActions(true, true, true, true, null);
response.setAfterInsertTrigger(new IJSONResponseAfterRecordChangeTrigger<FileBundle>()
{
@Override
public void doAfterAction(FileBundle record)
{
FileBundleEditor.allowEditFileBundleID(FileBundlesExplorer.this.context.getSession(), record.getId());
}
});

return response;
}



Definição de CalcFields em Tags.
Para implementação de Tags de negócio on features de DIF passou a ser possível numa Tag de uma coluna de uma grid definir o(s) clacFields que sejam necessários.
Possibilita assim isolar toda a funcionalidade desejada num único ponto (a tag) o que torna o seu uso muito mais intuitivo e fácil. Para lá de mais fácil de manter.

// Defined the calc field
this.getGridTag().addCalcField(this.getAttribute() + "Calc",
new FileBundleEditorCalcField(this.getDIFSession(), this.getAttribute(), this.getRefreshFunction(),
this.getLanguage(), this.allowNewBundle, this.allowChangeToAnotherBundle, this.twoLineLayout,
updateCode.toString()));



Window com layout clean
Num processo ainda transitório passou a estar disponível um novo tipo de dialog. O layout clean.
Este pretende ser o início da implementação deste tipo de layout como o default para todos os gerados pela DIF.
É similar ao dialog de login da DIF.
Para já não possibilita navegação nem link de fechar a janela dado que não tem a área do title. E obriga ao uso de um titlePanel para definir o titulo.
No futuro os paineis “normais” serão todos migrados para este tipo quando tiver todas as fetures.
Dado que não possibilita mover a janela, ele centra-se no ecran sempre que o ecran muda de dimensão. É possível fazer resize.
No exemplo abaixo vemos ainda o uso do titlePanel e subTitlePanel que são também novas tags.

<dif:dialog id="windowFiles" title="${messages.editBundleFiles}" panelContainer="true" width="900" height="700"
cleanLayout="true">
<dif:titlePanel><span id="bundleSummary"></span>
<dif:subTitlePanel actionID="bundleSummarySubTitleAction" actionLabel="toReplace"
actionJS="changeBundleState();">
<span id="bundleSummarySubTitle"></span></dif:subTitlePanel>
</dif:titlePanel>
<dif:grid id="gridFiles" ajaxEvent="files" showRowLines="false" showHeader="false" showStripeRows=false...
image.png



HibernateUtil.getOrLazyLoad
Muitas vezes recebemos um objeto, queremos obter uma relação dele e não sabemos se está ou não carregada. E podemos ter o problema do LazyLoad.
Umas vezes por segurança lemos em nova query o que precisamos (e podia já lá estar logo não ser necessário) ou alteramos a query de origem para já ter esse campo, podendo não ser o único local ou ser necessário raras vezes logo não sendo a opção mais performant.
Uma nova alternativa prática é este método. Ele valida se a relação está carregada e se estiver devolve o valor. Se não, carrega o objeto mas sem ocorrer o erro do lazyLoad, utilizando uma nova sessão se necessário.
public boolean canInvalidate(FileBundleInstanceFile fileInstance)
{
// TODO: Viegas: WIP: Workflow: Tratar das ACLs
FileBundleFile fileBundleFile =
HibernateUtil.getOrLazyLoad(fileInstance.getFileBundleFile(), FileBundleFile.class);
FileBundleInstance fileBundleInstance =
HibernateUtil.getOrLazyLoad(fileInstance.getFileBundleInstance(), FileBundleInstance.class);

return !fileInstance.isIsInvalid() && fileInstance.getDocumentId() != null &&
fileBundleFile.isHasValidation() && fileBundleInstance.isIsOpen();
}

Irei mandar mais dois emails com features que merecem mails dedicados.

Um abraço.

Com os melhores cumprimentos,



Pedro Viegas
Director Departamento de Desenvolvimento
(Development Manager)
Digitalis Informática Lda
R&D: http://development.digitalis.pt


Pedro Viegas

unread,
Sep 16, 2020, 5:53:08 AM9/16/20
to DIF2 Mailing list
Bom dia,

Segue mais uma lista de novidades dos últimos tempos.
Passaram-se 2 meses desde o último mail destes e por isso várias destas novas funcionalidades á as tem disponíveis à algum tempo como vos tenho dito aqui e ali.
Tudo isto está comited no development da DIF/SigesSrv.

Vamos à lista:

EncryptDataCalcField
Várias vezes temos informação sensível para passar via URL. Um bom exemplo disto é passar um código de UC por exemplo. Numa etapa temos uma lista de UCs devidamente filtradas por regras de acesso de negócio, e depois queremos passar à próxima etapa a UC escolhida. E na etapa de destino temos que validar novamente se tem acesso à mesma ou não.
Uma alternativa a passar parâmetros em texto aberto e ter que validar em cada ponto é passar os parâmetros encriptados.
Para isso foi criado um novo calcField que pode passar um valor com os parâmetros todos desejados de forma encriptada.

Envio:
JSONResponseDataSetGrid<GenericBeanAttributes> response;
response = new JSONResponseDataSetGrid<GenericBeanAttributes>(...);
response.addCalculatedField("encryptedParamName", new EncryptDataCalcField("field1", "field2"));
Leitura:
JSONObject infoReceived = EncryptDataCalcField.decode(this.encryptedParamName);
this.field1 = infoReceived.getString("field1");
this.field2 = infoReceived.getString("field2");

Melhorias automáticas nas Grids
Os campos das grid passaram a prever mais um conjunto de situações:
  1. Todas as colunas com largura proporcional (não px) passa a ter um mínimo de 70px de largura
  2. Todos os títulos de colunas passa a fazer WordWrap ao invés de truncar visualmente
  3. Quando alinhamos uma coluna à direita o título passa a manter-se à esquerda

Desenvolver componentes (Tags) através de stages
À muito tempo que vamos falando de poder fazer isto. Na prática o desejado é desenvolver uma stage normalíssima com parâmetros de passagem com uma dada funcionalidade.
Depois poder declarar a mesma como um componente que possa ser usado dentro de uma página e que se integre na mesma para lá de um include JSP.
Já é possível.

Foi criada a classe componente base para esta necessidade: AbstractComponentBuildOnStage
Esta poderá ser estendida normalmente. Vai ter dois métodos base:
  • buildDefinition: Onde será indicada a stage a incluir e quais os parâmetros a passar
  • customDoEndTag: Onde poderemos se quisermos fazer algum controle mais fino antes e depois da inclusão da stage como componente. Se for apenas incluir passando os parâmetros não é preciso sequer implementar este método
Vejamos um exemplo:
(várias partes omitidas, vejam no código da DIF o resto se necessário)
public class FileBundleEditor extends AbstractComponentBuildOnStage
{
@Override protected StageToCall buildDefinition()
{
StageToCall stageToCall = new StageToCall(FILE_BUNDLE_EDITOR_STAGE_ID)
.addParameterIfNotNull("businessProcessTypeID", this.businessProcessTypeID)
.addParameterIfNotNull("fileBundleID", this.fileBundleID)
.addParameterIfNotNull("refreshFunction", this.refreshFunction)
.addParameterIfNotNull("businessProcessTypeID", this.businessProcessTypeID);

if (this.getFileBundleID() != null)
{
stageToCall.addParameterIfNotNull("singleBundleEditor", true);
allowEditFileBundleID(this.getDIFSession(), this.getFileBundleID());
}

return stageToCall;
}

@Override protected void customDoEndTag()
throws JspException, ConfigurationException, IdentityManagerException, UnsupportedEncodingException
{
ConfigurationPanel configurationPanel = this.findAncestorWithClass(ConfigurationPanel.class);

// On configuration panels in popup mode we won't render this component.
// It would not be called in most cases, and the panel binding bellow would have to be synced
// to the window rendering and not ON_LOAD time
if (configurationPanel == null || !configurationPanel.isPopupMode())
{
super.customDoEndTag();

if (this.getFileBundleID() != null)
{
IPanelContainer parentPanelContainer = this.findAncestorWithClass(IPanelContainer.class);

if (parentPanelContainer != null)
{
AbstractDIFTag parentPanelContainerTag = (AbstractDIFTag) parentPanelContainer;

String parentID = parentPanelContainerTag.getId();
StringBuilder b = new StringBuilder();
b.append("Ext.getCmp('" + parentID + "').setLayout(new Ext.layout.BorderLayout());\n");
b.append("Ext.getCmp('" + parentID + "').add(Ext.getCmp('gridFiles" + this.getFileBundleID() +
"'));");

this.getContributions().addContribution(
new JavaScriptDocumentContribution(ScriptletScope.ON_LOAD, b.toString()).setOrder(2000));
}
}
}
}

O build definition apenas indica a stage e os parâmetros a passar.
O customDoEndTag valida por exemplo de o componente deve ou não incluir a stage e se sim, no final adiciona código JavaScript para integrar os componentes paineis gerados num eventual panel pai que exista.

Resultado:
image.png
Outro caso de uso:
(FileBundleInstanceEditor)

image.png
image.png


SessionPersistentStageStorageArea
Esta feature cria a possibilidade de criar áreas dentro da sessão específicas de conteúdos persistentes.
Nasceu da necessidade da Tag Grid e Colum passarem objetos em RAM da sua execução para o evento onAjax que lhes devolverá os dados.
Nomeadamente para que uma Tag possa declarar um CalcField que o OnAjax vai preencher usando o objeto de CalcField criado pela Tag Column customizado.
A estratégia passa por criar um mapa de áreas destas na sessão com um ID associado. O ID é aleatório para que possamos ter duas instâncias do mesmo serviço/stage por exemplo para editar duas registos diferentes, e desta forma cada um ter a sua configuração.
Dado que esta necessidade pode causar uma longa cadência de objetos, os IDs que deixam de ser referenciados nos pedidos acabam por ser descartados quando a fila enche.
A gestão é totalmente automática.

Usa-se assim:
SessionPersistentStageStorageArea storageArea = context.getSessionPersistentStageStorageArea();
storageArea.put("var1", objectInstance)
;
O objeto storage é um mapa por isso é um simples put/get.
Os componentes estão preparados para enviar o ID da storage em uso quando existentes em contexto. As forms, as grids, etc.

DatasetCacheManager
Passa a existir a possibilidade de fazer cache a um DataSet.
Ver o BoletimMatricula.java como um bom exemplo.

Exemplos:
String sql = "SELECT ... FROM ...";
SQLDataSet ds = new SQLDataSet(this.siges.getSession(), sql, SQLDialect.ORACLE, true);

return Option.listToOptions(ds.query().asList(), TableFormaConhecInst.Fields.ID,
TableFormaConhecInst.Fields.DESCRICAO);
O último parâmetro do SQLDataSet indica se queremos ou não cache.
Se sim e criado um cache dos dados da query passada. Cada vez que façamos uso da mesma query ele recupera o cache e não executa a query à BD.
Todos os filtros e ordenações serão feito ao dataSet em RAM sem uso da BD.
Se houver diferentes SQL existirão diferentes caches para cada String única de SQL.
Regra óbvia, não aplicar filtros dinâmicos (de parâmetros passados à mesma query base) no SQL da Query. Caso contrário não será reutilizada uma cache única mas sim uma por cada valor/combinação de parâmetro(s) passado(s).

Existem opções equivalentes para ligar o cache para:
  • SQLDataSet
  • JSONResponseSQLDataSetComboBoxSIGESTranslation
  • HibernateDataSet
Os dois primeiros são baseados numa string de SQL. O seu funcionamento é muito similar.
O HibernateDataSet tem um pouco mais de inteligência necessária.
Na realidade o cache de HibernateDataSet é toda a tabela subjacente. Cuidado por isso com as tabelas escolhidas pela quantidade de RAM que pode vir a ser usada.
Se a um HibernateDataSet destes em que usemos cache, passarmos um elemento da lista abaixo, ele reverte para o modo normal de query à BD e não usa o cache:
  • joins
  • distinct
A gestão do tempo de vida deste cache é falada no ponto seguinte.

CacheManager
A DIF passa a normalizar entradas de cache e a sua gestão.
Passa a existir o IBusinessCache
/**
* The interface Business cache.
*/
public interface IBusinessCache
{
/** Clear cache. */
public void clearCache();

/** Gets app name. */
public String getAppName();

/** Gets cache date. */
public Date getCacheDate();

/** Gets cache description. */
public String getCacheDescription();

/** Gets cache name. */
public String getCacheName();

/** Gets expiration time. */
public long getExpirationTime();
}
Existem já várias entradas desta contribuição.
A DIF tem um interface na administração para gerir todos eles:

image.pngimage.png

Aqui podemos ver todos os caches declarados perante a DIF. Á quanto tempo foi feito o cache, e qual a periodicidade de refresh. Podemos ainda forçar o limpar do cache individualmente.
O cache para o DatasetCacheManager é o último da lista.

Novo parâmetro readonlyForUpdate nos form fields Text/Radio/Date/Combo (não multi select) para DetailsForms
Este parâmetro como o nome diz faz com que estes campos apenas estejam disponíveis para o insert de novos registos. E ficam readonly nos updates de registos existentes.

Finalmente, Forms dentro de Forms
Por vezes precisamos usar um componente que cria uma form dentro da declaração de uma outra form.
Exemplo: um form com uma grid dentro que tem um DetailsForm ou um FilterForm.
O código gerado não funcionaria porque as forms interiores eram criadas dentro da form principal o que não é suportado.

Estas forms passam a detectar que estão a ser geradas dentro de outros e renderizam o seu conteúdo apenas após a mesma. Normalmente o seu conteúdo é sempre renderizado dentro de um elemento Dialog ou Panel que agarra no conteúdo gerado e o move para o local correto.
Nada mudou na forma de uso, apenas passou a ser possível esta combinação.

Forms com autoWidth
Até aqui todas as forms da DIF são geradas em default ou seja com "display: block".
Isto tem esta consequência o que é ilustrado na imagem seguinte:

image.png

Portanto a form apanha toda a largura do ecran mesmo que o seu conteúdo fique pequeno e encostado à esquerda.
Mas depois os Actions usam esse espaço real da form para se alinharem o que não resulta nada bem.

A DIF passa a renderizar em display: inline-block para corrigir este problema resultando no seguinte:
image.png
Até aqui quase nem merecia estar nesta lista de festures. O problema é que se a form contiver Panels de Ext os mesmos não seriam renderizados corretamente. Razão pela qual este comportamento reverte para o anterior se existir um panel/grid/tree/etc dentro da form.

Métodos para ler/escrever relações nos Data de Hibernate
Uma necessidade recorrente de muito código longo e repetido é ler e setar uma relação opcional.
Temos que ver se é nulo para setar ou obter o ID. Temos que ler o objeto só para obter o ID. Por vezes vemos se é -1 o valor para assumir como nulo esse valor.
Tudo isto é padronizável e como tal o processo de Reveng do ORM passa a darnos métodos para o fazer de forma automática.

Vejam os exemplos do antes e depois:
this.projetoMontanteTotal = projeto.getVlMontanteTotal();

if (projeto.getTableProjSituacao() != null)
{
TableProjSituacao situacaoProjeto = projeto.getTableProjSituacao();
this.projetoIdSituacao = situacaoProjeto.getId();
}

if (projeto.getTableProjSitCand() != null)
{
TableProjSitCand situacaoCand = projeto.getTableProjSitCand();
this.projetoIdSitCand = situacaoCand.getId();
}
this.projetoMontanteTotal = projeto.getVlMontanteTotal();
this.projetoIdSituacao = projeto.getTableProjSituacaoId();
this.projetoIdSitCand = projeto.getTableProjSitCandId();
O equivalente para a escrita:
projeto.setVlMontanteTotal(this.projetoMontanteTotal);

if (this.projetoIdSituacao != null)
{
TableProjSituacao situacaoProjeto =
this.siges.getWEB_PROJETO().getTableProjSituacaoDataSet().get(this.projetoIdSituacao.toString());
projeto.setTableProjSituacao(situacaoProjeto);
}

if (this.projetoIdSitCand != null)
{
TableProjSitCand situacaoCand =
this.siges.getWEB_PROJETO().getTableProjSitCandDataSet().get(this.projetoIdSitCand.toString());
projeto.setTableProjSitCand(situacaoCand);
}
projeto.setVlMontanteTotal(this.projetoMontanteTotal);
projeto.setTableProjSituacaoProxyFromId(this.projetoIdSituacao);
projeto.setTableProjSitCandProxyFromId(this.projetoIdSitCand);


Com os melhores cumprimentos,



Pedro Viegas
Director Departamento de Desenvolvimento
(Development Manager)
Digitalis Informática Lda
R&D: http://development.digitalis.pt


Reply all
Reply to author
Forward
0 new messages