O projeto xMovaJS é um framework para criar aplicativos móveis usando JSON e JavaScript.
O xMova interpreta o modelo de dados para criar as telas do fluxo. No modelo define-se todas as entidades e seus campos. Em cada entidade e campo pode-se definir atributos e eventos.
O modelo é definido da seguinte forma:
Model JS:
{
app:{
id:7885,
name:'Nome do Aplicativo',
version:'1.0.53',
intVersion:37,
appCode:'L',
dynamicQuery:1,
serverUrl:'http://xmova-server-dev.simova.ws/serverjs/m',
thirdServerUrl:'http://url.do.app/',
screens:{
Main:{
type:'main', fields:['descricaoFilial'],
actions: {
Entidade1: {type:'Main', position:'Menu'},
Sincronizar: {event:'sync()'},
Limpar: {event:'dropDbAndExit()'},
Sair: {type:'Back', position:'Menu', event:'exit()'}
}
}
}
},
entities:{
Auth:{
fields:{
idFilial: {fk:'Filial'}
}
},
AuthInput:{
fields:{
id:{},
idFilial: {fk:'Filial'},
descricaoFilial: {type:'text', autoFill:'this.idFilial.descricao'}
}
},
Teste:{
fields:{
id: {},
campo1: {}
}
},
Filial:{
label:'Filial',
fields:{
id: {},
descricao: {type:'text'}
}
}
},
lang:{
AuthInput:'Configuração',
AuthInput_descricaoFilial:'Filial'
}
}
Em cada entidade pode-se definir o tipo de sincronismo. Quando o app faz um sync com o servidor cada entidade será analisada para verificar se os dados dela serão enviados ou recebidos do servidor, ou se a entidade será ignorada no sync.
sync:'in': O servidor retornará os dados desta entidade em todos os sync Foreground.
sync:'out': Em todos os Sync, Foreground ou Background, os dados dessa tabela serão enviados ao servidor.
sync:['in', 'install']: O servidor retornará os dados desta entidade apenas no primeiro sync ANTES da instalação do app. Serve para fluxos em que seja necessário exibir lista de opções durante o processo de configuração inicial do fluxo.
Model JS:
Sincronizar sync():
{event:'sync()'}
Sair exit():
{event:'exit()', type:'Back'}
Limpar dropDbAndExit():
{event:'dropDbAndExit()'}
afterFinish: Evento chamado após finalizar o preenchimento de uma entidade. Quando o evento terminar, o fluxo precisa chamar crud.afterFinish()
App JS:
Nome_da_Entidade.afterFinish: crud
... ...
crud.afterFinish();
afterSave: Evento chamado após finalizar o preenchimento de uma entidade.
beforeSave: Evento chamado antes de salvar um registro da entidade.
onOpenMenu: Evento chamado ao abrir um menu. Quando o evento terminar, o fluxo precisa chamar crud.showMenu().
App JS:
Nome_da_Entidade.onOpenMenu_: crud
... ...
crud.showMenu();
onShowInfo: Evento usado para alterar a barra de info na entidade e em seus campos, conforme eles são exibidos. Caso seja passado null para o afterOnShowInfo, a barra não é exibida.
App JS:
Nome_da_Entidade.onShowInfo_: mgrs
... ...
mgrs.afterOnShowInfo('Texto informativo');
Note que quando os eventos são em background (simbolizados por terem um underline no fim do nome do evento), o retorno deve ser feito através de mgr.resume(‘valor’);
autoFill: Preenchimento automático do campo com o valor retornado. Se tiver autoFill, pula a fase Fill.
App JS:
Nome_da_Entidade.nome_do_campo.autoFill: mgr, crud
return 1000;
Nome_da_Entidade.nome_do_campo2.autoFill_: mgr, crud
mgr.resume(1000);
condition: Condição de preenchimento do campo. Se a condição retornar “false”, passa ao proximo campo. Se não for um método com callback, o retorno padrao é “true”, e o campo deve ser preenchido.
App JS:
Nome_da_Entidade.nome_do_campo.condition: mgr, crud
if (*condição de preenchimento*) {
return true;
} else {
return false;
}
Nome_da_Entidade.nome_do_campo.condition_: mgr, crud
if (*condição de preenchimento*) {
mgr.resume(true);
} else {
mgr.resume(false);
}
defaultValue: Pré-preenchimento do campo com o valor retornado. Se tiver defaultValue, o valor é apresentado, e pode ser editado.
App JS:
Nome_da_Entidade.nome_do_campo.defaultValue: mgr, crud
return 1000;
Nome_da_Entidade.nome_do_campo.defaultValue_: mgr, crud
mgr.resume(1000);
validation: Validação do valor preenchido para campo. Se a validação retornar “false”, não continua, volta ao campo anterior. Se não for um método com callback, o retorno padrao é “true”, a validação foi com sucesso.
App JS:
Nome_da_Entidade.nome_do_campo.validation: mgr, crud, value
if (entry) {
return false;
}
Nome_da_Entidade.nome_do_campo.validation_: mgr, crud, value
if (entry) {
mgr.resume(false);
}
onLoadSrcHtml: Preparação do Html a ser exibido.
App JS:
Nome_da_Entidade.nome_do_campo.onLoadSrcHtml: mgr, crud, entry
var html = '<html><head> <meta charset="UTF-8"> <style> table {font-family:Arial, Helvetica, sans-serif; font-size:12px; text-shadow: 1px 1px 0px #fff; background:#f6f6f6; border:#ccc 1px solid; -moz-border-radius:3px; -webkit-border-radius:3px; border-radius:3px; -moz-box-shadow: 0 1px 2px #d1d1d1; -webkit-box-shadow: 0 1px 2px #d1d1d1; box-shadow: 0 1px 2px #d1d1d1; width: 100%; display: table; } table tr.odd { font-size:13px; background: #eaebec; background: -webkit-gradient(linear, left top, left bottom, from(#f8f8f8), to(#eaebec;)); background: -moz-linear-gradient(top, #f8f8f8, #eaebec;); } </style> </head> <body> <table> ';
model.UltimoApontamento.dao().last({delegate:this, callback:function(entry) {
if (entry) {
html += '<tr class="odd"><th colspan="4"> Último Apontamento </th></tr>';
html += '<tr> <td>OS:</td> <td>' + entry.os + '</td> </tr>';
html += '<tr> <td>Operação:</td> <td>' + entry.operacao + '</td> </tr>';
html += '<tr> <td>Data:</td> <td>' + entry.data + '</td> </tr>';
} else {
html += '<tr><td colspan="4">Não encontrou último apontamento!</td></tr>';
}
}});
html += '</table> </body> </html>';
Nome_da_Entidade.nome_do_campo.onLoadSrcHtml_: mgr, crud, entry
var html = '<html><head> <meta charset="UTF-8"> <style> table {font-family:Arial, Helvetica, sans-serif; font-size:12px; text-shadow: 1px 1px 0px #fff; background:#f6f6f6; border:#ccc 1px solid; -moz-border-radius:3px; -webkit-border-radius:3px; border-radius:3px; -moz-box-shadow: 0 1px 2px #d1d1d1; -webkit-box-shadow: 0 1px 2px #d1d1d1; box-shadow: 0 1px 2px #d1d1d1; width: 100%; display: table; } table tr.odd { font-size:13px; background: #eaebec; background: -webkit-gradient(linear, left top, left bottom, from(#f8f8f8), to(#eaebec;)); background: -moz-linear-gradient(top, #f8f8f8, #eaebec;); } </style> </head> <body> <table> ';
model.UltimoApontamento.dao().last({delegate:this, callback:function(entry) {
if (entry) {
html += '<tr class="odd"><th colspan="4"> Último Apontamento </th></tr>';
html += '<tr> <td>OS:</td> <td>' + entry.os + '</td> </tr>';
html += '<tr> <td>Operação:</td> <td>' + entry.operacao + '</td> </tr>';
html += '<tr> <td>Data:</td> <td>' + entry.data + '</td> </tr>';
} else {
html += '<tr><td colspan="4">Não encontrou último apontamento!</td></tr>';
}
}});
html += '</table> </body> </html>';
mgr.resume(html);
onEntityListQueryPrepare: Preparação da lista filtrando por relacionamento entre entidades.
App JS:
Nome_da_Entidade.nome_do_campo.onEntityListQueryPrepare: mgr, crud, queryParams
queryParams.sql = 'SELECT t.* FROM Entidade1 AS e1 JOIN Entidade2 AS e2 on e1.id = e2.id_campo_entidade_1 WHERE e2.id_campo_entidade_2=' + this.id_campo_entidade_2.id;
onFilterListEntry: Preparação da lista filtrando registros por campo.
App JS:
Nome_da_Entidade.nome_do_campo.onFilterListEntry: mgr, crud, entry
if (entry.campo_a_ser_filtrado) {
return false;
}
onListEntries: Preparação de uma nova lista com os registros desejados.
App JS:
Nome_da_Entidade.nome_do_campo.onListEntries: mgr, crud, delegate, callback
model.Entidade2.dao().list({delegate:this, callback:function(list1) {
var list2 = [];
for (var i = 0; i < list1.length; i++) {
var entry = list1[i];
if (!entry.campo_a_ser_filtrado) {
list2.push(entry);
}
}
callback.call(delegate, list2);
}});
confirmCreate: Confirma criação da entidade. Se não houver o atributo confirmCreate, será mostrada a mensagem para confirmar a criação. Para retirar a mensagem, utilizar confirmCreate:0.
confirmFinish: Confirma finalização da entidade. Se não houver o atributo confirmFinish, será mostrada a mensagem para confirmar a finalização. Para retirar a mensagem, utilizar confirmFinish:0.
descriptionFields: Descrição do campo. Se não houver o atributo descriptionFields, a descrição é preparada com o primeiro campo INT que nao seja o ID se houver, mais o primeiro campo String se houver.
extName: Substitui o nome da entidade para enviar ao servidor.
info: Mostra informação no topo da tela. Utilizar info:’Nome_da_Entidade’, E na entidade adicionar o campo info: {type:’text’} que conterá a mensagem a ser exibida no topo da tela.
label: Texto que será exibido ao chamar o campo ou entidade.
verifyRequired: Verifica se tem registro no banco de dados. O atributo verifyRequired deve ser adicionado a entidade sync:out que será preenchida, para verificar se há alguma entidade do tipo sync:in que está sem dados. Nas entidades sync:in que devem ser verificadas, deve ser adicionado o atributo required:1. Caso alguma entidade obrigatoria esteja vazia, exibe uma mensagem ao usuario e impede o fluxo de prosseguir.
Caso o tipo não seja definido, por default será considerado um campo númerico.
type:'back': Utilizar com na tela Main junto com o evento para sair do aplicativo event:'exit()'.
type:'main': Utilizar com na tela para definir a entiade do tipo Main.
type:'custom': HTML. Utilizar o evento onLoadSrcHtml para preparação o HTML a ser exibido.
type:'lines': Campo de texto com multiplas linhas.
type:'text': Campo de texto.
position: Posição do campo na tela Main. Pode ser definida como: ‘Menu’, ‘Bar’, ‘Hidden’.
addFixedOptions:1: Usada para definir que as opções fixas do Crud menu devem ser adicionadas após esta opção.
fill:'no': Pula o preenchimento do campo.
fill:'finish': Pede o preenchimento do campo no processo de finalização da entidade.
fill:'manual':
Campos com nome Padrão
id: Não é pedido o preenchimento para o usuário. É um campo númerico com valor incremental para cada entidade.
date: Se o campo inicia com “date”, não é pedido o preenchimento para o usuário. É um campo númerico cujo o valor será a data atual em milissegundos.
onlineFlagInicial: Se o campo inicia com “onlineFlag”, não é pedido o preenchimento para o usuário. O campo númerico será preenchido no momento do sincronismo com os valores: 1 (online), 0 (offline).
uuid: Gera identificador único.
decimal: Defini o número de digitos da parte fracionária (à direita do separador).
formatThousand: Formatação de milhar no campo numérico. Para retirar a formatação, adicionar o atributo formatThousand:0.
maxLength: Defini o tamanho máximo de casas decimais
upperCase: Formatação do texto com letras maiusculas.
label: Defini o texto que será apresentado ao mostrar o campo.
list: Defini o campo como um item de menu que deve ser listado no menu da entidade em questão, após a criação da mesma. O atributo list:1 deve ser usado indicando a entidade a ser preenchida com fk:nome_da_entidade.
search: Defini o campo que será usado para busca numa lista.
Model JS:
Nome_da_Entidade :{
sync:'out',
extName:'Apontamento',
info:'UltimoApontamento',
label:'Apontar',
confirmCreate:0,
confirmFinish:0,
verifyRequired:1,
fields:{
id: {},
date: {},
onlineFlag: {},
idSeq: {uuid:1},
campo_nao_pedido: {fill:'no'},
campo_texto: {type:'text', upperCase:1, label:'Texto simples'},
campo_texto_multilinha: {type:'lines', fill:'manual',defaultValue:'texto inicial'},
campo_validacao_online: {onlineValidate:{name:'Metodo_de_validacao', block:1}},
campo_inteiro_ou_long: {autoFill:10},
campo_fixed_point: {onlyConfirm:true, saveAppState:true, label:'Confirma chegada?'},
campo_decimal: {decimal:2, maxLength:9, formatThousand:0},
campo_assinatura: {fk:'Binary', signature:1, condition:'this.campo_inteiro_ou_long > 10'},
campo_foto: {fk:'Binary', picture:1},
campo_foto: {fk:'Binary', list:1},
campo_html: {type:'custom'},
campo_entidade: {fk:'Outra_Entidade', fill:'finish'}
},
crudMenuActions:{
campo: {}
}
},
Outra_Entidade:{
sync:'in',
label:'Outra Entidade',
required:1,
descriptionFields:['codigo','descricao'],
fields:{
id: {},
codigo: {search:1, label:'código'},
descricao: {type:'text'}
}
}
Formata a data(UTC) em milliseconds para String. Pode ser chamado direto pelo app e model.
formatUTCTimeDate: Retorno no padrao: HH:mm:ss DD/MM/YYYY
App JS:
var dataStr = Dates.formatUTCTimeDate(dateField);
formatUTCDateTime: Retorno no padrao: DD/MM/YYYY HH:mm:ss
App JS:
var dataStr = Dates.formatUTCDateTime(dateField);
formatUTCDate: Retorno no padrao: DD/MM/YYYY
App JS:
var dataStr = Dates.formatUTCDate(dateField);
formatUTCTime: Retorno no padrao: HH:mm
App JS:
var dataStr = Dates.formatUTCTime(dateField);
Model JS:
Nome_da_Entidade:{
sync:'out',
fields:{
id: {},
fixedPoint: {onlyConfirm:true, saveAppState:true, label:'Confirma chegada?'}
}
}
Model JS:
Nome_da_Entidade:{
sync:'out',
info:'UltimoApontamento',
fields:{
id: {},
campo_crud: {fk:'ApontamentoServico', list:1, label:'Serviço', info:'UltimoApontamento'}
}
}
Campos enviados: long, lat, alt, gpsTime, time
Model JS:
Nome_da_Entidade:{
sync:'out',
fields:{
id: {},
location: {fk:'Location'}
}
}
Model JS:
Nome_da_Entidade:{
sync:'out',
fields:{
id: {},
binaryId: {fk:'Binary', picture:1}
}
}
Model JS:
Nome_da_Entidade:{
sync:'out',
fields:{
id: {},
binaryId: {fk:'Binary', signature:1}
}
}
crud: gera um novo registro da entidade passada como atributo
App JS:
crud(entity, delegate);
entity: entidade do modelo para criar o registro
delegate: quem irá executar o callback(onCrudFinish)
Exemplo:
Main.Boletim:
crud(model.Boletim);
crudFields: preenche apenas os campos da entidade que foram especificados em ‘fieldsNames’
App JS:
crudFields(entity, entry, fieldsNames, delegate, callback);
entity: entidade do modelo para criar o registro
entry: registro a ser preenchido, ou null se quiser que seja criado um registro
fieldsNames: array de nomes que deseja que sejam preenchidos Exemplo: ['id', 'date']
delegate: quem irá executar o callback
callback: função que deverá ser chamada quando o crud finalizar o preenchimento, pode ser definido também no delegate com o listener de nome onCrudFinish
Exemplo:
Boletim.onOpenMenu_: crudController
this.onCrudFinish= function(entry, crud) {
crudController.showMenu();
};
var entry = {name: 'test'};
var c = crudFields(model.entity, entry, ['id', 'date', 'onlineFlag', 'location'], this);
installData: Deve ser escrido alternando aspas duplas para os valores e aspas simples para o sql.
Model JS:
installData:[
'INSERT INTO Nome_da_Entidade VALUES(1,"descricao_do_campo")'
]
É possivel modificar os titulos, labels e mensagens padrão do fluxo, para isso é usado o atributo lang do sistema.
Exemplo de entidades:
Model JS:
Turno:{
sync:'out', extName:'Boletim',
fields:{
id: {},
idSeqBoletim: {uuid:1},
dateAbertura: {},
onlineFlagInicial: {},
location: {fk:'Location'},
apontamentoOS: {fk:'ApontamentoOS', list:1, label:'OS'},
apontamentoTG: {fk:'ApontamentoTG', list:1, label:'Tarefas Gerais'}
dateFechamento: {fill:'finish'},
onlineFlagFinal: {fill:'finish'},
}
},
ApontamentoDeslocamento:{
sync:'out',
fields:{
id: {},
apontamentoOS: {fk:'ApontamentoOS'},
idSeqBoletim: {type:'text', autoFill:'this.apontamentoOS.idSeqBoletim'},
idSeqOS: {type:'text', autoFill:'this.apontamentoOS.idSeqOS'},
idOS: {type:'text', autoFill:'this.apontamentoOS.idOS'},
idSeqApontamento: {uuid:1},
dateSaida: {},
onlineFlagSaida: {},
location: {fk:'Location'},
idModal: {fk:'Modal'},
idTipoTransporte: {fk:'TipoTransporte', label:'Tipo de Transporte'},
fornecedor: {fk:'Fornecedor', label:'Fornecedor', condition:'this.idTipoTransporte.fgFornecedor == 1'},
placa: {type:'text', upperCase:1, label:'Placa', condition:'this.idTipoTransporte.fgPlaca == 1', maxLength:250},
kmAnterior: {decimal:2, maxLength:9, label:'Km Anterior', formatThousand:0},
kmSaida: {decimal:2, maxLength:9, label:'Km Saída', formatThousand:0, condition:'this.idTipoTransporte.fgKM == 1', validation:'this.kmSaida > 0'},
idLocalSaida: {fk:'Local', label:'Local Saída'},
chegou: {onlyConfirm:true, saveAppState:true, label:'Confirma chegada?'},
idLocalChegada: {fk:'Local', label:'Local Chegada'},
kmChegada: {decimal:2, maxLength:9, label:'Km Chegada', formatThousand:0, condition:'this.idTipoTransporte.fgKM == 1'},
dateChegada: {condition:'this.chegou == 1'},
onlineFlagChegada: {condition:'this.chegou == 1'},
opcoesEscolhaOS: {fk:'OpcoesEscolhaOS', label:'Finalizar deslocamento e:'}
}
},
Exemplo de Lang:
Model JS:
lang:{
ApontamentoDeslocamento:'Deslocamento',
ApontamentoDeslocamento_idModal:'Modal',
ApontamentoDeslocamento_idTipoTransporte:'Tipo de transporte',
ApontamentoDeslocamento_chegou_input:'Confirma Chegada?',
ApontamentoDeslocamento_chegou_confirm:'Sim',
Turno_MsgCreateConfirm:'Confirma abertura de turno?',
Turno_MsgFinishConfirm:'Confirma fechamento de turno?',
Turno_LblFinish:'Finalizar/Pausar OS',
VerifyEmptyMessage:'O apontamento não poderá ser realizado pois o cadastro de <entities> está vazio.',
VerifyEmptyMessagePlural:'O apontamento não poderá ser realizado pois os cadastros de <entities> estão vazios.',
codigoTecnico_MsgOnlineValidateFail:'Matricula inválida para filial selecionada.'
}
Titulo da Entidade: Usado para alterar o titulo do menu da entidade, similar a colocar o atributo ‘label’ na entidade.
Model JS:
NomeDaEntidade:'Titulo da Entidade',
Titulo do Campo: Usado para alterar o titulo de um campo da entidade, exibido conforme este é preenchido. Similar a colocar o atributo ‘label’ no campo.
Model JS:
NomeDaEntidade_campo:'Campo da Entidade',
Confirmação de criação: Usado para alterar o texto na tela de confirmação de criação da entidade.
Model JS:
NomeDaEntidade_MsgCreateConfirm:'Confirma a criação da entidade?',
Confirmação de finalização: Usado para alterar o texto na tela de confirmação de finalização da entidade.
Model JS:
NomeDaEntidade_MsgCreateConfirm:'Confirma a finalização da entidade?',
Label de finalização: Altera o texto da opção de “Finalizar Entidade” do menu.
Model JS:
NomeDaEntidade_LblFinish:'Fechar menu de Entidade',
Falha no sincronismo: Altera o texto exibido quando ocorre algum erro no sincronismo de dados com o servidor. Usado junto com o atributo required das entidades.
Model JS:
VerifyEmptyMessage:'O apontamento não poderá ser realizado pois o cadastro de <entities> está vazio.',
VerifyEmptyMessagePlural:'O apontamento não poderá ser realizado pois os cadastros de <entities> estão vazios.',
Falha na validação: Altera o texto exibido num toast quando ocorre um erro na validação de um campo.
Model JS:
campoEntidade_MsgOnlineValidateFail:'Opção invalida selecionada.'
O xMova pode fazer validação online dos valores inseridos nos campos de uma entidade. Basta adicionar ao campo o atributo onlineValidate.
Model JS:
campo: {onlineValidate:{name:’Metodo_de_validacao’, block:1}}
O sistema de terceiro deve retornar os seguites status: