2021/09/17

逐步使用 React 時,元件如何和外部 DOM 連結?

已知 React 會渲染指定的 div,學習資料中似乎沒有和外部 DOM 連結的方法?

React 元件渲染的條件是 props 或 state 變更時觸發,所以把 DOM 元素放入到 React 元件的 Props,就能夠滿足此條件,範例程式碼如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const e = React.createElement;

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.props.textBox.onchange = () => this.calcCallback();
    this.state = { text: "" };
  }

  calcCallback() {
    this.setState((state, props) => ({
      text: this.props.textBox.value
    }));
  }

  render() {
    return e(React.Fragment, null, e("div", null, this.state.text));
  }
}

ReactDOM.render(
  e(MyComponent, { textBox: document.querySelector("#textBox1") }, null),
  document.querySelector("#app2")
);

HTML

1
2
<input id="textBox1" value="Hello Vanilla"></input>
<div id="app2"></div>

 

成果圖



 

 

 

 

2021/09/16

愛用 jQuery 又想擁抱 React 的逐步採用練習,以 DevExtreme 為例

React 逐步採用

不知道你是否和我一樣,斷斷續續學幾年的 React,總是無法順利把 React 加入到現有的 jQuery 網站中,學 jQuery 時很快樂,需要什麼功能時只需要在 script 標籤裡信手捻來 js 檔案就能快速使用,怎麼到了 React 就完全走鐘?非得使用 NodeJS 編譯才可以?React 起手式就是 create-react-app?這其中一定有什麼誤會,帶你來看看官方說法:

React 在剛推出的時候就容許被逐步採用,你可以按自己所需可多可少的採用 React。 或許你只想在現存的網頁上增加少量的互動性。採用 React component 會是個非常好的選擇。 -- React 官方手冊

按照官網裡的步驟練習結束,就如上面所說一樣,可以很順利的把 React 加入不需 NodeJS 編譯的傳統網站裡。

造成網頁變慢的元兇

會學習到可選的 JSX,JSX 很有意思,可以在 JS 裡安插類 HTML 標籤,從【逐步採用】學習到【可選:嘗試 React 與 JSX】的最後一段結論:

這種方式最適合用來學習和建立簡單的示範。然而,它會令你的網頁變慢,而且並不適合發佈到線上環境。 -- React 官方手冊:快速嘗試 JSX 章節

之所以會令你的網頁變慢,是因為它是載入 babel.js 後在網頁執行時才編譯 JS 裡的 JSX 標籤內容,每次都要花時間編譯,網頁自然快不了。

原來網頁變慢是因為動態編譯 JSX,而不是 React 慢。

JSX 最佳用法就是預先編譯,但

官方的教學在此之後就是利用 NodeJS 把 babel 安裝到現有的 WebPack 專案中,再編譯 JSX 內容成為標準 JS 內容,最後才把編譯後的成品上傳到網站執行。

等等,這樣就不是逐步採用 React 了,要加入 WebPack 就等於是大改現有網站啦!

小結:要逐步採用 React 就不能使用 JSX

原有網站在沒有 WebPack 加持下,要逐步採用 React 勢必不能使用 JSX,因使用 Babel 動態轉換 JSX 的效率不佳。

結論就是:逐步採用 React 下又不要使用 NodeJS 編譯網站,React.createElement 你只能用它了。 😁

逐步採用 React 下的難題:外掛元件

逐步採用 React 雖然可以限縮自己要學習的內容,比如我只要學 ES6 + React.State + React.Props 等,只要會 CSS,要刻什麼都很簡單。

但外掛元件呢?

我的現有網站使用了大量的 EasyUI 和 DevExtreme 元件,這些元件僅提供 NodeJS 下載安裝,並沒有發佈編譯後可使用的 JS 檔案。

解決方案就只好在 React class 裡使用 jQuery 操作外掛元件?

 

這叫 React 入門到放棄。


所幸的是,在 DevExpress 官網裡有個叫【DevExtreme Tickets】的技術小本本,它記錄許多過往使用者所提出的技術問題和解決方案,其中一個主題是:Have demo about devextreme-react without npm?

