Blazor是一个使用 .NET 生成交互式客户端 Web UI 的框架。目前社区刚起步,相关的组件并不多,有幸有一群爱好者正在努力建设社区,我作为社区一员也来贡献一些内容。这里我就分享分享我封装G2Plot后的Blazor组件ant-design-charts-blazor。

ant-design-charts-blazor属于Ant Design of Blazor的一个开源项目,我们的目标是做Ant Design在Blazor生态圈最优秀的实现,废话不多说,先来看一下目前实现的图表。

安装与使用

项目地址:

ant-design-blazor/ant-design-charts-blazorgithub.com

安装 Nuget 包引用

$ dotnet add package AntDesign.Charts

wwwroot/index.html(WebAssembly) 或Pages/_Host.razor(Server) 中引入静态文件:

<script src="https://unpkg.com/@antv/g2plot@latest/dist/g2plot.js"></script>
<script src="_content/AntDesign.Charts/ant-design-charts-blazor.js"></script>

_Imports.razor中加入命名空间

@using AntDesign.Charts

最后就可以在.razor组件中引用啦!

<Line Data="data" Config="config" />@code{object[] data = new object[] {new  { year= "1991", value= 3 },new  { year= "1992", value= 4 },new  { year= "1993", value= 3.5 },new  { year= "1994", value= 5 },new  { year= "1995", value= 4.9 },new  { year= "1996", value= 6 },new  { year= "1997", value= 7 },new  { year= "1998", value= 9 },new  { year= "1999", value= 13 },
};LineConfig config = new LineConfig(){title = new Title(){visible = true,text = "曲线折线图",},description = new Description(){visible = true,text = "用平滑的曲线代替折线。",},padding = "auto",forceFit = true,xField = "year",yField = "value",smooth = true,};
}

组件的使用相对于原先的G2Plot并没有太多变化,主要针对C#语法的特点增加了强类型支持,属性命名规则符合C#语法习惯等。

所以这篇文章不想多聊如何使用的问题,我想谈谈在对G2Plot封装的过程中遇到的一些困难和实现方式,希望能为Blazor社区做出一些绵薄的贡献,如大佬看了有更好的实现方式希望告知在下。

Config对象的封装

G2Plot是通过传入一个Config对象来配置图表的呈现内容,而且只需要给出必要的参数即可,这一切设计的都非常合理。

但是用C#实现的时候就遇上了问题,当Config对象通过InvokeVoidAsync传到JS后,对象会包含所有属性,这对于G2Plot来说就画蛇添足了,有些默认属性会被带进来的空属性给覆盖了,图表显示就出现了问题。

当然最简单的就是把值为Null的属性从Config剔除,嘿嘿,我就是这么干的。通过一个递归遍历对象中的所有属性,移除没有值的属性。

function isEmptyObj(o) {for (let attr in o) return !1;return !0
}function processArray(arr) {for (let i = arr.length - 1; i >= 0; i--) {if (arr[i] === null || arr[i] === undefined) arr.splice(i, 1);else if (typeof arr[i] == 'object') removeNullItem(arr[i], arr, i);}return arr.length == 0
}function proccessObject(o) {for (let attr in o) {if (o[attr] === null || o[attr] === undefined) delete o[attr];else if (typeof o[attr] == 'object') {removeNullItem(o[attr]);if (isEmptyObj(o[attr])) delete o[attr];}}
}//清除没有值的项
function removeNullItem(o, arr, i) {let s = ({}).toString.call(o);if (s == '[object Array]') {if (processArray(o) === true) {if (arr) arr.splice(i, 1);}} else if (s == '[object Object]') {proccessObject(o);if (arr && isEmptyObj(o)) arr.splice(i, 1);}
}

一个问题解决了,有一个问题来了,JS/TS是弱类型语言,他对象中的属性可以在实例化时任意添加,C#可不行,他被定义约束的死死的,这就遇到下面两个问题:

  1. 有些配置属性不可能穷举所有的属性定义。

  2. 而且作为组件的封装者不可能无时差的跟进G2Plot对Config定义的变化。

