当前位置: 首页 > news >正文

基于 Delphi 的前后端分离:之五,使用 HTMX 让页面元素组件化之面向对象的Delphi代码封装

前情提要

本博客上一篇文章,描述了使用 Delphi 作为后端的 Web Server,前端使用 HTMX 框架,把一个开源的前端图表 JS 库,进行了组件化。

上一篇文章仅仅是描述了简单的前端代码组件化的可能性,依然是基于前端库的 JS 字符串代码。

接下来,我们要把 JS 的字符串代码,变成 Delphi 的面向对象编程,封装为一个 Delphi 的类。

当然,最终输出给前端的,依然是 JS 字符串代码。

详情开始

需求描述

当前端需要一个图表组件的时候,后端的 Web Server 需要发送这个图表组件的代码。这个图表组件有很多变量,比如 Title,是 Light 模式还是 Dark 模式(显示在图表上,一个是白底,一个是黑底),等等。另外,图表的数据,也需要程序根据外来的数据进行赋值。外来数据,可能是一个来自数据库的 DataSet。

框架描述

因为上一篇博客里面用到的那个图表的 JS 代码,保存在一个外部文件中,我们把这部分代码的需要根据程序运行动态变化的部分拿出来,由我们的 Delphi 的对象去生成。不变的部分,依旧让它在这个外部文件里面,作为一个模板。

做一个包装这个图表的对象,在这个对象里面,处理这个图表有关的数据。这个对象最终将这些数据输出为 JSON 字符串作为前端图表的代码的一部分。

总之,就是把需要动态变化的部分,作为对象的属性参数。把图表的数据也是需要随时可以修改的,作为对象的方法。最终,这个对象,把数据打包为一个 JSON 字符串,然后加载外部模板文件,用这个 JSON 字符串替换掉模板文件里面的标志位,最终输出完整的图表的 JS 代码给客户端。

实现代码

图表组件的代码:

图表组件原本的代码如下:

<div id="chartContainer" style="height: 370px; width: 100%;"></div><script type="text/javascript">var chart = new CanvasJS.Chart("chartContainer", {theme: "light1", // "light2", "dark1", "dark2"animationEnabled: false, // change to true		title:{text: "Basic Column Chart"},data: [{// Change type to "bar", "area", "spline", "pie",etc.type: "column",dataPoints: [{ label: "apple",  y: 10  },{ label: "orange", y: 15  },{ label: "banana", y: 25  },{ label: "mango",  y: 30  },{ label: "grape",  y: 28  }]}]
});
chart.render();</script>

上述代码中,有关图表的数据是写死的。我们要把数据部分拿出来,用 Delphi 的对象来动态创建。抽调数据部分,留下不变的部分,作为模板。

图表组件的模板代码:

<div id="chartContainer" style="height: 370px; width: 100%;"></div><script type="text/javascript">
var chart = new CanvasJS.Chart("chartContainer", #JSON);chart.render();</script>

上述模板代码中,【#JSON】是替换标记,也就是动态的数据部分,需要用我们的 Delphi 的对象的代码来生成。

Delphi 对象的代码

unit UCanvaChart;
{----------------------------------------------------------------------封装来自 CanvasJS 的图表 javascript 代码到 Delphi 中。验证概念的实验性代码。pcplayer 2024-6-15
------------------------------------------------------------------------}interfaceusesSystem.SysUtils, System.Classes, System.IOUtils, System.Generics.Collections, System.JSON;typeTChartTheme = (ctLight1, ctLight2, ctDark1, ctDatk2);TChartType = (Column, Bar, Area, Spline, Pie);typeTCanvasJSChart = classprivateFTheme: TChartTheme;FAnimationEnabled: Boolean;FTitle: string;FChartType: TChartType;FData: TDictionary<string, Integer>;function GetJsonStr: string;function GetThemeStr: string;function GetChartTypeStr: string;function GetChartJS: string;publicconstructor Create;destructor Destroy; override;property AnimationEnabled: Boolean read FAnimationEnabled write FAnimationEnabled;property Title: string read FTitle write FTitle;property ChartType: TChartType read FChartType write FChartType;property Theme: TChartTheme read FTheme write FTheme;property JSONStr: string read GetJsonStr;property ChartJS: string read GetChartJS;procedure AddRecord(const AName: string; const AValue: Integer);end;implementation{ TCanvasJSChart }procedure TCanvasJSChart.AddRecord(const AName: string; const AValue: Integer);
beginFData.Add(AName, AValue);
end;constructor TCanvasJSChart.Create;
beginFData := TDictionary<string, Integer>.Create;
end;destructor TCanvasJSChart.Destroy;
beginFData.Free;inherited;
end;function TCanvasJSChart.GetChartJS: string;
varS, JS: string;
beginS := UTF8Decode(TFile.ReadAllText('MyChart.txt'));JS := Self.JSONStr;Result := S.Replace('#JSON', JS);
end;function TCanvasJSChart.GetChartTypeStr: string;
begincase Self.FChartType ofColumn: Result := 'column';Bar: Result := 'bar';Area: Result := 'area';Spline: Result := 'spline';Pie: Result := 'pie';end;
end;function TCanvasJSChart.GetJsonStr: string;
varjo: TJSONObject;ATheme: string;AData: TJSONArray;AName: string;AValue: Integer;
begin//按照 CanvasJS 的规则封装 JSONAData := TJSONArray.Create;for AName in Self.FData.Keys dobeginAData.Add(TJSONObject.Create.AddPair('label', AName).AddPair('y', Self.FData.Items[AName]));end;jo := TJSONObject.Create;tryjo.AddPair('theme', Self.GetThemeStr);jo.AddPair('animationEnabled', TJSONBool.Create(Self.FAnimationEnabled));jo.AddPair('title', TJSONObject.Create.AddPair('text', Self.FTitle));jo.AddPair('data', TJSONArray.Create.Add(TJSONObject.Create.AddPair('type', Self.GetChartTypeStr).AddPair('dataPoints', AData)));Result := jo.ToString;finallyjo.Free;end;
end;function TCanvasJSChart.GetThemeStr: string;
begincase Self.FTheme ofctLight1: Result := 'light1';ctLight2: Result := 'light2';ctDark1: Result := 'dark1';ctDatk2: Result := 'dark2';end;
end;end.

上述代码中,加载外部文件 MyChart.txt 作为模板文件,然后把生成的 JSON 数据插入模板文件里面,输出为图表组件的 JavaScript 代码字符串。

TCanvasJSChart 有一个 AddRecord 方法,可以多次调用这个方法,给这个对象加入多条用于图表显示的数据。

Delphi WebBroker 的代码

作为 WebServer,当收到来自客户端需要图表的请求时,调用 TCanvasJSChart 类,输入图表相关数据,获得图表的 JavaScript 代码,输出给客户端。

unit WebModuleUnit1;interfaceusesSystem.SysUtils, System.Classes, Web.HTTPApp, UCanvaChart;typeTWebModule1 = class(TWebModule)procedure WebModule1DefaultHandlerAction(Sender: TObject;Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);procedure WebModule1WebActionItem1Action(Sender: TObject;Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);private{ Private declarations }function GetMyChart(const ATitle: string; const AChartType: TChartType;const Animation: Boolean; const ATheme: TChartTheme): string;public{ Public declarations }end;varWebModuleClass: TComponentClass = TWebModule1;implementationuses System.IOUtils;{%CLASSGROUP 'Vcl.Controls.TControl'}{$R *.dfm}function TWebModule1.GetMyChart(const ATitle: string;const AChartType: TChartType; const Animation: Boolean;const ATheme: TChartTheme): string;
varAChart: TCanvasJSChart;
beginAChart := TCanvasJSChart.Create;tryAChart.Title := ATitle;AChart.AnimationEnabled := Animation;AChart.Theme := ATheme;AChart.ChartType := AChartType;//作为实验性代码,这里直接写死数据。//这里的数据,可以是从数据库来的 DataSet,然后对 DataSet 做一个循环,逐条记录用 AddRecord 加入到这个图表里面。AChart.AddRecord('apple', 10);AChart.AddRecord('orange', 25);AChart.AddRecord('banana', 33);AChart.AddRecord('mango', 50);AChart.AddRecord('梨子', 45);Result := AChart.ChartJS;finallyAChart.Free;end;
end;procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject;Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
beginResponse.Content := UTF8Decode(TFile.ReadAllText('index.html'));
end;procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
beginResponse.Content := Self.GetMyChart('This is My Chart Title', TChartType.Column, True, TChartTheme.ctDark1);
end;end.

当客户端浏览器里面,用户点击页面里面的按钮,HTMX 根据页面代码,触发对 WEB 服务器的访问,访问的路径是 /MyChart,这个路径也是写在页面里面的 HTMX 的代码决定的。

Web 服务器端 Delphi 的 WebBroker 框架根据上述路径,触发该路径对应的 Action,这里就是:

procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
beginResponse.Content := Self.GetMyChart('This is My Chart Title', TChartType.Pie, True, TChartTheme.ctDark1);
end;

这个方法里面获得了图表组件,输出给客户端浏览器。浏览器页面上,这个图表显示出来了。

上述代码中,在 function TWebModule1.GetMyChart 这个方法里面,创建了 TCanvasJSChart 这个类的对象,并且给它加入了一些数据,这些数据果然在图表里面显示出来了。

经过测试,在调用这个图表对象的时候,果然可以通过设置不同的属性,改变底色,改变图表的形态(比如,柱状图,饼图,等等)。

当然,本测试程序还需要用到一个 Index.html 文件,在这个文件里面,声明了对 HTMX 的引用,也声明了对这个图表库的引用。这个文件的代码,请看本博客上一篇文章:基于 Delphi 的前后端分离:之四,使用 HTMX 让页面元素组件化

总结

这篇文章是上一篇文章内容的进一步改进。如果你想测试,把这篇文章里面的代码直接放到 Delphi 里面去,加上上一篇文章里面提到的 index.html,是可以直接运行的。

结论就是:完全可以把各路前端库,用 Delphi 的代码封装起来,结合 HTMX ,可以用非常简单,符合编程直觉的方式,用 Delphi 写出漂亮的 WEB 应用。

至于前端漂亮的页面,也可以去下载成熟的页面框架来使用。这一点可以参考本博客前面的一篇文章:基于 Delphi 的前后端分离:之二

相关文章:

基于 Delphi 的前后端分离:之五,使用 HTMX 让页面元素组件化之面向对象的Delphi代码封装

前情提要 本博客上一篇文章&#xff0c;描述了使用 Delphi 作为后端的 Web Server&#xff0c;前端使用 HTMX 框架&#xff0c;把一个开源的前端图表 JS 库&#xff0c;进行了组件化。 上一篇文章仅仅是描述了简单的前端代码组件化的可能性&#xff0c;依然是基于前端库的 JS…...

讲透计算机网络知识(实战篇)01——计算机网络和协议

一、计算机网络和协议 1、网络和互联网络 1.1 网络、互联网、Internet 用交换机、集线器连接在一起的计算机构成一个网络。 用路由器连接多个网络&#xff0c;形成互联网。 全球最大的互联网&#xff1a;Internet。 1.2 网络举例 家庭互联网 图中的无线拨号路由器既是路由…...

8个宝藏APP,个个都牛逼哈拉!

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/ 目前win7已经逐渐淡出人们的视野&#xff0c;大部分人都开始使用win10&#xff0c;在日常工作和使用中&#xff0c;创客们下载神奇的软件能大幅提…...

使用docker构建java应用

1、docker简介 Docker是一个开源的容器化平台&#xff0c;可以帮助开发人员将应用程序及其依赖项打包成一个可移植的容器。容器化是一种轻量级的虚拟化技术&#xff0c;可以使应用程序在不同的操作系统和环境中具有一致的运行方式。 使用Docker带来的好处包括&#xff1a; 简…...

Oracle 存储过程

Oracle存储过程 创建存储过程 CREATE OR REPLACE PROCEDURE UPDATE_EMPLOYEE_SALARY(p_employee_id IN NUMBER,p_employee_salary IN NUMBER )AS BEGINUPDATE employeesSET salary p_employee_salaryWHERE employee_id p_employee_id;COMMIT;EXCEPTIONWHEN NO_DATA_FOUND T…...

下载站名文件

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 得到了请求地址与请求参数后&#xff0c;可以发现请求参数中的出发地与目的地均为车站名的英文缩写。而这个英文缩写的字母是通过输入中文车站名转换…...

345453

38744...

Java操作redis

目录 一&#xff1a;Jedis 二&#xff1a;使用Spring Data Redis Redis 的 Java 客户端很多&#xff0c;官方推荐的有三种&#xff1a; 1.Jedis 2.Lettuce 3.Redisson 同时&#xff0c;Spring 对 Redis 客户端进行了整合&#xff0c;提供了 Spring Data Redis&#xff0c;在S…...

【数据结构(邓俊辉)学习笔记】图03——拓扑排序

文章目录 0. 概述1. 零入度算法1. 1 拓扑排序1. 2 算法 2. 零出度算法2.1 算法2.2 实现2.3. 复杂度 0. 概述 学习下拓扑排序 1. 零入度算法 1. 1 拓扑排序 首先理解下拓扑排序 其实老师经常干这事&#xff0c;如编讲义&#xff0c;将已经知道的知识点串起来变成讲课序列。那…...

C#参数使用场景简要说明

C#参数使用场景简要说明 1、传值参数 方法、类成员的初始化 2、输出参数 方法返回值不能满足&#xff0c;需要多个返回值时&#xff1b; 3、引用参数 方法需要修改变量需带回原变量时&#xff1b; 4、具名参数 代码可读性高&#xff0c;参数可交换位置 5、方法扩展&#xff08…...

线性代数|机器学习-P10最小二乘法的四种方案

文章目录 1. 概述2. SVD奇异值分解3. 最小二乘法方程解4. 最小二乘法图像解释5. Gram-Schmidt 1. 概述 当我们需要根据一堆数据点去拟合出一条近似的直线的时候&#xff0c;就会用到 最小二乘法 .根据矩阵A的情况&#xff0c;有如下四种方法 在r n m 时&#xff0c;SVD奇异…...

【Android面试八股文】你能描述一下JVM中的类加载过程吗?

文章目录 一、Java类的生命周期二、JVM类加载过程1. 加载(Loading)2. 链接(Linking)a. 验证(Verification)b. 准备(Preparation)b.1 准备阶段的初始值b.2 用户定义的初值b.3 常量的初始化c. 解析(Resolution)3. 初始化(Initialization)3.1 什么是 `<clinit>`…...

MYSQL八、MYSQL的SQL优化

一、SQL优化 sql优化是指&#xff1a;通过对sql语句和数据库结构的调整&#xff0c;来提高数据库查询、插入、更新和删除等操作的性能和效率。 1、插入数据优化 要一次性往数据库表中插入多条记录&#xff1a; insert into tb_test values(1,tom); insert into tb_tes…...

鸿蒙轻内核M核源码分析系列二一 02 文件系统LittleFS

1、LFS文件系统结构体介绍 会分2部分来介绍结构体部分&#xff0c;先介绍LittleFS文件系统的结构体&#xff0c;然后介绍LiteOS-M内核中提供的和LittleFS相关的一些结构体。 1.1 LittleFS的枚举结构体 在openharmony/third_party/littlefs/lfs.h头文件中定义LittleFS的枚举、…...

【ARMv8/ARMv9 硬件加速系列 3 -- SVE 指令语法及编译参数详细介绍】

文章目录 SVE 汇编语法SVE 单通道谓词SVE 测试代码 SVE 软件和库支持SVE 编译参数配置-marcharmv8-alseprofilememtagsve2-aessve2-bitpermcryptosve2sve2-sha3sve2-sm4 SVE 汇编语法 在介绍 SVE 汇编指令语法之前&#xff0c;先介绍下如何判断自己所使用的芯片是否实现了SVE功…...

Java版+ SaaS应用+接口技术RESTful API 技术开发的智慧医院HIS系统源码 专注医院管理系统研发 支持二开

Java版 SaaS应用接口技术RESTful API WebSocket WebService技术开发的智慧医院HIS系统源码 专注医院管理系统研发 支持二开 医院住院管理系统&#xff08;Hospital Information System简称HIS&#xff09;是一门医学、信息、管理、计算机等多种学科为一体的边缘科学&#xff…...

工业机器人远程运维,增强智慧工厂运营管理

1、需求背景 随着工业自动化技术的普及和工业机器人应用的增加&#xff0c;制造业对于生产线稳定性和效率的要求不断提高。然而&#xff0c;传统的现场监控方式存在着地理位置限制、实时监控难度大以及诊断能力有限等问题&#xff0c;迫切需要一种更具灵活性和效率的监控方式。…...

理解Python的元类

1.type()函数 type 函数是一个内置函数&#xff0c;用来获取一个对象的类型。它可以接受一个参数&#xff0c;返回这个参数的数据类型。type也可以用来创建类&#xff0c;type就是元类 x333 list["ab"] tuple (1, "a", True, 3.14) dict {name: Alice,…...

web前端黑马下载:探索学习资源的海洋

web前端黑马下载&#xff1a;探索学习资源的海洋 在数字化时代&#xff0c;Web前端技术日益成为互联网行业的核心驱动力。为了跟上这一趋势&#xff0c;众多学习者纷纷投身于Web前端的学习之中。而在这个过程中&#xff0c;“黑马”作为一个备受瞩目的品牌&#xff0c;其Web前…...

最新版jd-gui下载

对于java开发的工程师来说&#xff0c;jd-gui应该是经常会用到的工具了 官网的jd-gui目前只支持到JAVA13&#xff0c;更新版本JAVA编译出来的JAR包就反编译不出来了 此版本支持到了JAVA23 如果需要win以外的其他版本&#xff0c;可以查看我的其他上传 如果不想花积分&#…...

(051)FPGA时钟--->(001)时钟介绍

(001)时钟介绍 1 目录 (a)FPGA简介 (b)Verilog简介 (c)时钟简介 (d)时钟介绍 (e)结束 1 FPGA简介 (a)FPGA(Field Programmable Gate Array)是在PAL (可编程阵列逻辑)、GAL(通用阵列逻辑)等可编程器件的基础上进一步发展的产物。它是作为专用集成电…...

Java程序员英语单词通关:

Java程序员英语单词通关&#xff1a; abstract - 抽象的 boolean - 布尔值 break - 打断 byte - 字节 case - 情况&#xff0c;实例 catch - 捕获 char - 字符 class - 类 continue - 继续 default - 默认&#xff0c;通常 do - 做&#xff0c;运行 double - 双精度…...

数据库开发-Mysql03

目录 1. 多表查询 1.1 概述 1.1.1 数据准备 1.1.2 介绍 1.1.3 分类 1.2 内连接 1.3 外连接 1.4 子查询 1.4.1 介绍 1.4.2 标量子查询 1.4.3 列子查询 1.4.4 行子查询 1.4.5 表子查询 1.5 案例 2. 事务 2.1 介绍 2.2 操作 2.3 四大特性 3. 索引 3.1 介绍 3…...

0-1 背包问题(动态规划 查询背包元素)

描述 给定n种物品和一个背包&#xff0c;物品i的重量是Wi​&#xff0c;其价值为Vi​&#xff0c;问如何选择装入背包的物品&#xff0c;使得装入背包的物品的总价值最大&#xff1f; 在选择装入背包的物品时&#xff0c;对每种物品i只能有两种选择&#xff0c;装入或者不装入…...

elasticsearch快照生成与恢复

Elasticsearch快照生成与恢复的场景主要涉及到数据的备份与恢复需求。当需要对Elasticsearch集群中的数据进行备份&#xff0c;或者在数据丢失、损坏等情况下需要恢复数据时&#xff0c;就可以使用快照功能。 快照生成的方法通常包括以下步骤&#xff1a; 1、创建一个快照仓库…...

178.二叉树:最大二叉树(力扣)

代码解决 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* Tre…...

跨境电商中的IP隔离是什么?怎么做?

一、IP地址隔离的概念和原理 当我们谈论 IP 地址隔离时&#xff0c;我们实际上是在讨论一种网络安全策略&#xff0c;旨在通过技术手段将网络划分为不同的区域或子网&#xff0c;每个区域或子网都有自己独特的 IP 地址范围。这种划分使网络管理员可以更精细地控制哪些设备或用…...

【C++】stack、queue和deque的使用

&#x1f497;个人主页&#x1f497; ⭐个人专栏——C学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 目录 导读 一、stack 1. stack介绍 2. stack使用 二、queue 1. queue介绍 2. queue使用 三、deque 1. deque介绍 2. deque的…...

通过SSH远程登录华为设备

01 进入系统编辑视图 system-view Enter system view, return user view with return command. 02 创建本地RSA密钥对 [HUAWEI]rsa local-key-pair creat The key name will be:HUAWEI_Host The range of public key size is (2048 ~ 2048). NOTE: Key pair generation will ta…...

算法day27

第一题 515. 在每个树行中找最大值 首先是遍历每层的节点&#xff0c;将每一层最大值的节点的值保留下来&#xff0c;最后将所有层的最大值的表返回&#xff1b;具体的遍历每层节点的过程如上一篇故事&#xff1b; 综上所述&#xff0c;代码如下&#xff1a; /*** Definition …...