DevExtreme 官方提出【提取+橋接】的概念。撇開現有網站,另開【Webpack-React-DevExtreme】專案,使用 WebPack 提取所需的元件,如 dxButton 等,再利用 NodeJS 編譯,把提取並編譯過的 JS 檔放入現有網站,如此就能在逐步採用 React 的場合下又能享受外掛元件的開發便利性。

而外掛元件則是存放到 DOM 的 window 裡,範例程式碼如下:

DevExtreme 提取 JS

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import * as React from "react";
import { Button } from "devextreme-react/button";

export class ButtonExample extends React.Component {
    // <Button icon="plus" text="Click me"/>
  constructor(props) {
    super(props);
  }
    render() {
        return (
          <Button {...this.props} />
        );
    }
}

const MyLib = {
    ButtonExample
}

export default MyLib

逐步採用 React 專案呼叫編譯後的 DevExtreme 元件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!doctype html>
<html>

<head>
  <meta charset="utf-8">
    <link rel='stylesheet' type='text/css' href='https://cdn3.devexpress.com/jslib/18.1.3/css/dx.common.css' />
    <link rel='stylesheet' type='text/css' href='https://cdn3.devexpress.com/jslib/18.1.3/css/dx.material.blue.light.css' />  
  <title>DevExtreme with webpack and React example</title>
</head>

<body>
  <div class="container">
    <h1>DevExtreme with webpack and React example</h1>
    <div id="app"></div>
    <div id="app2"></div>
  </div>
    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  <script src="js/app/bundle.js"></script>
  <script>
    const e = React.createElement
    ReactDOM.render(e(window.MyLib.ButtonExample, { icon:"plus",text:"Click me"}, null), document.getElementById("app2"))
  </script>
</body>

</html>

至於 Webpack-React-DevExtreme 原始碼,你可以在上述的票證裡找到。

結論時間

愛用 jQuery 是我個人的選擇,你也可以使用單純的 Vanilla JS,端看你的場合和上手程度來使用。

初學 React 最怕的就是在環境上的摸索上花很多時間,在不使用 npm、webpack 甚至是 Babel,現在我可以專注在學習 React 的設計上,並且和現有 HTML / JavaScript 專案進行互動,做中學所得到的回饋是最直接的。

官方不建議採用【逐步使用 React】?官方是建議使用 NPM Bundler,官方有說明建議原因,和相關資源也比較多,但有關於官方不建議【逐步使用 React】?我官方網站翻了翻,沒找到,有找到的期待你分享這類的相關資料。

React 裡要不要用 jQuery 是另一個問題,React 元件裡我是盡量用相容性比較高的 ES5/6 標準,至於 IE,我的環境不會遇到它,忽略也無仿。

在逐步採用 React 又要使用外掛元件,不再是魚和熊掌的問題,使用一個簡單 WebPack 專案 + 一次編譯,就能把所需的外掛元件放到 window 中,完全滿足「我全都要」的需求,實在是太棒啦!

如果有其它的外掛元件,我想也可以參照此方法把外掛元件產出。

底下是使用 Vanilla JS + React 使用 DevExtreme 小部件的範例。

和你分享 😉

SEE ALSO

2021/09/13

你需要處理 Unicode 編碼?在複製之前邀請你思考這個問題


底下的程式內容到了 XE 後已經被 TEncoding 物件所取代,但這問題時常被問到,之前主流是 Base64,當時還可以用 EncdDecd 單元處理 Unicdoe 編碼,現在已逐漸改以 JSON 格式為主流,EncdDecd 單元已無法滿足眼下的需求了。

WideString 是 Delphi 7 相容 Unicode 的字串類型,然而 WideString 並不適合作為資料傳輸,要達到良好的相容性,使用 UTF16 是比較好的選擇,可以確定的是 Delphi 7 沒有 WideString 對 JSON 的 Encode 和 Decode 的函式內容,不想自己寫?不囉嗦,直接上程式碼:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function DecodeUTF16(sStr: string): WideString;
var
  i: Integer;
  StrPos: Integer;
  temp, top, last: string;
  ResultStr: WideString;
