基于 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 的前后端分离:之二

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/27811.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

windows下的eclipse按Ctrl+Shift+F格式化代码不起作用的处理

1、先上张图&#xff1a; 上面Format&#xff1a;CtrlShiftF&#xff0c;按了以后不起作用。 2、这个快捷键不起作用的原因&#xff1a;可能是快捷键冲突了。 机器上装了Sougou输入法&#xff0c;将输入法切换为英文模式是起作用的。 那么应该就是这个原因了。 3、解决方法…

【Ardiuno】使用ESP32单片机创建web服务通过网页控制小灯开关的实验(图文)

经过实验测试ESP32单片机的网络连接还是很方便的&#xff0c;这里小飞鱼按照程序实例的代码亲自实验一下使用Esp32生成的网页服务来实现远程无线控制小灯开关功能&#xff0c;这样真的是离物联网开发越来越近了&#xff0c;哈哈&#xff01; 连接好开发板和电路&#xff0c;将…

React入门教程:构建你的第一个React应用

在当今快速发展的Web开发领域&#xff0c;前端技术日新月异&#xff0c;而React作为一款强大的JavaScript库&#xff0c;已经成为众多开发者的首选。React以其组件化、高效的性能和灵活的数据处理机制闻名于世&#xff0c;被广泛用于构建动态且复杂的用户界面。在本教程中&…

Elixir学习笔记——输入输出和文件系统

本章介绍输入/输出机制、文件系统相关任务以及相关模块&#xff08;如 IO、File 和 Path&#xff09;。IO 系统提供了一个很好的机会来阐明 Elixir 和 Erlang VM 的一些思维模式和新奇思想。 输入输出模块 输入输出模块是 Elixir 中读写标准输入/输出 (:stdio)、标准错误 (:s…

vue学习(一)

1.vue是用于构建用户界面的渐进式js框架&#xff0c;自底向上逐层的应用&#xff1a; 简单应用&#xff1a;只需一个轻量小巧的核心库&#xff1b; 复杂应用&#xff1a;可以引入各式各样的vue插件&#xff1b; 2.vue特点&#xff1a; 采用组件化模式&#xff0c;提高代码复…

Python中关于电商商品数据的采集【taobao/JD/商品详情数据返回】

在Python中采集电商商品数据&#xff08;如淘宝、京东等&#xff09;通常涉及到网络爬虫&#xff08;web scraping&#xff09;或称为网络数据抓取&#xff08;web data scraping&#xff09;。由于电商平台通常会有反爬虫机制&#xff0c;因此直接抓取数据可能会遇到各种挑战&…

基于51单片机的智能水表

一.硬件方案 本设计主要以51单片机作为主控处理器的智能水表&#xff0c;该水表能够记录总的用水量和单次用水量&#xff0c;当用水量超出设定值时系统发出声光报警提醒&#xff0c;水量报警值能够通过按键进行自行设置&#xff0c;并且存储于AT24C02中&#xff0c;并且可以测…

C++11默认成员函数控制

默认成员函数有 如果自己不显示声明&#xff0c;那么默认编译器会自己生成一个 如果有一个构造函数被声明了&#xff0c;那其他的默认函数编译器就不会再生成 这样的有时又不生成&#xff0c;容易造成混乱&#xff0c;于是C11让程序员可以控制是否需要编译器生成。 显式缺省函…

H5112B 降压恒流芯片12V24V36V48V60V72V100V 1.2ALED 调光无频闪光滑细腻

H5112B多功能LED恒流驱动器是一款具有良好性能与高度集成度的驱动芯片。以下是该产品的主要优点及应用领域的详细分析&#xff1a; 产品优点&#xff1a; 宽电压输入范围&#xff1a;H5112B支持5V至90V的宽电压输入范围&#xff0c;使其能够适应多种不同的电源环境&#xff0…

(虚拟机)VMware软件的安装及Ubuntu系统安装

一、VMware软件的安装 软件下载&#xff0c;可以自己找或者百度网盘下载&#xff1a; 通过百度网盘分享的文件&#xff1a;ubuntu16…等2个文件 链接:https://pan.baidu.com/s/1VEnZKY9DJ1T1vC3ae20gKQ 提取码:11b6 复制这段内容打开「百度网盘APP 即可获取」 1、解压VMwar…

