Share Button

Em dezembro de 2017 separei um notebook que não estava mais usando e tomei o caminho de instalar um linux para avaliar o quando seria possível viver sem Windows e sem uma máquina virtual (rsrs).
Como todos sabem, a principal ferramenta de trabalho que utilizo é o Delphi – então o desafio ficou imenso – já que sem uma máquina virtual e sem o windows é impossível utilizar o Delphi. Usando outro framework para substituir o Delphi não iria proporcionar emoção – então descartado frameworks na mesma linha.
Primeiro ponto, o que usar para desenvolver aplicativos ? Como bom delpheiro JAVA não estava nos meus planos, queria algo mais novo.

Primeira parada – se atualizar
Desktop, WEB, Mobile – são opções de plataforma de trabalho. Estacionei no item WEB, vamos procurar o que temos para explorar aí.
Já havia utilizado o Angular-JS a alguns anos para implementar algumas coisas, então vamos nos atualizar no que há de mais novo de Angular – Angular 5 (logo no início do ano veio o 6), tudo novo – esquece o Angular-JS – agora é Typescript.
Typescript é uma implementação feito utilizando como base o javascript – você escreve Typescript e o compilador gera código javascript para ser utilizada no Browser – nem tudo precisa iniciar do zero – legal.

No AngularIO, novas ferramentas de geração e compilação vieram no pacote:

  • o NG (a ferramento do AngularIO) agora gera compilações para serem publicadas no servidor do SITE
  • compila para teste local na máquina com um mecanismo de “Hot Load” muito eficiente – ficou muito bom
  • é muito produtivo para criação de páginas dinâmicas
  • com uso de NodeJS é possível escrever servidores para publicar serviços (ex: busca em banco de dados)

Ferramentas
Para trabalhar com AngularIO, não é preciso ter um windows rodando – Assim como no windows, no linux temos disponível o VS – Virtual Code Studio da MS que faz o trabalho com perfeição, primeira etapa concluída – já é possível seguir o trabalho diário sem Windows.

Preparação de Infra-estrutura
Primeiro passo a resolver é fazer a integração do código Typescript para consumir os serviços de REST em produção escritos em DELPHI. Para isto utilizei o ODataBr (parte do pacote MVCBr – vários artigos aqui no blog – MVCBr).

  • Criado uma camada base de comunicação com o servidor – a parte de http, autenticação, tradução de protocolo, tratamento de “Future” ou respostas assíncronas
  • Criado camada de comandos com classe que tenham regrar de tratamento do protocolo OData
  • Criado camada de classes de acesso aos serviços do servidor utilizados pela aplicação

Produtos com AngularIO

  • Criado um Dashboard para os aplicativos Delphi que estavam em fase de produção para lançamento em 2018
  • Treinamento de equipe para desenvolvimento em AngularIO
  • Implementação da plataforma NcConstruir – com portal de interação do logista com a plataforma – ver: Nc Construir
  • Implementado uma experiência de venda de produto por Mobile rodando em WebView;

Segunda parada – Google Firebase
…. próximo

Share Button

Para visualizar efeito de Zebra em uma lista de dados com “Directive” no Angular5 para mostrar cores alternadas.

Exemplo:

Zebra Demo
<div matZebra index="0">
      Text value 1
</div>

<div matZebra index="1">
  Text value 2
</div>

<div *ngFor="let item of rows; let idx = index">
   <div matZebra [index]="idx" oddColor="lightcyan" evenColor="lightgray">
     {{item.t}}
   </div>
  </div>

  

Código: ZebraDirective

Share Button

Até a versão Tokyo do Delphi, o navegador padrão embutido é o TWebBrowser – uma implementação para chamada do Navegador padrão do Windows. Se as páginas a serem chamadas são páginas mais antigas – ele até vai bem. No entanto quando o desejo for carregar o que há de mais novo em estrutura do HTML5 com frameworks como Angular5 e outros, vários problemas de incompatibilidade surgirão á inviabilizar o uso do TWebBrowser.

TChromium como Navegador
A alternativa que se mostra mais adequada para uso dos novos frameworks é a utilizado o TChromium com a implementação CEF4Delphi créditos para Salvador Díaz Fau como consta.

