terça-feira, 16 de agosto de 2016

Smart Mobile Studio - SearchBar Component


SearchBar Component

Let's say I have a big list, 23,018 cities (DOM elements) on a page, the user interact with the page (searching for cities). When the user type a key to fetch data it causes page reflow. Look, the searchbar is using documentFragment to reduce redraws and repaints.
In the following experiment, I'm going to use this SearchBar component. We're filtering a big list 23,018 cities.

SearchBar Source


unit uSearchBar;

{ ╔═════════════════════════════╗
  ║  _______  _______  _______  ║
  ║ (  ____ \(       )(  ____ \ ║
  ║ | (    \/| () () || (    \/ ║
  ║ | (_____ | || || || (_____  ║
  ║ (_____  )| |(_)| |(_____  ) ║
  ║       ) || |   | |      ) | ║
  ║ /\____) || )   ( |/\____) | ║
  ║ \_______)|/     \|\_______) ║
  ║ component by: warleyalex    ║
  ╚═════════════════════════════╝ }


interface

uses
  SmartCL.System,
  SmartCL.Components,
  DataModule,
  FW7, ECMA.JSON, w3C.HTML5, W3C.Console, W3C.DOM;

type
  THandleArray = array of variant;
  TNotifyEvent  = procedure(Sender: TObject);

  TSearchBar = class(TW3CustomControl)
  private
    { Private declarations }
    FSelected:  Boolean;
    FStore: Variant;
    FLeft, FTop, FWidth, FHeight: variant = 0;

  protected
    { Protected declarations }
    procedure setSelected(Const Value:Boolean);
    procedure createSearchBar;
    procedure InitializeObject; override;
    { procedure ObjectReady; override; }
empty;
    procedure ListItems(arrayStore: variant);
  public
    { Public declarations }
  published
    { Published declarations }
     property store: Variant read FStore write FStore;
     property SearchItems: Boolean read FSelected write setSelected;
  end;

implementation

function document: variant; external "document" property;

{ ╔════════════════════════════════════════════╗
  ║ Insert here the Component Events Handlers  ║
  ╚════════════════════════════════════════════╝ }

procedure SearchBarOnFocus(e: variant);
begin
  var input := SMS(e.target);
  input.parents(".searchbar").addClass("searchbar-active");
end;

procedure SearchBarOnClick(e: variant);
begin
  var btn = SMS(e.target);
  btn.parents(".searchbar").removeClass("searchbar-active");
end;

procedure SearchBarOnBlur(e: variant);
begin
  var input = SMS(e.target);
  input.parents(".searchbar").removeClass("searchbar-active");
end;

{ ╔═══════════════════════════════════════════════════════════════════════╗
  ║ ### refs ###                                                          ║
  ║ You can set up references / bindEvents listeners to components        ║
  ║ This allows you to retrieve and manipulate components on the page     ║                            ║
  ╚═══════════════════════════════════════════════════════════════════════╝ }

var bindings : THandleArray =
[CLASS
  element  := '.searchbar input';
  event    := 'focus';
  handler  := @SearchBarOnFocus;
END,
CLASS
  element  := '.searchbar-cancel';
  event    := 'click';
  handler  := @SearchBarOnClick;
END,
CLASS
  element  := '.searchbar input';
  event    := 'blur';
  handler  := @SearchBarOnBlur;
END];

procedure bindEvents(bindings: THandleArray);
var i : integer;
begin
 for i := Low(bindings) to High(bindings) do
  begin
  SMS(bindings[i].element).on(bindings[i].event, bindings[i].handler);
 end;
end;

{ ╔════════════════╗
  ║ Constructor    ║
  ╚════════════════╝ }
/*Constructor TSearchBar.create(AOwner: TW3Component;left,top,width,height: variant=0);
begin
inherited Create(AOwner);
  FLeft   := left;
  FTop    := top;
  FWidth  := width;
  FHeight := height;
end;*/

procedure TSearchBar.ListItems(arrayStore: variant);
var
  i, len: integer;

begin
len := arrayStore.length;
if len = 0 then arrayStore := "";

var myList   := document.getElementById('list_'+Self.Handle.id);
var tempFrag := document.createDocumentFragment();

while (myList.firstChild <> nil) do begin
 myList.removeChild(myList.firstChild);
end;