针对这两个问题,我们需要C#中有一个类似的弱类型的对象,这时候我看上了object,它可以定义成任何类型,如果用它做Config参数,可谓是一劳永逸,岂不美哉。

理想是美好的,现实是残酷的。如果用object,那么C#的强类型就彻底丧失,编写图表配置时完全没有提示,基本就是盲写,太恐怖了,C#的优雅荡然无存。

这时突然脑子冒出个念头,为啥不能使用两个配置,一个是强类型的,一个是object,然后将他们合并成最终的配置给到G2Plot,完美。

基本原理就是通过一个递归将传入JS的配置对象挨个对比,并将它们合并,JS实现代码如下

function deepObjectMerge(source, target) {for (var key in target) {if (source[key] && source[key].toString() === "[object Object]") {deepObjectMerge(source[key], target[key])} else {source[key] = target[key]}}return source;
}

Blazor中使用方法如下(部分代码)

<Tabs><TabPane Key="1"><Tab>示例1</Tab><ChildContent><Line Data=data1 Config="config1" OtherConfig="otherConfig1" /></ChildContent></TabPane>
</Tabs>@code{LineConfig config1 = new LineConfig(){XField = "date",YField = "value",};object otherConfig1 = new{= new object[]{new{Visible = true,data =new object []{new { date = "2019-05-01", value = 4.9 },new  { date = "2019-10-01" } },Label = new{Visible = true,Field = "festival",},},},};
}

G2Plot对象方法调用

通过Config生成的图表是静态的,没有生命的死物,我们需要通过调用它的一些函数给予他生命,比如更新配置,更新数据,设置高亮等等。

那么问题就来了,IJSRuntime提供的InvokeAsync方法只能返回可以序列化成json的对象,通过new G2Plot得到的对象不属于此列,我们如何在C#代码中得到G2Plot对象就是个难题了。

好在Blazor提供了一个ElementReference对象给我们,它只能作为Html对象的引用,没法作为JS对象引用,有点可惜,不过它给我们分配了唯一的Id,既然如此,我们就曲线救国。

第一步,得到div的ElementReference

<div @ref="Ref"></div>@code{protected ElementReference Ref;
}

第二步,构造的G2Plot对象,并放到一个叫“chartsContainer”的JS对象中,以Ref.Id作为索引

const plot = new G2Plot[type](domRef, config);
plot.render();
window.AntDesignCharts.chartsContainer[domId] = plot;

第三步,如果需要调用G2Plot对象的方法,那么直接拿着Ref.Id去“chartsContainer”中找到对象后调用,下面是“changeData”方法的实现

changeData(domId, data, all) {if (window.AntDesignCharts.chartsContainer[domId] == undefined) return;window.AntDesignCharts.chartsContainer[domId].changeData(data, all);
},

第四步,组件销毁时及时清理“chartsContainer”中不用的对象

public async void Dispose()await JS.InvokeVoidAsync(InteropDestroy, Ref.Id);
}

到目前已经完成绝大部分功能,还有一些功能需要进一步技术攻关,欢迎有兴趣的网友共享你们的代码,项目进度情况如下

https://github.com/ant-design-blazor/ant-design-charts-blazor/issues/2

相关连接

微软Reactor_SH活动:从 HelloWorld 到 AntDesign,Blazor 将 .NET 带到现代前端圈

https://www.bilibili.com/video/BV17z4y1Q7cJwww.bilibili.com

一套基于 Ant Design 和 Blazor 的企业级组件库

https://github.com/ant-design-blazor/ant-design-blazorgithub.com

如果您在使用的过程中碰到问题,可以通过以下途径寻求帮助,同时我们也鼓励资深用户通过下面的途径给新人提供帮助。