Leia os exemplos
É imperativo a leitura dos exemplos que são baixados do GIT. Ele requer chamada e configuração inicial antes do uso da biblioteca, assim como é necessário aguardar a inicialização da mesma antes de sair carregando a página. Então não deixe de olhar os exemplos.

Na inicialização – antes de abrir uma página


// preparando as configurações
var FStarted:boolean;
function CEFApplication: TCefApplication;
var sPath:String;
begin
  sPath := extractFilePath(ParamStr(0));   /// ou outro que for adequado ao seu aplicativo - com o local de instalação das DLLs do pacote
  if not assigned(GlobalCEFApp) then
  begin
    GlobalCEFApp := TCefApplication.Create;
    GlobalCEFApp.FlashEnabled := False;
    GlobalCEFApp.FastUnload := true;

    GlobalCEFApp.FrameworkDirPath := sPath+'cef';
    GlobalCEFApp.ResourcesDirPath := sPath+'cef';
    GlobalCEFApp.LocalesDirPath := sPath+'cef\locales';
    GlobalCEFApp.cache := sPath+'cef\cache';
    GlobalCEFApp.cookies := sPath+'cef\cookies';
    GlobalCEFApp.UserDataPath := sPath+'cef\User Data';
    GlobalCEFApp.EnableGPU := false;
  end;
  if not FStarted then
    FStarted := GlobalCEFApp.StartMainProcess;

  result := GlobalCEFApp;
end;


// na inicialização do projeto - chamar os dados de configuração da biblioteca - 
CEFApplication;


Antes de abrir uma página
Olhando para os exemplos, ele requer que a criação da área de apresentação (o Browser) exige que a estrutura de carga da biblioteca já esteja carregada.
Para fazer isto é preciso ficar monitorando a conclusão da inicialização para somente após criar a camada de apresentação (a VIEW);
Aqui nós usamos um timer para ficar monitorando quando que a estrutura foi inicializada.

algo que represente um loop:

while not (Chromium1.CreateBrowser(CEFWindowParent1, '')) and
        (Chromium1.Initialized) do
begin
   delay(100);
   application.processMessage();  // avaliar se a aplicação requer despachar outras mesnagens.
end;

Somente após a resposta que tudo foi carregado com sucesso que a página é chamada. Neste ponto uma dica é usar o evento “AfterCreated” do componente, ou seja, o “AfterCreated” é chamado após o componente ter concluído a criação da área de apresentação.

Carregando uma página

Depois de tudo inicializado “AfterCreated” é o momento de carrega a nossa página:

Chromium1.LoadURL(url);

Share Button

A versão 2 do ODataBr (antigo MVCBrODataServer) recebeu novos recursos de melhoria de performance e outros de funcionalidades como segue.

1. Melhorias
– algumas propriedades do FireDAC foram alteradas visando a melhoria de performace – alterações de propriedades;

2. Correções
– Foi feito correção para tabelas JOIN

exemplo: http://localhost:8080/OData/OData.svc/cliente(1)/vendas

no exemplo é feito um JOIN entre os dados do cliente=1 e a tabela de vendas para mostrar as vendas do daquele cliente;

3. Recursos novos

– Adicionado na pasta ./bin/admin o front-end de acesso administrativo para acesso: http://localhost:8080/admin
– Nas instruções PATCH de atualização dos dados no servidor, acrescentado a opção “rowstate”: “modifyORinsert” – tenta fazer um update, caso não encontre nenhum registro então tenta um INSERT;
com isto a propriedade “rowstate” passou a ter as seguintes opções: inserted, updated, modified e modifyORinsert
– proxy para gerar código TypeScript nativo fazendo a chamada: http://localhost:8080/OData/hello/ng irá retornar um código typescript/angular para acesso ao metadata utilizado no servidor;

exemplo: http://localhost:8080/OData/hello/ng


/// <summary>                                 
/// ODataBr - Generate NG5 Script                           
/// Date: 17/01/2018 22:39:46                          
/// Auth:  amarildo lacerda - tireideletra.com.br           
///        gerado pelo Servidor ODataBr: .../OData/hello/ng   
/// </summary>                                 

