News & Events
Tutorial: Criando meu app step-by-step – Sincronismo – Parte X – Final
- 6 de julho de 2015
- Posted by: Adriano Santos
- Category: delphi Desenvolvimento embarcadero Notícias Tutoriais XE8 [Tutorial]

Chegamos a última parte de nosso Tutorial: Criando meu app step-by-step. Até agora vimos como fazer toda a parte de criação do aplicativo desde a concepção, passando por layout até o download de uma tabela inteira vinda de um servidor DataSnap apontado para uma base de dados em Firebird 2.5. Muitos leitores nos escreveram solicitando essa última parte que seria a mais importante de todas: o Sincronismo. Nesse tutorial nós veremos como fazer a exclusão, atualização e inserção de novos títulos no servidor Firebird usando o aplicativo Mobile.
Na parte anterior vimos:
- Comandos ArrayDML para rápida inserção no banco;
- Ajustes na liberação de memória de objetos FORM;
Como mencionado veremos a seguir:
- Como criar um método para excluir registros do aplicativo móvel e do servidor;
- Como enviar novos registros do Mobile para o Servidor;
- Como criar um ApplyUpdates dos dados locais para o servidor;
Requisitos:
Tutorial: Criando meu app step-by-step – Sincronismo – Parte X
Antes de iniciarmos nosso tutorial, vamos relembrar algumas partes importantes para que possamos dar continuidade, começando pelo nosso DataModule na aplicação Mobile. Nós temos em nosso DM apenas um componente FDConnection para acesso ao nosso banco de dados SQLite e duas queries. Uma para listar os títulos da locadora e outra para efetuar pesquisas. Em nosso evento OnCreate, nós atribuímos as devidas configurações de acesso ao banco local e então conectamos a ele. Podemos ver isso na Figura 1 e em nossa Listagem 1.
Figura 1. DataModule Mobile
procedure TDM.DataModuleCreate(Sender: TObject); var sPath: String; begin with fdcConexao do begin {$IF DEFINED(IOS) OR DEFINED(ANDROID)} Params.Values['DriverID'] := 'SQLite'; Params.Values['OpenMode'] := 'ReadWrite'; try sPath := TPath.Combine(TPath.GetDocumentsPath, 'Locadora.s3db'); Params.Values['Database'] := TPath.Combine(TPath.GetDocumentsPath, 'Locadora.s3db'); Connected := True; except on E: Exception do begin ShowMessage(E.Message); end; end; {$ELSE} try Params.Values['Database'] := '$(DB_LOCADORA_SQLITE)'; Connected := True; except on E: Exception do begin ShowMessage(E.Message); end; end; {$ENDIF} end; end;
Listagem 1. OnCreate do DataModule
Até o presente momento, nosso aplicativo é capaz apenas de listar os títulos já disponíveis no aparelho celular assim como fazer um breve sincronismo trazendo os títulos do servidor para o aparelho celular através de um botão Sincronizar presente na tela principal.
Em nossa janela de títulos, nós preparamos para que ela possa mostrar os títulos em uma lista e ao tocar nela podemos ver seus detalhes. Em frmTitulos temos um TabControl com duas abas, tbitemTitulos e tbitemDetalhe como visto na Figura 2.
Figura 2. Design da tela de Títulos
Na aba Detalhes faremos a inclusão, alteração e exclusão de títulos da tabela em questão. Teremos que criar alguns métodos no servidor para que possamos trabalhar essas integrações.
Retornando a tela principal, nosso botão Sincronizar temos a função de buscar no servidor os dados atualizados e gravar no banco de dados local. Vejamos o código-fonte na Listagem 2.
procedure TfrmMain.spbSincronizarClick(Sender: TObject); var LDataSetList : TFDJSONDataSets; begin MessageDlg('Confirma sincronismo de dados?', TMsgDlgType.mtConfirmation, [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo], 0, procedure (const AResult: TModalResult) begin //Efetua o download da tabela TITULOS vinda do Servidor DataSnap LDataSetList := ModuloCliente.SrvServerMetodosClient.GetTitulos; //Prepara o MemoryTable temporário memTemporario.Active := False; //Fazemos um teste para verifica se realmente há DataSet no retorno da função Assert(TFDJSONDataSetsReader.GetListCount(LDataSetList) = 1); //Adicionamos o conteúdo do DataSet "baixado" ao Memory Table memTemporario.AppendData(TFDJSONDataSetsReader.GetListValue(LDataSetList, 0)); //Chamamos a rotina para incluir os dados recebidos na tabela Temporária, dentro da tabela no SQLite. AtualizarTitulos(memTemporario); end ); end; //Método de atualização procedure TfrmMain.AtualizarTitulos(AMemoryTable: TFDMemTable); const _INSERT = 'INSERT INTO TITULOS ' + '( ' + ' ID_TITULO , ' + ' TITULO , ' + ' SUBTITULO , ' + ' FOTO , ' + ' MINIATURA ' + ') ' + 'VALUES ' + '( ' + ' :ID_TITULO , ' + ' :TITULO , ' + ' :SUBTITULO , ' + ' :FOTO , ' + ' :MINIATURA ' + '); '; var intNumInserts : Integer; begin {Número de registros recebidos do servidor} intNumInserts := AMemoryTable.RecordCount; DM.qryAuxiliar1.Active := False; DM.qryAuxiliar1.SQL.Text := _INSERT; {Informamos a Query Auxliar qual o número de Inserts será efetuado} DM.qryAuxiliar1.Params.ArraySize := intNumInserts; AMemoryTable.First; while not AMemoryTable.Eof do begin DM.qryAuxiliar1.ParamByName('ID_TITULO').AsIntegers[AMemoryTable.RecNo-1] := AMemoryTable.FieldByName('ID_TITULO').AsInteger; DM.qryAuxiliar1.ParamByName('TITULO') .AsStrings [AMemoryTable.RecNo-1] := AMemoryTable.FieldByName('TITULO') .AsString; DM.qryAuxiliar1.ParamByName('SUBTITULO').AsStrings [AMemoryTable.RecNo-1] := AMemoryTable.FieldByName('SUBTITULO').AsString; DM.qryAuxiliar1.ParamByName('FOTO') .AsBlobs [AMemoryTable.RecNo-1] := AMemoryTable.FieldByName('FOTO') .AsVariant; DM.qryAuxiliar1.ParamByName('MINIATURA').AsBlobs [AMemoryTable.RecNo-1] := AMemoryTable.FieldByName('MINIATURA').AsVariant; AMemoryTable.Next; end; {Se houver mais de um registro a ser aplicado, então chama o Execute da Quuery Auxiliar} if intNumInserts > 0 then DM.qryAuxiliar1.Execute(intNumInserts, 0); DM.qryAuxiliar1.Active := False; end;
Listagem 2. Sincronizando dados
Apenas para relembrar, nós estamos criando uma variável do tipo TFDJSONDataSets que é a classe utilizada para exportar os dados do servidor para o mundo externo, nesse caso para nosso cliente. Essa classe traz o próprio DataSet aberto do servidor no cliente de acordo com os devidos filtros aplicados. Em seguida nós adicionamos esse DS em nosso componente MemTable através do método AppendData do componente. Feito isso basta criarmos algum método para percorrer os registros. É o que fazemos no método AtualizarTitulos. Aqui nós percorremos a MemTable e adicionamos um a um no banco de dados local. Algo parecido pode ser efetuado no servidor, ou seja, podemos enviar um FDJSONDataSets para o servidor e fazer o trabalho de inclusão no banco.
Editando e Adicionando registros no cliente
Antes de iniciarmos o sincronismo, faremos com que nossa tela de títulos se torne totalmente independente, ou seja, poderemos incluir, excluir e alterar registros na tela do celular. Nós montaremos a tela completa de edição, inclusive a inclusão de fotos. Vamos lá:
Nossa janela de títulos a essa altura do campeonato deve conter um componente Layout chamado lytTitulos e dentro dele um TabControl com o nome tbctrlTitulos. Caso ainda não tenha adicionado a aba de detalhes nesse tabcontrol, faça isso agora. Nós precisamos de duas abas. Uma se chamará tbitemTitulos e a outra tbitemDetalhe. Mude para a aba de detalhe e adicione a ela duas ToolBars. A primeira delas ficará alinhada ao topo e a segunda abaixo da TabItem.
Na toolbar superior insira os seguintes componentes e configure-os de acordo com o descrito:
- SpeedButton:
- Name: spbVoltar
- StyleLookup: backtoolbutton
- Align: left
- Margins.Left: 8
- SpeedBuuton:
- Name: spbCancelar
- StyleLookup: deletetoolbutton
- Align: left
- Margins.Left: 8
- SpeedButton:
- Name: spbSalvar
- StyleLookup: donetoolbutton
- Align: right
- Margins.Right: 8
- SpeedButton:
- Name: spbEditar
- StyleLookup: composetoolbuttonbordered
- Align: right
- Margins.right: 8
- Label:
- Name: lblTituloDet
- StyleLookup: listboxheaderlabel
- Align: Contents
- Margins: todas em 8px
Na ToolBar inferior adicione um SpeedButton com o nome spbExcluir. Alinhe-o como Client e coloque 8px em todas as margens dele. No corpo do TabItem adicione um ListBox com o nome lsboxDetalhe e o alinhamento como Client. Adicione dois itens no lsboxDetalhe. No primeiro item adicione um componente TImage e alinhe-o a esquerda com margens 8px em todos os lados. Em seguida aumente a altura do item de listbox para 105 na propriedade Height. Nosso TImage deverá ficar com os tamanhos de largura (width) e altura (height) próximos de 89px.
Nesse mesmo item de listbox adicione um TLayout e coloque-o como alinhamento Client. Dentro desse layout adicione outro ListBox que conterá dois novos itens. Insira em cada item um componente TEdit para cada campo da tabela de Títulos, ou seja, um para Título e outro para SubTitulo, veja abaixo:
- TEdit:
- Name: edtTitulo
- Align: Client
- Margins: todas como 8px
- TEdit:
- Name: edtSubTitulo
- Align: Client
- Margins: todas como 8px
No segundo item do nosso listbox geral, lsboxDetalhe, adicione um label alinhado a esquerda com 8px de margem esquerda. Depois insira um TEdit chamado de edtAno e alinhe-o à direita. Feitos os ajustes, nós termos algo parecido com a Figura 3.
Figura 3. Aba de detalhe
Agora precisaremos criar os devidos métodos para ativar e desativar botões e também para colocar em modo de edição ou adição.
Editando os dados
A primeira providência é criarmos um método para ativar e desativar os botões. Para isso, acesse a área Public do form e crie um método com o nome AtualizarBotoes, como abaixo:
procedure AtualizarBotoes;
Pressione Ctrl + Shift + C para criar o escopo do método e codifique-o como na Listagem 3. E vamos a explicação. Nós deixamos os botões Voltar, Editar e Excluir visíveis somente se estivermos em modo Browse, ou seja, visualizando os dados. Os botões Salvar e Cancelar somente ficam visíveis se estivemos em modo de edição. Ainda mudamos a propriedade ReadOnly de alguns componentes para permitir digitação.
procedure TfrmTitulos.AtualizarBotoes; begin spbVoltar.Visible := (DM.qryTitulos.State = dsBrowse); spbEdicao.Visible := (DM.qryTitulos.State = dsBrowse); spbExcluir.Visible := (DM.qryTitulos.State = dsBrowse); spbCancelar.Visible := (DM.qryTitulos.State in dsEditModes); spbSalvar.Visible := (DM.qryTitulos.State in dsEditModes); edtTitulo.Enabled := (DM.qryTitulos.State in dsEditModes); edtSubTitulo.Enabled := (DM.qryTitulos.State in dsEditModes); edtAno.Enabled := (DM.qryTitulos.State in dsEditModes); edtTitulo.ReadOnly := (DM.qryTitulos.State = dsBrowse); edtSubTitulo.ReadOnly := (DM.qryTitulos.State = dsBrowse); edtAno.ReadOnly := (DM.qryTitulos.State = dsBrowse); if (DM.qryTitulos.State in dsEditModes) then begin edtTitulo.StyleLookup := 'editstyle'; edtSubTitulo.StyleLookup := 'editstyle'; edtAno.StyleLookup := 'editstyle'; lblTituloDet.Text := 'Editando...'; end else begin edtTitulo.StyleLookup := 'transparentedit'; edtSubTitulo.StyleLookup := 'transparentedit'; edtAno.StyleLookup := 'transparentedit'; lblTituloDet.Text := 'Detalhes'; end; end;
Listagem 3. AtualizarBotões
Acesse o evento OnClick do botão voltar e digite o código abaixo para que possamos Cancelar a edição caso o usuário deseje retornar para a tela anterior.
if DM.qryTitulos.State in dsEditModes then DM.qryTitulos.Cancel; DM.qryTitulos.Active := False; DM.qryTitulos.Active := True; tabctrlTitulos.Previous;
Uma providência que poderíamos criar aqui seria perguntar ao usuário com um MessageDlg se ele realmente deseja sair da tela e cancelar o cadastro. Mas isso fica a critério do leitor.
No botão cancelar insira o código:
DM.qryTitulos.Cancel; AtualizarBotoes;
Assim nós cancelamos a edição e atualizamos os botões de tela. Em seguida vamos mexer no evento OnClick do botão Salvar, veja como fica:
if DM.qryTitulos.State in [dsInsert] then DM.qryTitulos.FieldByName('ID_TITULO').AsInteger := DM.GetIDFromTable('TITULOS'); DM.qryTitulos.Post; AtualizarBotoes;
Aqui é simples entendermos. Se o cadastro estiver em estado de inserção, ou seja, cadastro novo. Nós chamamos o métodos de criação de ID para esse registro. Então gravamos o registro e atualizamos novamente os botões da tela. Por fim, codifique o botão Editar como a seguir:
DM.qryTitulos.Edit; AtualizarBotoes; edtTitulo.SetFocus;
Muito fácil, apenas editamos e atualizamos novamente os botões. Focamos em cima do primeiro edit para que o teclado suba. O último botão é o de exclusão. Nesse codificaremos como abaixo, mas voltaremos mais tarde aqui para fazermos uma alteração.
MessageDlg('Confirma exclusão do título selecionado?', System.UITypes.TMsgDlgType.mtConfirmation, [System.UITypes.TMsgDlgBtn.mbYes, System.UITypes.TMsgDlgBtn.mbNo], 0, procedure(const AResult: TModalResult) begin case AResult of mrYES : begin DM.qryTitulos.Delete; Self.spbVoltarClick(Sender); end; end; end )
Apenas perguntamos ao usuário se confirma a exclusão e em caso positivo chamamos o método Delete do Query e o botão voltar automaticamente para que a tela seja atualizada. Nós teremos que voltar aqui mais tarde para poder colocar a exclusão também no servidor.
Se você rodar sua aplicação nesse momento, tudo funcionará perfeitamente com exceção da parte de fotos que não foi codificada ainda. Retorne a aba de listagem nessa tela e adicione um novo SpeedButton na toolbar superior. Dê o nome de spbAdd nele e alinhe-o à direita com Margin.Right igual a 8px. Seu StyleLookup será addtoolbuttonbordered e seu código como a seguir:
DM.qryTitulos.Append; tabctrlTitulos.Next; AtualizarBotoes;
Aqui apenas chamamos o método de inclusão do DataSet e mudamos para a aba de detalhes sem esquecer de atualizar os botões de tela.
Atualizando e Selecionando fotos
Agora faremos a parte de seleção de fotos através da câmera ou da biblioteca. Insira na tela um componente TActionList e clique duas vezes nele. Clique no botão de seta da janelinha que aparece e selecione New Standard Action. Localize as opções Media Library e adicione um TTakePhotoFromLibraryAction e um TTakePhotoFromCameraAction. Dê os nomes de actFotoBiblioteca e actFotoCamera, respectivamente.
No evento OnDidFinishTaking digite o código para uso da foto, como abaixo:
DM.qryTitulosFOTO.Assign(Image);
Como isso nós estamos recebendo a foto tirada/escolhida pelo usuário e inserindo direto no respectivo campo da base de dados. Por fim, vá até o evento OnClick do imgFoto e adicione o código abaixo:
if DM.qryTitulos.State in dsEditModes then begin frmMain.ShowBackground(Self.lytTitulos, lstitCancelarClick); lstPopUpFoto.Visible := True; lstPopUpFoto.AnimateFloat('position.y', 0); lstPopUpFoto.BringToFront; end else ShowMessage('Você precisa estar em modo de edição!');
Perceba que é bem fácil entendermos. Estamos primeiro verificando se a tabela está em modo de edição e então mostramos um pop-up menu criado em aulas anteriores. Nesse pop-up nós teremos as opções Novo Foto, Biblioteca e Cancelar. Basta voltar no tutorial que falamos disso e veja a codificação. Finalizamos por aqui.
Teste a aplicação e verifique se tudo funcionou.
Excluindo do servidor
Nossa brincadeira agora começa a ficar mais séria. Até o momento estamos alterando somente no dispositivo móvel, ou seja, se excluirmos algo no aparelho, o registro será excluído apenas do aparelho. Quando o usuário fizer uma nova sincronização, o dado virá novamente. Então voltemos ao código-fonte do servidor e vamos criar um método para exclusão. Acesse o nosso único ServerMethod no servidor DataSnap e insira na área Public o seguinte método e pressione Ctrl + Shift + C para criar o escopo do método:
function ExcluirTitulo(ATitulo: Integer): Boolean;
Codifique esse métodos como abaixo. Insira nesse ServerMethod um FDQuery e dê o nome de qryAuxiliar. O que estamos fazendo é bem simples. Criamos uma constante com a instrução DELETE para excluir título da base de dados. Fechamos a query, inserimos o valor do parâmetro ATitulo que será enviado pelo Cliente e então usamos o ExecSQL. Caso dê tudo certo, resultamos True, caso contrário False para que possamos enviar uma mensagem ao usuário.
function TSrvServerMetodos.ExcluirTitulo(ATitulo: Integer): Boolean; const _DELETE = 'DELETE FOM TITULOS WHERE ID_TITULO =:ATITULO'; begin try qryAuxiliar.Active := False; qryAuxiliar.ParamByName('ATITULO').AsInteger := ATitulo; qryAuxiliar.ExecSQL; Result := True; except Result := False; end; end;
Compile o servidor e execute. Em seguida inicie o servidor.
Excluindo no cliente versus Excluindo no servidor
Retorne agora ao nosso cliente e vamos fazer as devidas alterações para que possamos excluir o dado nos dois locais. Abra nosso módulo cliente onde o componente DSRESTConnection se encontra. Clique com o direito nele e mande gerar novamente nosso arquivo de Proxy. Note que agora temos o método ExcluirTitulo. Abra o formulário frmTitulos e vamos fazer a alteração no botão de exclusão. Caso ainda não tenha incluído a unit ModuloCliente ao formulário, acesse File > Use Unit e insira a unit que contem o componente DSRESTConnection.
Agora acesse o evento OnClick do botão de exclusão e altere seu código como abaixo:
procedure TfrmTitulos.spbExcluirClick(Sender: TObject); begin MessageDlg('Confirma exclusão do título selecionado?', System.UITypes.TMsgDlgType.mtConfirmation, [System.UITypes.TMsgDlgBtn.mbYes, System.UITypes.TMsgDlgBtn.mbNo], 0, procedure(const AResult: TModalResult) begin case AResult of mrYES : begin if ModuloCliente.SrvServerMetodosClient.ExcluirTitulo(DM.qryTitulosID_TITULO.AsInteger) then begin DM.qryTitulos.Delete; Self.spbVoltarClick(Sender); end else ShowMessage('Ocorreu um erro ao excluir!'); end; end; end ) end;
Perceba que fizemos uma pequena alteração no miolo de nosso método. Chamamos o server método ExcluirTitulo passando para ele o ID_TITULO do título selecionado. Se o método retornar verdadeiro, significa que conseguimos excluir no servidor, então excluímos também off-line. Caso contrário, mensagem ao usuário.
ApplyUpdates no servidor
Agora entramos na reta final. Vamos fazer a primeira parte, o ApplyUpdates. Quem já está acostumado a desenvolver com DBExpress e ClientDataSet, sabe que existe um método chamado ApplyUpdates do ClientDataSet que faz o trabalho de enviar as alterações para o servidor de banco de dados. Nós teremos que criar nosso próprio método de atualização do lado do servidor. Retorne ao servidor DataSnap e adicione um novo método como abaixo:
procedure TSrvServerMetodos.ApplyUpdadesTitulos(const ADeltaList: TFDJSONDeltas); var LApply: IFDJSONDeltasApplyUpdates; begin LApply := TFDJSONDeltasApplyUpdates.Create(ADeltaList); LApply.ApplyUpdates(sTitulos, qryTitulos.Command); end;
O que nós fazemos aqui é criar uma instância da interface IFDJSONDeltasApplyUpdates para que possamos fazer o apply. Instanciamos a variável e então chamamos o método ApplyUpdates da Interface passando como parâmetro o nome da tabela gravado na constante sTitulos (const sTitulos = ‘TITULOS’) na área Private no do servidor DataSnap. E o segundo parâmetro nós chamamos o método Command do qryTitulos. Somente isso. O restante a própria interface faz o trabalho para nós. Gere novamente o servidor e execute-o.
Voltando ao aplicativo mobile, retorne no nosso módulo cliente e então atualize o arquivo Proxy. Antes de fazermos a alteração, precisamos de um componente MemoryTable no DataModule principal. Adicione um componente FDMemTable no DataModule e dê o nome de memTitulos. Nós usaremos esse componente para receber os dados alterados e enviá-lo ao servidor. Nós precisamos alterar o botão Salvar. Volte no código-fonte e então modifique-o como a seguir:
procedure TfrmTitulos.spbSalvarClick(Sender: TObject); var LDeltaList : TFDJSONDeltas; LDataList : TFDJSONDataSets; begin if DM.qryTitulos.State in [dsInsert] then DM.qryTitulos.FieldByName('ID_TITULO').AsInteger := DM.GetIDFromTable('TITULOS'); DM.qryTitulos.Post; LDeltaList := TFDJSONDeltas.Create; LDataList := TFDJSONDataSets.Create; TFDJSONDataSetsWriter.ListAdd(LDataList, DM.qryTitulos); TFDJSONDeltasWriter.ListAdd(LDeltaList, sTitulos, DM.memTitulos); ModuloCliente.SrvServerMetodosClient.ApplyUpdadesTitulos(LDeltaList); AtualizarBotoes; end;
O que nós fazemos aqui é bem simples. Nós precisamos criar uma instância da classe FDJSONDeltas. Essa instância receberá os dados alterados da query para envio ao servidor. Também precisamos de uma variável para receber os dados em formato FDJSONDataSets. O que estamos fazendo é adicionando os registros da query Titulos dentro do componente memTitulos. Logo em seguida chamamos o método ApplyUpdatesTitulos passando o LDeltaList como parâmetro. Feito isso o servidor receberá a lista de Deltas e aplicará ao banco de dados.
Enviando novos registros
Na seção anterior, nós fizemos a parte de ApplyUpdates, como mencionado. Se voltarmos no tempo para falarmos de DBExpress, vamos lembrar que o ApplyUpdates na verdade é o envio de um pacote com Deltas dos registros alterados, excluídos e incluídos do ClientDataSet para o DataSetProvider. O Provider se encarregava de fazer o trabalho de receber esses Deltas e criar as instruções UPDATE, DELETE e INSERT de acordo com o tipo de registro recebido. E essas instruções são aplicadas ao banco de dados.
Com FireDAC e Reflection, o processo é semelhante, mas evidentemente que não precisamos usar o DataSetProvider, tão pouco o ClientDataSet. Nosso único deve é usar a classe TFDJSONDeltas para extrair o Delta da query e só então enviar esses dados para o outro lado do sistema, o servidor. E então aplicar esse pacote ao banco como vimos.
Automaticamente, não precisamos fazer um método para inserção tão pouco exclusão. Nesse caso só fizemos um método para excluir, pois precisamos ter um retorno do servidor. Mas facilmente poderíamos seguir apenas com a opção ApplyUpdates.
Para um maior entendimento do processo, recomendo que leiam e testem o exemplo completo de Reflection presente na pasta Samples do Delphi.
Conclusão
Finalizamos aqui nosso Tutorial: Criando meu app step-by-step que iniciamos em RAD Studio XE6 e finalizamos em XE7 (ou XE8 que também funcionará normalmente). O que vimos nesse tutorial é que com poucos detalhes e até com pouca programação, conseguimos criar um aplicativo totalmente compatível com iOS 32 e 64Bits, Android, Mac OS X e Windows 32 e 64bits.
Desenvolver aplicativos para essas plataformas hoje é muito mais simples do que podemos imaginar. E o melhor de tudo, com um único código-fonte. Em nosso app, temos acesso a um banco de dados local SQLite e ainda incluímos a funcionalidade de sincronismo com um servidor de aplicativos plugado em um Firebird 2.5.
Esperamos que esse tutorial tenha sido importante para você leitor e que possa compartilhar para outros que desejam aprender mais.
Até a próxima
#GoToDelphi
Baixe o código-fonte completo clicando aqui.
Nessa última parte de nosso tutorial, alguns leitores detectaram problemas na função ApplyUpdates e resolvemos revisar o artigo. De fato, o ApplyUpdatesTitulos criado aqui não funciona perfeitamente, isso porque estamos tentando extrair os Deltas do qryTitulos (Query), mas isso não é possível. A codificação utilizada não consegue extrair justamente por não existirem Deltas em FDQuery. Para corrigir o problema, devemos fazer algumas alterações no nosso projeto. Vamos lá:
A primeira alteração é redirecionar todos os binds para o memTitulos presente em nosso DataModule. Então, nosso memTitulos deverá possuir os campos da tabela Títulos. Nós já adicionamos esses campos no FieldsEditor da qryTitulos, por isso clique com o direito nesse componente e escolha Fields Editor. Selecione todos os campos, pressione Ctrl + C para copia-los e feche-o. Em seguida clique com o direito no memTitulos e escolha Fields Editor. Clique com o direito na janelinha e em seguida em Paste.
Agora volte ao formulário títulos, acione a janela de Binding e refaça todos os links de campos apontando-os para o memTitulos, como na Figura 4.
Figura 4. Links refeitos no binding.
Agora o que precisamos fazer é refazer a codificação toda apontando para o memTitulos ao invés do qryTitulos. O primeiro lugar que devemos mexer é no OnCreate, onde abrimos a querie. Localize a linha onde fechamos e abrimos a querie e troque por memTitulos onde for necessário.
Localize ao longo do código-fonte todos os lugares que fazem menção ao qryTitulos e altere.
A última e mais importante alteração, diz respeito ao botão Salvar da tela de edição. Esse código precisamos dar uma atenção maior.
Na Listagem 4 logo a seguir, nós podemos visualizar todo o código que utilizamos para gravar os dados no banco local e no servidor. O problema aqui é que estamos tentando extrair o delta do qryTitulos, coisa que não será possível como mencionamos anteriormente.
procedure TfrmTitulos.spbSalvarClick(Sender: TObject); var LDeltaList : TFDJSONDeltas; LDataList : TFDJSONDataSets; begin if DM.memTitulos.State in [dsInsert] then DM.memTitulos.FieldByName('ID_TITULO').AsInteger := DM.GetIDFromTable('TITULOS'); DM.memTitulos.Post; LDeltaList := TFDJSONDeltas.Create; LDataList := TFDJSONDataSets.Create; TFDJSONDeltasWriter.ListAdd(LDeltaList, sTitulos, DM.memTitulos); TFDJSONDataSetsWriter.ListAdd(LDataList, DM.memTitulos); ModuloCliente.SrvServerMetodosClient.ApplyUpdadesTitulos(LDeltaList); AtualizarBotoes; end;
Listagem 4. Código Original do Salvar
Nossa codificação ficará ainda mais simples agora. Basta omitirmos as linhas referentes a variável LDataList. Então o código final ficará como na Listagem 5 abaixo:
procedure TfrmTitulos.spbSalvarClick(Sender: TObject); var LDeltaList : TFDJSONDeltas; //LDataList : TFDJSONDataSets; desnecessário begin if DM.memTitulos.State in [dsInsert] then DM.memTitulos.FieldByName('ID_TITULO').AsInteger := DM.GetIDFromTable('TITULOS'); DM.memTitulos.Post; LDeltaList := TFDJSONDeltas.Create; //LDataList := TFDJSONDataSets.Create; desnecessário TFDJSONDeltasWriter.ListAdd(LDeltaList, sTitulos, DM.memTitulos); //TFDJSONDataSetsWriter.ListAdd(LDataList, DM.memTitulos); desnecessário ModuloCliente.SrvServerMetodosClient.ApplyUpdadesTitulos(LDeltaList); AtualizarBotoes; end;
Listagem 5. Código alterado
Ainda precisamos fazer um último passo, atualizar e abrir o memTitulos no nosso DataModule. Nós manteremos o qryTitulos, mas precisamos pegar os dados dessa query e adicionar diretamente no memory table para que possamos navegar e fazer as devidas manutenções nos registros.
Crie um novo método em nosso DataModule no lado cliente. O método terá a seguinte codificação:
procedure TDM.AbrirTitulos; var LDataSetList : TFDJSONDataSets; begin qryTitulos.Active := False; qryTitulos.Active := True; LDataSetList := TFDJSONDataSets.Create; memTitulos.Active := False; TFDJSONDataSetsWriter.ListAdd(LDataSetList, qryTitulos); memTitulos.AppendData(TFDJSONDataSetsReader.GetListValue(LDataSetList, 0)); end;
Com isso, nós agora temos o processo completo de alteração funcionando perfeitamente no Android e iOS. Agora no evento OnCreate do formulário Titulos chame esse método para que possamos abrir a tabela.
O código-fonte disponível no BitBucket já está atualizado para download.

Author: Adriano Santos
1 comentário
Comments are closed.
[…] Tutorial: Criando meu app step-by-step – Sincronismo – Parte Final […]