本章节介绍如何在 IBAX区块链平台 编写一个简单的应用程序。
应用程序开始时很简单,随着教程的深入,其复杂性也在增加。
应用程序的最终版本在数据表中存储为简单消息(字符串),其中包含时间戳和消息发送者的帐户标识符。用户可以访问这些消息列表并从应用程序页面添加新消息。该应用程序的页面可以从生态系统菜单中访问。
Weaver
Weaver 是 IBAX区块链平台 的唯一客户端,Weaver 为所有用户和生态系统角色提供功能。应用程序开发人员可以在 Weaver 中开发和测试应用程序,生态系统管理员使用 Weaver 来管理生态系统,用户可以通过 Weaver 与生态系统应用进行交互。
在本教程中,你将在 Weaver 中编写智能合约代码、页面模版代码和执行其他所有操作。Weaver 还提供一种恢复、保存和执行智能合约代码,管理数据结构(数据表),分配访问权限和创建应用程序的方法。
每个节点都有自己的 Weaver 实例。
您的第一个简单的应用程序为"Hello,World!"。
应用程序在表中存储为字符串。它没有任何用户页面。
具有 Developer 角色的帐户可以使用生态系统的"root"特权。默认情况下,该角色可以访问所有操作。在新的生态系统中,创始人帐户被分配给Developer角色。您必须使用这个帐户来引入生态系统的重大更改,比如创建新的应用程序和数据表。
使用创始人的帐户登录生态系统。
一旦您作为生态系统的创始人登录,您就可以创建一个新的应用程序。
创建新应用程序:
转到 Developer 选项卡;
在左侧的菜单中选择 应用程序;
在 应用程序 页面选择 创建;
在 名称 字段中指定应用程序的名称;
在 更改条件 指定
true
:
true
表示任何人都可以更改应用程序;另一种选择是指定
ContractConditions("MainCondition")
。除了创始人之外,将禁止任何人对进行应用程序更改。您的应用程序将显示在应用程序列表中,单击指定应用程序的名称字段使其激活。
在 *Developer*选项卡中选择应用程序可以更轻松地导航与所选应用程序相关的资源,它对生态系统没有影响。 无论选择哪一个,所有生态系统应用程序仍然可用。
要存储数据,应用程序需要一个数据表。在 Weaver 创建该数据表。
创建数据表:
在 Developer 选项卡,选择 应用程序 > APP > 数据表;
这显示所选应用程序的所有数据表。如果该列表为空,则您的应用还没有任何数据表。
单击 创建;
Weaver 将显示创建数据表页面。
在 名称 字段中指定您的数据表名称;
本教程使用
apptable
数据表名称。添加列。名为
message
,其类型为Text
;对于读写权限,在每个字段指定为
true
;这将允许任何人在数据表上执行插入条目、更改条目、添加列和读取条目数据;
作为选项,您可以限定读写权限给创始人帐户,在这种情况下, 请在该字段中指定为
ContractConditions("MainCondition")
。
每个智能合约都有三个部分,更多请参阅:智能合约结构 。
-
在 Developer 选项卡选择 应用程序 > APP > 智能合约;
这将显示所选应用程序的所有智能合约。新应用程序该列表将为空。
-
单击 创建;
将在编辑器中打开一个新的智能合约模版。
空智能合约模版如下所示:
contract ... {
data {
}
conditions {
}
action {
}
}
首先,给智能合约命名。
contract AppContract {
}
填写 data
部分。
在如下示例中,Message
是变量名称,string
是其类型。
data {
Message string
}
填写 conditions
部分。简单的验证条件是指定的字符串不能为空,如果 Message
长度为 0
,则智能合约将在执行时生成带有已定义的消息警告。
conditions {
// avoid writing empty strings
if Size($Message) == 0 {
error "Message is empty"
}
}
填写 action
部分。 简单的操作是将 Message
写入数据表中。
action {
DBInsert("apptable", {message: $Message})
}
以下部分是完整智能合约的代码。
IBAX区块链平台 的所有智能合约都像这样构建,包含 data
、conditions
和 action
部分。
contract AppContract {
data {
Message string
}
conditions {
// avoid writing empty strings
if Size($Message) == 0 {
error "Message is empty"
}
}
action {
DBInsert("apptable", {message: $Message})
}
}
智能合约准备进行测试:
如果成功添加了字符串,则结果将包含引入更改交易的区块ID和结果代码。
{
"block": "31",
"result": null
}
在智能合约生效之后,是时候把它扩展成更有用的东西了。在这部分中,您将实现UI和其他功能。
该应用程序将字符串存储在表中,就像日志中的条目一样。每个字符串都有一个作者和一个时间戳。
用户可以从应用程序页面查看存储的字符串列表,此时该页面是一个简单的表格。
与之前一样,从 Developer 选项卡 > 应用程序 > APP > 数据表 页面编辑数据表;
将以下字段添加到 apptable
数据表:
-
author
字段,类型Number
,更改 设置为true
;该字段将存储作者帐户的标识符。
-
timestamp
字段,类型Date/Time
,更改 设置为true
。
更改智能合约代码来处理作者ID和时间戳。
作者ID是生态系统帐户ID。时间戳是以Unix时间格式执行智能合约的日期和时间。
这两个值都由 预定义变量 提供。所以无需输入或验证预定义变量,因此仅在操作部分中进行更改。
更改智能合约,以便在添加消息时将作者的ID和时间戳写入数据表中。作者的ID由 $key_id
定义,时间戳由定义 $time
。
action {
DBInsert("apptable", {message: $Message, author: $key_id, timestamp: $time})
}
对于此部分,应用程序的页面是一个显示存储在表中的信息的简单页面。
就像所有其他资源一样,可以在 Weaver 中创建UI页面:
-
导航到 Developer 选项卡 > 应用程序 > APP > 页面;
-
单击 创建;
可视化设计器将在新选项卡中打开。
默认页面为空。您可以使用预定义的结构快速填充页面。
创建一个基本的表单:
-
在右侧的视图选择器中,单击 视图化(Designer);
视图将切换到可视化设计器。
-
从左侧菜单中,选择 Search > Table With Header 并将其拖到页面上。
将出现包含多个元素的表格。
IBAX区块链平台 的用户页面用 模版语言 编写。您需要为页面编写代码,因此请切换到开发者(Developer)的视图。
切换到开发者(Developer)视图。
-
在右侧的视图选择器中,单击 开发者。
视图将切换到包含页面代码的代码编辑器。
目前为止,页面模版并没有做什么。接下来就得更改代码,以便页面显示来自 apptable
表的数据。
-
想要请求表中数据,使用 DBFind 函数;
以下示例中该函数调用从
apptable
表中获取数据,并将其放入src_table
源中。并按时间戳字段对其进行排序。该src_table
源稍后用作页面上表视图的数据源。DBFind(Name: apptable, Source: src_table).Columns(Columns: "author,timestamp,message").Order(timestamp)
-
想要显示
src_table
源中的数据,在Table
函数中将其指定为一个源以及列标题。Table(Columns: "AUTHOR=author,TIME=timestamp,MESSAGE=message", Source: src_table)
-
在右侧的视图选择器中,单击 预览 以检查数据是否正确显示。
以下是该部分的完整页面代码。该基本页面将在稍后进行扩展。
DBFind(Name: apptable, Source: src_table).Columns(Columns: "author,timestamp,message").Order(timestamp)
Div(Class: panel panel-primary) {
Div(Class: panel-heading, Body: Table block)
Table(Columns: "AUTHOR=author,TIME=timestamp,MESSAGE=message", Source: src_table)
Div(Class: panel-footer text-right) {
Button(Class: btn btn-primary, Contract: ContractName, Body: More)
}
}
单击 保存 以保存页面:
- 在 页面名称 字段中为页面指定
AppPage
或任何其他名称; - 在 菜单 中选择
default_menu
; - 指定 更改条件 为
true
; - 单击 确认。
在前面的部分中,您创建了一个智能合约,一个用于存储数据的表,以及一个用于显示该数据的基本UI页面。
在该部分中,您将确定最终的应用程序,因此它的外观和操作类似于实际应用程序。
页面需要链接到一个菜单,例如,在 Home 选项卡上显示的 default_page
页面链接到默认生态系统菜单 default_menu
。
由于应用程序教程很简单(只有一个页面),因此无需为其创建单独的菜单。默认菜单中的新菜单项就足够了。
您可以通过在 Developer 选项卡 > 应用程序 > APP > 页面 中编辑页面属性来定义页面显示的菜单。例如,如果您的应用程序有多个页面,则可能需要创建一个菜单以在这些页面之间导航并将其分配给应用程序的所有页面。
与所有其他资源一样,可以在 Weaver 中创建和编辑菜单:
-
导航到 Developer 选项卡 > 菜单;
-
单击
default_menu
条目名称;编辑器将在新选项卡中打开。
-
将新菜单项添加到模版的末尾。该菜单项将打开应用程序页面。该图标来自 FontAwesome 图标集。
MenuItem(Title:Messages, Page:AppPage, Icon:"fa fa-envelope")
-
单击 保存。
检查新菜单项是否有效:
Logicor 中的按钮可以执行智能合约和打开页面,具体取决于参数。Button 函数有智能合约的两个参数:
-
Contract
激活的智能合约名称。
-
Params
智能合约的输入参数。
要将数据发送到智能合约,请将表单添加到应用程序页面。该表单必须具有消息的输入字段,以及将激活AppContract合约的按钮。
以下是该类表格的示例。它嵌套在自己的 Div 中。将它放在包含表单视图的Div元素之后,该表单定义了 Input 字段有一个已定义的名称 message_input
。按钮使用这个名称向合约发送 Message
参数值。最后,Val 函数用于获取输入字段的值。
Div(Class: panel panel-primary) {
Form() {
Input(Name: message_input, Class: form-control, Type: text, Placeholder: "Write a message...", )
Button(Class: btn btn-primary, Body: Send, Contract: AppContract, Params: "Message=Val(message_input)")
}
}
您可能会注意到通过发送消息测试该新功能时,表单不会刷新。这将在 页面刷新 介绍。
页面上的默认表格视图第一页仅显示25个条目。添加一个简单的导航,允许用户导航所有表格条目。
该导航将使用两个按钮。每个按钮都会重新加载应用程序的页面并将参数传递给它。
- Previous 按钮将显示前25个条目。如果没有其他条目,则不会显示该按钮;
- Next 按钮将显示下25个条目。如果没有其他条目,则不会显示该按钮。
该导航需要两个变量来存储表视图状态:
#table_view_offset#
该变量存储当前表视图偏移量。
导航按钮将在重新加载页面时将其作为参数传递。
#record_count#
该变量存储表中的条目总数。
将计算该值。
要计算 #record_count#
,请修改现有的 DBFind 函数调用。在 . count()
调用中指定的变量将存储条目计数。
DBFind(Name: apptable, Source: src_table).Columns(Columns: "author,timestamp,message").Order(timestamp).Count(record_count)
必须在打开页面时将表视图偏移传递给页面。如果 #table_view_offset#
未获得值则指定为 0
。
将以下代码添加到页面的顶部。
If(GetVar(table_view_offset)){ }.Else{ SetVar(table_view_offset, 0) }
再次修改 DBFind 函数调用。这次它必须使用新的表视图偏移量。
DBFind(Name: apptable, Source: src_table).Columns(Columns: "author,timestamp,message").Order(timestamp).Count(record_count).Offset(#table_view_offset#)
找到定义页脚的 Div 函数调用:Div(Class:panel-footer text-right)
。将按钮代码添加到其中。
Div(Class: panel-footer text-right) { }
Previous 按钮只有在至少有一个 Next 要返回时才会显示。当添加按钮时,将计算页面的新表视图偏移量 offset_previous
。参数被传递到重新打开页面的 PageParams
参数中。
If(#table_view_offset# >= 25) { SetVar(offset_previous, Calculate(#table_view_offset# - 25)) Button(Class: btn btn-primary, Body: Previous, Page: AppPage, PageParams:"table_view_offset=#offset_previous#") }
仅当总记录数大于页面上显示的数量时,才会显示 Next 按钮。当添加按钮时,将计算页面的新表视图偏移量 offset_next
。参数被传递到重新打开页面的 PageParams
参数中。
If(#record_count# >= Calculate(#table_view_offset# + 25)) { SetVar(offset_next, Calculate(#table_view_offset# + 25)) Button(Class: btn btn-primary, Body: Next, Page: AppPage, PageParams:"table_view_offset=#offset_next#") }
添加按钮后,保存页面并从 Home > Messages 菜单项进行测试。
实现的最后一项功能就是自动更新位于页面上的表格,当用户发送新消息时,它必须显示在表格中。
除了执行合同之外,您还可以通过 Send 按钮重新打开当前页面来实现这一点。必须将 #table_view_offset#
参数传递到该页面,而不进行任何更改。
添加 Page
和 PageParams
参数到 Send 按钮,代码如下所示:
Button(Class: btn btn-primary, Body: Send, Contract: AppContract, Params: "Message=Val(message_input)", Page:AppPage, PageParams:"table_view_offset=#table_view_offset#")
这部分介绍了应用程序页面的许多更改。以下是该应用程序页面的完整代码。
If(GetVar(table_view_offset)){
}.Else{
SetVar(table_view_offset, 0)
}
DBFind(Name: apptable, Source: src_table).Columns(Columns: "author,timestamp,message").Order(timestamp).Count(record_count).Offset(#table_view_offset#)
Div(Class: panel panel-primary) {
Div(Class: panel-heading, Body: Table block)
Table(Columns: "AUTHOR=author,TIME=timestamp,MESSAGE=message", Source: src_table)
Div(Class: panel-footer text-right) {
If(#table_view_offset# >= 25) {
SetVar(offset_previous, Calculate(#table_view_offset# - 25))
Button(Class: btn btn-primary, Body: Previous, Page: AppPage, PageParams:"table_view_offset=#offset_previous#")
}
If(#record_count# >= Calculate(#table_view_offset# + 25)) {
SetVar(offset_next, Calculate(#table_view_offset# + 25))
Button(Class: btn btn-primary, Body: Next, Page: AppPage, PageParams:"table_view_offset=#offset_next#")
}
}
}
Div(Class: panel panel-primary) {
Form() {
Input(Name: message_input, Class: form-control, Type: text, Placeholder: "Write a message...", )
Button(Class: btn btn-primary, Body: Send, Contract: AppContract, Params: "Message=Val(message_input)", Page:AppPage, PageParams:"table_view_offset=#table_view_offset#")
}
}
本教程将介绍生态系统的基本应用程序。它没有为应用程序开发者解析其他重要的主题,比如布局样式、管理访问权限以及应用程序和资源之间的交互。有关这些高级主题的更多信息,请参阅其他文档。