import { Injectable, Inject } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { ODataProviderService,ODataFactory,ODataService } from './odata-provider.service';
import { Observable } from 'rxjs/Rx';

export interface ODataBrQuery extends ODataService{} 

@Injectable()
export class ODataBrProvider {
  token:string=""; 
  urlBase:string=""; 
  urlPort:number = 8080; 
  headers: HttpHeaders;

  port(aPort:number):ODataBrProvider{
     this.urlPort = aPort;
     return this;
  } 
  url(aUrl:string):ODataBrProvider{
    this.urlBase = aUrl;
    return this;
  }

  getJson(url:string):Observable<any>{
    this.configOptions();
    return this._odata.getJson(url);
  }
  getOData(query:ODataService):ODataProviderService{
    this.configOptions();
    return this._odata.getValue(query);
  }
  private configOptions(){ 
      if (this.token!="") { this._odata.token = this.token; }; 
      this._odata.createUrlBase(this.urlBase,this.urlPort); 
      if (this.headers.keys().length>0) {
        //this._odata.headers.clear;
        for(let item of this.headers.keys() ){ 
          this._odata.headers.set(item,this.headers.get(item));
         }
      }
  }

constructor(private _odata:ODataProviderService ) {
       this.headers = new HttpHeaders(); 
}
   get_filial( query:ODataBrQuery ):ODataProviderService { 
      this.configOptions(); 
      query.resource = "filial"+(query.join?query.join:"");
      return this._odata.getValue( query ); 
   }

   get_produtos( query:ODataBrQuery ):ODataProviderService { 
      this.configOptions(); 
      query.resource = "produtos"+(query.join?query.join:"");
      return this._odata.getValue( query ); 
   }
...
}

– Adicionado classe base em Typescript/Angular para acesso o servidor OData com a estrutura fundamental de acesso aos dados
Classe typescript/angular para acesso ao servidor

Exemplo de utilização da classe typescript/angular é possível consulta o font-end de “admin” em: http://front-end admin

ver também:
introdução ao oData

Share Button

Porque um TTimer Anonymous ?

A vantagem em ter um TTimer usando método anônimo esta na facilidade em criar/destruir o objeto sem precisar criar o evento OnTimer para receber a mensagem.


  TTimer.createAnonymousTimer(
    function: boolean
    begin
      /// executa em loop enquanto retornar FALSE... para finalizar o Timer: retornar  TRUE; 
      return  :=  not queroContinuar();
    end, 10, 500);

   {
      AFunc -> TFunc<Boolean>
      AInterInicial ->  tempo para primeira execução
      AInterRegular ->  intervalor para as demais execuções.
   }

Ver unit: TTimer.CreateAnonymousTimer(..)

Share Button

Estava pesquisando como fazer coleta de temperatura para uma solução de Angular5 implementando com Typescript; A solução é bem simples, mas pode ajudar aqueles que estejam buscando tal solução;

Implementando a classe “provider”

/*
  <summary>
    subject: weather-service para acessar  openweathermap
    auth: amarildo lacerda   <tireideletra.com.br>
    uso:
        - providers: [ 
                WeatherService,..]

        - para enviar pedido de temperatura:
          ngOnInit() {
             this._weather.getWeatherbyName("SAO PAULO,BR").subscribe(rsp => {
             this.weather.fillWeather(rsp);
             console.log(this.weather.response());
          });
        }
        - ...fillWeather -> preenche os dados retornados do json  
  
  </summary>

*/
import { Injectable } from '@angular/core';
import { Http, Response, URLSearchParams } from '@angular/http';
import { Observable } from 'rxjs/Rx'; /// para corrigir erro do catch

export interface Coord {
    lon: number;
    lat: number;
}