begin
  ResultStr := '';
  repeat
    StrPos := Pos('\u', sStr) - 1;
    if StrPos < 0 then begin            // 無 unicode 編碼不需轉換
      last := sStr;
      ResultStr := ResultStr + last;
      Break;
    end;
    top := Copy(sStr, 1, StrPos);       // 取出編碼字符前非 unicode 編碼的文字,如數字
    temp := Copy(sStr, StrPos + 1, 6);  // 取出編碼,包括 \u,如\u53f0
    Delete(temp, 1, 2);
    Delete(sStr, 1, StrPos + 6);
    ResultStr := ResultStr + top + WideChar(StrToInt('$' + temp));
  until (StrPos >= 0);
  Result := ResultStr;
end;

function EdenUTF16(const sStr: WideString): string;
var
  w: Word;
  StrPos: Integer;
  UTF16Str, TmpStr: string;
begin
  TmpStr := '';
  for StrPos:=1 to Length(sStr) do begin
    w := Ord(sStr[StrPos]);
    UTF16Str := IntToHex(w, 4);
    TmpStr := TmpStr +'\u'+ UTF16Str;
  end;
  Result := TmpStr;
end;

 

結論時間

以上的程式碼雖然能應用在實戰中,其 Delphi 7 裡的函式庫幾乎沒有對 WideString 處理的問題需要正視,在尋求古老三方元件的協助也無可避免需要手刻不足的區塊;自 Delphi XE 開始至現在的 Delphi 11 早已解決絕大部份 Unicode 的麻煩。若你仍有中文處理需求,真心邀請你使用 Delphi CE 社群版來進行專案提升,省下你開發的時間,專注地在商業邏輯的開發會為你帶來更好的效益,畢竟公司開來目的是為了賺錢,而非程式碼研究,研究的事我來就行。😁

和你分享 😉

 

 

2021/09/10

Delphi XE 存取目錄檔案清單、和排序方式


 

最近同事詢問到 Delphi 如何讀出檔案時間,而版本為 Delphi 7,網路資料很多,Google 一找一大把,但關於 XE 以上的新版本卻找不到什麼資料,究竟新版 Delphi 有沒有更好的解法呢?

這問題可以拆分為:

  • 讀取目錄檔案
  • 讀取檔案時間
  • 儲存檔案清單(含時間)
  • 排序檔案清單

有了以上步驟後,各別突破就簡單了。😉

讀取磁碟資料在 XE 後,為了跨平台特別設計了【IOUtils】單元,目的在各家平台都能使用相同程式碼讀取其儲存媒體內容,所以 IOUtils 是非常值得投資學習的單元。

讀取目錄檔案

IOUtils.TDirectory 是讀取目錄結構的物件,只要使用 GetFiles 就可以讀出目錄內所有檔案,簡單程式碼如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var
  LFileList: TStringDynArray;
  LFile: string;
begin
  LFileList := IOUtils.TDirectory.GetFiles('D:\Eden的目錄');
  for LFile in LFileList do
  begin
    ListBox1.Items.Add(LFile);
  end;
end;


讀取檔案時間

取得檔案路徑後,就可以依序取得它們的時間,在 IOUtils 中,TFile 物件可以取得以下時間

  • GetCreationTime: 檔案建立時間
  • GetLastAccessTime: 檔案最後讀取時間
  • GetLastWriteTime: 檔案最後寫入時間

以「檔案建立時間」為例,簡單程式碼如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var
  LFileList: TStringDynArray;
  LFile: string;
begin
  LFileList := IOUtils.TDirectory.GetFiles('D:\Eden的目錄');
  for LFile in LFileList do
  begin
    ListBox2.Items.Add(DateTimeToStr(IOUtils.TFile.GetCreationTime(LFile)));
  end;
end;


儲存檔案清單(含時間)

有了檔案和時間清單,就要進行兩者的結合,使用快取資料集是不錯的選擇,Delphi 內建的快取資料集有兩個:

  • TClientDataSet
  • TFdMemTable

TFdMemTable 是比 TClientDataSet 更為輕巧的資料集物件,而 TClientDataSet 則是經典物件,選擇上看個人喜好,設計上大同小異。簡單程式碼如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var
  LFilePos: Integer;