for i := 0 to len - 1 do
begin
 var listItem := JHTMLLIElement(document.createElement('LI'));
 listItem.setAttribute("class", "item-content");
 tempFrag.appendChild(listItem);

 var div0 := JHTMLDivElement(document.createElement('DIV'));
 div0.setAttribute("class", "item-inner");
 listItem.appendChild(div0);

 var div_0 := JHTMLDivElement(document.createElement('DIV'));
 div_0.setAttribute("class", "item-title");
 div0.appendChild(div_0);
 div_0.appendChild( JText(document.createTextNode(arrayStore[i])) );
end;
myList.appendChild(tempFrag.cloneNode(true));

end;

procedure TSearchBar.setSelected(Const Value:Boolean);
begin
//DataModule1
  if value<>FSelected then
  begin
    FSelected:=Value;
    If Value then
    begin
      SMS('input#search'+Self.Handle.id).on("change keyup", procedure(e: variant)
      begin
        var SearchIn := ( SMS('input#search'+Self.Handle.id).val() );
        DataModule1.findByKey(SearchIn, Store, procedure(ds: variant)
        begin
          Self.ListItems(ds);
        end);
      end);

    end;
  end;
end;

{ ╔══════════════════════════════════════════════════╗
  ║ Insert here the innerComponent (generated code)  ║
  ╚══════════════════════════════════════════════════╝ }
procedure TSearchBar.createSearchBar;
begin
{ ╔═══════════════════════════════════════════════════════════════════════════╗
  ║ Since the document fragment is in memory and not part of the main DOM     ║
  ║ tree, appending children to it does not cause page reflow (computation    ║
  ║ of elements position and geometry). Consequently, using documentfragments ║
  ║ often results in better performance.                                      ║
  ╚═══════════════════════════════════════════════════════════════════════════╝ }
  var docFragment: variant = JDocumentFragment(document.createDocumentFragment()); // contains all gathered nodes
  

  var div1 := JHTMLDivElement(document.createElement('DIV'));
  div1.setAttribute("class", "bar");
  docFragment.appendChild(div1);

  var div_0 := JHTMLDivElement(document.createElement('DIV'));
  div_0.setAttribute("class", "searchbar ");
  div1.appendChild(div_0);

  var a := JHTMLAnchorElement(document.createElement('A'));
  a.setAttribute("class", "searchbar-cancel");
  div_0.appendChild(a);

  var text_0 := JText(document.createTextNode("Cancel"));
  a.appendChild(text_0);

  var div_1 := JHTMLDivElement(document.createElement('DIV'));
  div_1.setAttribute("class", "search-input");
  div_0.appendChild(div_1);


  var label := JHTMLLabelElement(document.createElement('LABEL'));
  label.setAttribute("class", "icon icon-search");
  label.setAttribute("for", "search");
  div_1.appendChild(label);

  var search := JHTMLInputElement(document.createElement('INPUT'));
  search.setAttribute("type", "search");
  search.setAttribute("id", "search"+Self.Handle.id);
  search.setAttribute("placeholder", "type some city...");
  div_1.appendChild(search);
  //-------------------------------------------
  var pageindex := JHTMLDivElement(document.createElement('DIV'));
  pageindex.setAttribute("class", "content");
  docFragment.appendChild(pageindex);

  var pageindex1 := JHTMLDivElement(document.createElement('DIV'));
  pageindex1.setAttribute("class", "list-block search-here searchbar-found");
  pageindex.appendChild(pageindex1);

  var list := JHTMLUListElement(document.createElement('UL'));
  list.setAttribute("id", "list_"+Self.Handle.id);
  pageindex1.appendChild(list);
  //-----------------------------
  Self.Handle.appendChild( docFragment );
end;

procedure TSearchBar.InitializeObject;
begin
  inherited;
{ ╔══════════════════════════════════════════════════╗
  ║ this is a good place to initialize the component ║
  ╚══════════════════════════════════════════════════╝ }
  createSearchBar;
  //WriteLn (Self.Handle.id);

{ ╔═════════════════════════════════════════════════════════════════════╗
  ║ make the control display itself correctly when its ready in the DOM ║
  ╚═════════════════════════════════════════════════════════════════════╝ }
  Handle.ReadyExecute(procedure()
  begin
    (* call createInnerComponent *)
    bindEvents(bindings);

{ ╔══════════════════════════════════════════════════╗
  ║ some basic style attributes after render         ║
  ╚══════════════════════════════════════════════════╝ }
    Self.Handle.style['left'] := null;
    Self.Handle.style['width'] := '100%';
    Self.Handle.style['height'] := '100%';
  end);

end;

/*procedure TSearchBar.FinalizeObject;
begin
  inherited;

end;*/

TDataModule { TODO }

Nenhum comentário:

Postar um comentário