export class Weather {
    temp: any;
    temp_max: any;
    temp_min: any;
    description: string;
    city: string;
    county:string;
    date: any;
    pressure: any;
    humidity: any;
    sunrise: any;
    coord: Coord;
    wind:any;
    weather:any;
    private resp: any;
    response() {
        return this.resp;
    }
    public fillWeather(body: any) {
        this.resp = body;
        this.city = body.name;
        this.county = body.sys.country;
        this.description = body.weather[0].main;
        this.temp = body.main.temp.toFixed(0);
        this.temp_max = body.main.temp_max;
        this.temp_min = body.main.temp_min;
        this.pressure = body.main.pressure;
        this.humidity = body.main.humidity;
        this.date = new Date(body.dt * 1000);
        this.sunrise = new Date(body.sys.sunrise * 1000);
        this.coord = body.coord;
        this.wind = body.wind;
        this.weather = body.weather;
    }

}

@Injectable()
export class WeatherService {
    city: string = "Sao Paulo,BR";

    urlAPI = "http://api.openweathermap.org/data/2.5/weather";
    tokenAPI = ""; //gerar uma API no provedor - esta é limitada 
    units = "metric";

    constructor(private http: Http) {

    }

    getWeatherbyName(localName: string = "Sao Paulo,BR", api:string="") {
        if (localName == "") {
            localName = this.city;
        }
        if (api==""){
            api = this.tokenAPI;
        }
        console.log(localName);
        let myParams = new URLSearchParams();
        myParams.append('appid', api);
        myParams.append('q', localName);
        myParams.append('units', this.units);
        return this.http.get(this.urlAPI, { search: myParams })
            .map(this.extractData)
            .catch(this.handleError);
    }

    private extractData(res: Response) {
        let body = res.json();
        return body;
    }

    private handleError(error: Response | any) {
        console.error(error.message || error);
        return Observable.throw(error.message || error);
    }

}

Usando o “Provider”


import { Component, OnInit } from '@angular/core';
import { WeatherService, Weather } from './weather-service';

const 
  token = "gerar um token no site - openweathermap";
const  
  local = "SAO PAULO,BR";

@Component({
  selector: 'app-previsao-tempo',
  template: '<div>{{weather.city}}: {{weather.temp}}c </div> \
             Min: {{weather.temp_min}}c Max: {{weather.temp_max}}c ',
  styleUrls: []
})
export class PrevisaoTempoComponent implements OnInit {
  weather: Weather = new Weather();
  constructor(private _weather: WeatherService) {
  }
  ngOnInit() {
    this._weather.getWeatherbyName(local,token).subscribe(rsp => {
      this.weather.fillWeather(rsp);
      console.log(this.weather.response());
    });
  }
  
}


Share Button

Executar um “select” em paralelo na verdade é bem simples, mas complexo se não deter algumas informações elementares sobre o tratamento que o FireDAC dá ao isolamento de conexão.

Basicamente o processo se dá pelo isolamento da “connection” ao fazer a chamada no banco de dados, talvez aí o maior problema já que é necessário ter uma conexão individual para cada chamada ao banco.

No FireDAC esta disponível na IDE o componente TFDMonitor que é responsável em gerar um novo componente de conexão para cada chamada feita. Este é o caminho mais fácil para utilizar “multithreaded”, assunto que não irei tratar aqui já que o interessante é explorar recursos que entregue mais conteúdo a quem quer entender como o processo acontece.

em construção …. ajude a escrever este texto enviando comentários. Gratidão

Base para o texto:
Unit: Data.FireDAC.Helper
Ver: Exemplo no GIT

Share Button

Criar um novo componente no Delphi é basicamente implementar algum comportamento “by code” que será executado quando for requisitado ao componente.

base
Um componente publicado na paleta de objetos da IDE permitem arrastar e soltar para os formulários entregando recursos RAD ao desenvolvedor.
O componente pode ter um comportamento visual no FORM quando herda de outro componente visual, ou não visual quando herda de um TComponent;

Exemplo

  • um componente visual: TEdit
  • componente não visual: TDatasource

estrutura de um componente

  /// sample1
  unit xxxx;
   ...
  type
      /// minha implementação
      TMeuComponent = class(TComponent)
      private 
         FAbout:string;
      published
         /// mostrar a propriedade no Object Inspect da IDE
         property About:string read FAbout write FAbout;
      end;

      /// registrar o component na paleta da IDE
      procedure register;

   implementation
      procedure register;
         begin 
            RegisterComponents('MVCBr',[TMeuComponent] );
         end;
   end.