本地生活元宇宙 橘子浑身都是宝,吃对营养加倍

橘子是秋冬季节的应季水果 含有丰富的营养价值 而且浑身都是宝 但你知道吗 吃橘子也是有讲究的 如何吃橘子才能营养最大化&#xff1f; 橘子有哪些食用禁忌&#xff1f; 一起来看看吧&#xff5e; 划重点 一颗橘子&#xff0c;3大营养 橘子具有开胃理气、止渴润肺、治胸膈结气、…

06--jenkins构建CI_CD

前言&#xff1a;上一篇文章整理了git的部署和使用&#xff0c;这章主要复习持续集成软件Jenkins&#xff0c;这个技术现在在云计算方面也是有应用的&#xff0c;同时也是越高级越智能的软件代表。 1、概念简介 1&#xff09;jenkins是什么 Jenkins是一个开源的、可扩展的持…

Linux 终端窗口设置为透明

Linux 终端窗口设置为透明 打开终端 右键鼠标 选择Profile Preferences 点击Background 选择 Transparent background 拖动滑条调整透明度 完成。

Nintex流程平台引入生成式人工智能,实现自动化革新

工作流自动化提供商Nintex宣布在其Nintex流程平台上推出一系列新的人工智能驱动改进。这些增强显著减少了文档化、管理和自动化业务流程所需的时间。这些新特性为Nintex流程平台不断扩展的人工智能能力增添了新的亮点。 Nintex首席产品官Niranjan Vijayaragavan表示&#xff1a…

甄嬛传熹贵妃上户口:如果让他陪你过冬天,那朕能不能睡中间?贝叶斯模型推导爸爸去哪儿

关注微信公众号 数据分析螺丝钉 免费领取价值万元的python/java/商业分析/数据结构与算法学习资料 背景 《甄嬛传》是大家耳熟能详的宫廷剧&#xff0c;其中复杂的宫斗情节和深刻的人物刻画让人津津乐道。甄嬛因为与皇帝(四郎)闹翻了&#xff0c;去甘露寺待了一段时间&#x…

Navicat和SQLynx产品功能比较一(整体比较)

Navicat和SQLynx都是数据库管理工具&#xff0c;在过去的二十年中&#xff0c;国内用户主要是使用Navicat偏多&#xff0c;一般是个人简单开发需要&#xff0c;数据量一般不大&#xff0c;开发相对简单。SQLynx是最近几年的数据库管理工具&#xff0c;Web开发&#xff0c;桌面版…

PyTorch C++扩展用于AMD GPU

PyTorch C Extension on AMD GPU — ROCm Blogs 本文演示了如何使用PyTorch C扩展&#xff0c;并通过示例讨论了它相对于常规PyTorch模块的优势。实验在AMD GPU和ROCm 5.7.0软件上进行。有关支持的GPU和操作系统的更多信息&#xff0c;请参阅系统要求&#xff08;Linux&#xf…

代码安全问题0

该篇目主要是记录日常遇到的代码安全问题的记录 # 清空会话的RAM del combined_list gc.collect()# 重新读取上述合成的NPZ文件为一个新的文件 combined_arrays [] for i in range(1, batch_count 1): # 从1到batch_count1&#xff0c;包括剩余的最后一个文件data np.load(…

SpringBoot使用jasypt实现数据库信息的脱敏,以此来保护数据库的用户名username和密码password(容易上手,详细)

1.为什么要有这个需求&#xff1f; 一般当我们自己练习的时候&#xff0c;username和password直接是爆露出来的 假如别人路过你旁边时看到了你的数据库账号密码&#xff0c;他跑到他的电脑打开navicat直接就是一顿连接&#xff0c;直接疯狂删除你的数据库&#xff0c;那可就废…

字符数组基础知识及题目

死识。。。 字符该如何存储呢&#xff1f;这一点我们在以前就接触过了。用char来存储。 如何输入一个单词呢&#xff1f; char a[10002]; scanf("%s",a); 就不用地址符了。 如何输入句子呢&#xff1f; char a[100002]; gets(a); gets是读入句子的&#xff0c…