begin
  with ClientDataSet1.FieldDefs do
  begin
    Add('File', ftWideString, 200);
    Add('CreationTime', ftDateTime);
  end;
  ClientDataSet1.CreateDataSet;
  ClientDataSet1.LogChanges := False;
  ClientDataSet1.IndexFieldNames := 'CreationTime;File';
  for LFilePos := 0 to ListBox1.Items.Count-1 do
  begin
    ClientDataSet1.Append;
    ClientDataSet1.Fields[0].Value := ListBox1.Items[LFilePos];
    ClientDataSet1.Fields[1].Value := ListBox2.Items[LFilePos];
    ClientDataSet1.Post;
  end;
  ClientDataSet1.First;
  while not ClientDataSet1.Eof do
  begin
    ListBox3.Items.Add(Format('%s, %s', [ClientDataSet1.Fields[0].AsString, ClientDataSet1.Fields[1].AsString]));
    ClientDataSet1.Next;
  end;
end;


使用 TFdMemTable 請注意

TFdMemTable 預設不會記錄資料歷程 (TFdMemTable.CachedUpdates default value is False),故 LogChanges 該行程式碼要刪除。


排序檔案清單

ClientDataSet1.IndexFieldNames 屬性設定好以時間欄位排序,即可得到理想的結果,如果要倒序,則可以參閱【【Delphi】ClientDataSet 的排序】,裡面有詳細的解說。


資料集太大?泛型物件也可以

如果認為資料集物件過於龐大,則可以考慮使用泛型物件,在這裡使用 Generics.Collections 單元裡的 TDictionary 物件,目的是它可以存放 Key=Value 字典,在這個場合可以存放兩種值,適合使用 TDictionary 物件。

搭配 TArray 處理排序功能便可以取代資料集的設計方式,完整程式碼如下:


結論時間

在 Windows 平台下找尋 Win32 API 是一件再正常不過的事情了,但進入到 64 位元或是行動裝置等平台後,以往的開發習慣也要跟著一起改變,Delphi 隨著科技的更新也一同進步,你不需要 Win64 API、Android API、MacOS API、iOS API 等全部自行實作一次,只需要在設計程式上跟著 Delphi 轉換開發思維,如前面的範例所述,不僅僅是 API 上的改變,框架的整合應用也是重要且必學的技術,本文提供了硬體存取和資料庫存取的綜合應用,期待你的發揮 ❤


和你分享 😉


See also


2021/09/07

Delphi 10.4 Community Edition (CE) 社群版初體驗

到 Youtube 上觀看 

你遲早都要用編譯器來編譯跨平台 APP,何不現在就用最好的! 

你開發 APP 所消耗的心力遠超過你的想像?

以往以 Web 平台為基礎的開發框架,到最後都必須使用 NodeJS 進行編譯,看著 NodeJS 的編譯效率和 WebPack 的龐大,這還不包含配置各家平台的開發工具,你花了多少心力在「部署」你的環境? 你遲早都要用編譯器來編譯跨平台 APP 專案,何不現在就用最好的! 

Delphi 社群版是入門最佳首選的理由?

Delphi 企業版是火力強大的全方位開發工具,而 Delphi 社群版則提供了另一條開發跨平台 APP 的道路,去無存菁地提供跨平台專屬的框架。除此之外,各家的開發工具都已在安裝程式安排妥當,僅需「下一步」就能在彈指之間完成跨平台 APP 大部份所需要的前置工具部署。 

給自己一個學習 Delphi 的理由

你花了多少時間在打基礎?你又花了多少時間「準備」進入業界?學習 Delphi,這會是你會發現這是從入門到入行最好的投資,給自己一個學習 Delphi 的理由,現在就試試 Delphi 社群版吧!

2021/09/03

Desktop First UX Summit 2021 桌面第一高峰會


2021 年的「桌面第一高峰會」研討會內容十分豐富,各家贊助商也來參與這難得齊聚的盛事。