Caso o componente implemente uma interface é possível indicar qual a interface junto com a mesma declaração do novo componente:

  /// sample2
  unit xxxx;
   ...
  type
      IMeuComponent = interface
         [Ctrl+G para obter um GUID]
         ....
      end;
      /// minha implementação
      TMeuComponent = class(TComponent,IMeuComponent)
      private 
         FAbout:string;
      published
         /// mostrar a propriedade no Object Inspect da IDE
         property About:string read FAbout write FAbout;
      end;

      /// registrar o component na paleta da IDE
      procedure register;

   implementation
      procedure register;
         begin 
            RegisterComponents('MVCBr',[TMeuComponent] );
         end;
   end.

a pegadinha

Para componentes simples será suficiente o código de exemplo indicado. Já quando o componente encapsula outros componentes (visuais/ou não) que são ligados diretamente no FORM através de propriedades, será necessário código adicional para ajudar a IDE a retirar a ligação dos componentes quando não forem mais necessário.

vejamos um exemplo de um componente com um “link”:

    ///sample3
    type
      TDBMeuComponent = class(TComponent)
         private 
           FDatasource:TDatasource;
         published
           property Datasource:TDatasource read FDatasource write FDatasource;
      end;

escrevendo para a IDE

Neste caso (sample3) a propriedade Datasource permitirá que em tempo de design seja ligado um TDatasource na propriedade Datasource; A ligação do componente ocorre visualmente na IDE (ligando as propriedade), o problema ocorre neste ponto em razão da IDE não saber lidar com o desfazimento desta ligação. Como a IDE não sabe qual o momento correto em desfazer a ligação, é necessário indicar esta situação no código através do método “Notification” como segue.

    ///sample4
type

  TDBMeuComponent = class(TComponent)
  private
    FDatasource: TDatasource;
  protected
    procedure Notification(AComponent: TComponent;
      AOperation: TOperation); override;
  published
    property Datasource: TDatasource read FDatasource write FDatasource;
  end;

  { TDBMeuComponent }

procedure TDBMeuComponent.Notification(AComponent: TComponent;
  AOperation: TOperation);
begin
  inherited;
  /// retira o link do datasource
  if AComponent.Equals(FDatasource) and (AOperation = TOperation.opRemove) then
    FDatasource := nil;
end;

Importante !!!
A maioria dos BUGs de IDE em tempo de Design ocorre pela uso inadequado do Notification, então é bom ficar atento a este ponto obrigatório para aqueles que pensam em implementar novos componentes para a IDE do Delphi.

Exemplo Completo

Share Button

As units MVCBr.MongoModel e MVCBr.FDMongoDB implementam acesso as coleções MongoDB diretamente em um componente TFDMemTable abrindo os dados e enviando as alterações de volta no servidor do MongoDB.

base
O acesso ao MongoDB implementado como um Model do MVC, faz acesso direto a biblioteca MongoWire e está pronto para Auth3 presente na versão mais recente do MongoDB.
Dado a facilidade de uso do MongoDB que não exige um “schema” para as tabelas é uma boa solução em ambiente que requeira compartilhamento de dados e flexibilidade na mudança de estrutura dos documentos, justificando esforço adicional para implementar soluções RAD que de velocidade ao “coder”.

elementar
Bancos NoSQL como MongoDB não são acessados com comandos tradicionais como no ambiente SQL. É uma nova fronteira em armazenamento de dados que evoluiu muito recentemente, ainda que há um movimento para retroceder para modelos Cliente/Servidor mais tradicional adicionado de boas experiências com aprendizagem NoSQL como é o caso de TimescaleDB que faz uma mescla de recursos do Postgres e novos recursos até então visto somente nos bancos NoSQL – Novos estudos pela frente.

componentes

MongoConnection
Provê conectividade com o MongoDB.