用Blazor技术封装G2Plot实现Charts组件相关推荐

  1. java项目中可以封装的组件_封装属于自己的组件库

    封装属于自己的组件库 - VUE 「适合人群:一起聊聊组件这件事」 「观看时间:30min」 「说明:一些经验分享,欢迎探讨」 前言 现目前,vue 社区下大大小小的组件库是越来越多,优秀的开源组件也 ...

  2. JS组件系列——封装自己的JS组件,你也可以

    前言:之前分享了那么多bootstrap组件的使用经验,这篇博主打算研究下JS组件的扩展和封装,我们来感受下JQuery为我们提供$.Extend的神奇,看看我们怎么自定义自己的组件,比如我们想扩展一 ...

  3. react封装函数_GitHub - daifee/react-component: 用 React 封装一套常用组件

    Daifee-React-Component 用 React 封装一套常用组件 所有组件都是 stateless 组件,但为方便,某些组件提供 API 调用方式的静态方法. [TOC] 使用前须知 源 ...

  4. 一步一步封装自己的HtmlHelper组件:BootstrapHelper

    前言:之前学习过很多的Bootstrap组件,博主就在脑海里构思:是否可以封装一套自己Bootstrap组件库呢.再加上看到MVC的Razor语法里面直接通过后台方法输出前端控件的方式,于是打算仿照H ...

  5. 学会使用ant design封装一个锚点组件

    我是歌谣 放弃很容易 但是坚持一定很酷 封装一个锚点组件就是要知道一个父子组件的一个传值 很显然 父亲这边传过去一个数组 然后就可以进行循环遍历得到一个新的数值 这边注意 当我们进行一个map返回值得 ...

  6. Vue封装下拉框组件时,为document绑定原生事件addEventlistener(click“),切换页面之后事件还未被摧毁...

    1 <script> 2 export default { 3 props: ["lists"], 4 data() { 5 return { 6 isactive: ...

  7. element ui 多个子组件_ElementUI 技术揭秘(2) 组件库的整体设计

    需求分析 当我们去实现一个组件库的时候,并不会一上来就撸码,而是把它当做产品一样,思考一下我们的组件库的需求.那么对于 element-ui,除了基于 Vue.js 技术栈开发组件,它还有哪些方面的需 ...

  8. C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)...

    阅读目录 一.NumberBoxExtensions 二.DateTimeBoxExtensions 1.初始方案 2.改进方案 三.TextareExtensions 四.SelectExtensi ...

  9. 初探 amaze-vue( 基于vue.js封装的Amaze UI 组件库)

    Amaze UI 是以移动优先(Mobile first)为理念,面向 HTML5 开发的国产优秀组件库.因官方未提供vue.js版本,而且民间一直对vue.js版本的 Amaze UI 组件库呼声很 ...

最新文章

  1. Python的逻辑判断和循环 || 打印九九乘法表
  2. VC++学习(6):菜单编程
  3. 与mysql数据库的交互实战_实战教程丨五步教你优雅地在Windows下安装MySQL数据库...
  4. 如何获取网站icon
  5. LeetCode 第 29 场双周赛(890/2259,前39.4%)
  6. java自定义栈类代码,异常堆栈和自定义类
  7. TensorFlow tf.keras.layers.Dense
  8. java 与sas交互_SAS与MACRO的交互使用
  9. Spring使用内存数据库
  10. 天线分类特征学习笔记(1)偶极子天线
  11. 中控考勤机Java二次开发(附Demo)
  12. KUCAS清关文件申请形式 TER与TIR认证介绍
  13. 饭谈:盘点六种错误的技术问题提问
  14. ai绘画到底有大强?我开发了一款ai绘画(来看看)
  15. 几个吓尿了的公众号,前几个你不可能没听过
  16. Labview2018视频教程(共51节)
  17. 线性回归假设条件及残差检验
  18. C++STL算法 search你是我的半截的诗
  19. 使用JavaScript进行销毁:操作指南
  20. Guava学习笔记(转)

热门文章

  1. 中国古代历朝首都一览
  2. 14.6.3.1 The InnoDB Buffer Pool
  3. 查询表结构的语句总结
  4. Oracle中的USEREVN()
  5. MyEclipse6.5配置反编译插件
  6. 学习kaneboy的 sps区域列表的管理入口程序
  7. java避免使用orderby_java – @OrderBy在JPA中无法正常工作
  8. qmediaplayer获取流类型_Java 流API
  9. 能上架App的GooglePlay开发者账号获取流程
  10. [收藏]REST -维基百科