其中 Marco Cantu 提到 RAD Studio (Delphi, C++ Builder) 11 預覽版本的內容,他認為有三個值得一提的亮點:

  1. 高解析度的工具介面

  2. 隨主題變化,所見即所得的設計介面

  3. 改良 VCL TRichEdit 元件

當然還有很多,例如 FMX 上的 TWebBrowser 在 Windows 平台上支援 WebView2 (edge) 版本等。

研討會訴求主軸從【升級】變為【推廣】。

感受到 EMBT 是下定決心要擺脫【老派】應用程式的惡名,未來提供教育版時,新人或學生的接受度也會提高,才不會有「現在都 202x 年了你還讓我開發 WinXP APP」的反抗心態。

重新雕塑 VCL 和 FMX 的選擇

VCL 重回主角身份,依舊是地表上最強的桌面開發框架無誤,FMX 持續發展,相較於 VCL 少了三方廠商的支援,FMX 不再訴求「取代 VCL」,反而比較適合作為 VCL 的延伸。

在應用程式開發會是以 Windows 為核心,Mac 和行動裝置為延伸。對我們已有的產品來說,原有的產品提高了全功能的重要性,跨平台會以重新檢視和篩選出必要的功能實現為主軸,旨在提供使用者便利性和即時性,加強提供全方位的服務為其核心價值。

總結

小孩才做選擇,我全都要!

VCL 你要學,FMX 也是必學的功課,你的薪資也莫名地變薄了呢 (誤),是說能給服務的對象更有價值的內容,才是我們作產品最大的樂趣,你說是吧!

SEE ALSO

2021/08/25

從Excel操作問題來看工程師的通靈技能


前陣子在 Delphi.KTOP 看到一篇「請問 Excel AddSmartArt 第一個參數該怎麼設定」。

覺得操作 SmartArt 這點很有意思,這兩天才有時間認真看了這篇文章,一開始以為使用 Excel 的「錄製巨集」就可以搞定,但直覺認為回覆此內容時會得到「我早就已經知道」的結果。

因此決定一試 Excel 巨集功能,取得的 VBA 內容是:

    Call ActiveSheet.Shapes.AddSmartArt(Application.SmartArtLayouts( _
        "urn:microsoft.com/office/officeart/2005/8/layout/cycle1")).Select

就這麼短短一行,轉到 Delphi 上會是以下內容:

procedure TForm2.Button3Click(Sender: TObject);
var
  ExcelApp,
  ActiveSheet,
  oSALayout: Variant;
begin
  try
    ExcelApp := CreateOleObject('Excel.Application');
  except
    ShowMessage('建立EXCEL錯誤');
  end;

  ExcelApp.Visible := True;
  ExcelApp.WorkBooks.Add;
  ActiveSheet := ExcelApp.ActiveSheet;
  oSALayout := ExcelApp.SmartArtLayouts('urn:microsoft.com/office/officeart/2005/8/layout/hierarchy2');
  ActiveSheet.Shapes.AddSmartArt(oSALayout, 50, 50, 200, 200);
end;

卻得到以下結果:

看來樓主的問題內容並沒有說明完整,就來看一下 Excel Developer Docs 怎麼描述 SmartArtLayouts:


文件也就這麼一點點,沒了。 再透過 Google 搜尋,沒有更多的資料,Delphi 似乎沒有人這樣做,【找不到成員】這問題難道就無解了嗎?

Google 沒有沒關係,Excel_TLB 單元來解答

利用 Excel 執行檔來製作最適合它的 Delphi 元件看來是最終解法,果不其然,答案在這裡:
ExcelApplication 確實有 SmartArtLayouts 成員,接下來就是直接操作它,程式碼直接公開:
VBA 對 OLE 成員非常自由,Item 到底是什麼東西?經查詢的結果是:

微軟連自家軟體的工程師手冊都寫得如此破碎和簡短,也難怪這方面的開發資訊幾近沒有。

原本還要多寫些關於 SmartArt 賦值的內容,無奈再往下追盡是 Access Violation,使用 Excel_TLB 時必定會發生,Stack Overflow 這篇【Delphi - How to create Excel PivotChart】也遇到一模一樣的問題,雖無法找到原因,但改以 OLE 重新刻一次後卻可以解決問題。