MongoDataset
Um Dataset para obter os dados do servidor. Onde: CollectionName representa a tabela (coleção) que armazena os documentos; KeyFields é uma lista de colunas (seprado por “;” que indica a “where” a ser submetido ao servidor quando faz um “update” ou “delete”;

Exemplo no Git

Veja o Mongo Tips do amigo Thulio Bittencourt

Share Button

Singleton – este é um cara engraçado. Não aceita sobrecarga nem substituição – fecha em sí e não abre para estender seu recursos. Maaasss… ele é o fino em compartilhar os seus atributos. Sempre que alguém pergunta por ele, a resposta é a sempre a mesma – inflexível e sem surpresas.

base
Singleton é uma instância global a ser evitada, mas quando não tem outra saída – é um bom parceiro.

elementar meu caro
Sendo um Singleton uma instância global, basta criar a instância uma única vez e sair usando – controlar o momento em que um Singleton pode ser liberado da memória pode ser um bom desafio. Em geral a instância é criado em algum momento do código e sua liberação ocorrerá ao finalizar a aplicação, mantendo-o carregado na memória durante o tempo de vida do aplicativo.

quando usar
Sempre que for necessário uma instância que faça controle de outras instâncias, o Singleton será bem útil. O Delphi possui alguns deles como exemplo o “Application”, uma instância que existe por toda a vida ativa do aplicativo, criado ao entrar e liberado somente quando é encerrado.
O MVCBr possui um subtitulo ao “application” o “ApplicationController”. ApplicationController é uma instância única que mantém uma pilha de “controllers” aberto no aplicativo, aqueles que estão instanciados.

limitações

Quando o desejo é atribuir um variável única para o um objeto específico e suas herança, o uso do Singleton é bastante “simples” – basca criar um “Class Var” para a instância a controlar.
Se de outro lado, se o desejo for permitir herança e cada herança tem o seu próprio Singleton então “Class Var”* não irá resolver a classe base precisa implementar mecanismos mais espertos (*a class var pertencerá sempre ao pai e não permite nova cópia aos filhos).

curiosidades
O TMVCBrSingleton faz uso de outros dois patterns:
LazyLoad – quando cria a instância para a classe alvo somente quando ela for necessária (ver invoke);
Adpater – exporta a classe base por default sem necessidade de fazer referências indiretas ( ver ..factory.mudarFlag(true) .. )

Uma classe que permite transferir um Singleton individual aos filhos
Unit: MVCBr.Patterns.Singleton
O Singleton do MVCBr implementa uma interface de adaptadora para a interface IMVCBrSingleton como já vimos antes (ver mais: princípio adaptador para interface ). Como uma classe adaptadora, é possível aplicar o “generics” para retornar o próprio singleton adaptado pela classe, vejamos:

unit singleton.MinhaClasse;

interface

uses MVCBr.Patterns.singleton;

type

  /// minha classe alvo
  TMinhaClasseAlvo = class(TObject)
  public
    function mudarFlag(AValue: Boolean): Integer;
  end;


  /// criando uma classe factory filha
  TMinhaClasseFactory = class(TMVCBrSingleton<TMinhaClasseAlvo>)
  end;

var
  MinhaClasseFactory: IMVCBrSingleton<TMinhaClasseAlvo>;

implementation

{ TMinhaClasseAlvo }
function TMinhaClasseAlvo.mudarFlag(AValue: Boolean): Integer;
begin
  result := ord(AValue);
end;

initialization
  MinhaClasseFactory := TMinhaClasseFactory.new() ;
end.

caso 1
Para criar uma instância local da interface – não parece lógico, somente para ilustrar, podemos fazer:

var
  MinhaClasseFactory: IMVCBrSingleton<TMinhaClasseAlvo>;
begin
  /// LOCAL
  /// criando a classe factory para singleton
  MinhaClasseFactory := TMVCBrSingleton<TMinhaClasseAlvo>.New();
  showMessage(MinhaClasseFactory.mudarFlag(true).ToString);
...

caso 2
Usando a classe factory que implementa uma herança de TMVCBrSingleton:

   // usa o singleton 
   showMessage( MinhaClasseFactory.mudarFlag(false).toString);