outsystems从入门到精通
outsystems需要讲解的内容
下载、安装
官网地址:https://www.outsystems.com/downloads/,需要翻墙
下载后得到一个安装包,安装到指定目录,安装完成。
概述
outsystems的架构
outsystems的架构
outsystems由图中的各个部分组成
Platform Server
提供应用程序的编译、部署、管理、运行、监视服务。
Server Studio
Server Studio是安装在开发者的电脑上的,该应用程序用于连接Platform Server,同时也是outsystems开发环境。一旦连接上了Platform Server,开发者可以创建和发布应用到Platform Server。每个版本的应用将会被存储在Platform Data数据库。Platfrom Server会编译生成那些应用的代码,然后部署它们到标注Application Server上。
Application Server
application server使用传统数据库和外部系统来运行你创建的程序。
Integration Studio
允许你创建平台外的扩展。Integration Studio提供了大量的加速器去集成额外的资源例如C#和数据库。Integration Studio提取那些资源然后在outsystems的世界里创建展示品(representation),一旦展示品存在于扩展,它们可以被发布到platform server和被Service Studio使用(使用起来如同普通的outsystems资源一样)。
----以上四个部分用于创建应用----
----以下这个部分用于管理Platform Server本身----
Service Center
Service Center是一个Platform Server管理器和管理员控制台。它是一个WebApplication,可以通过浏览器访问。它允许你以一个管理者和操作者的身份查看和配置platform server。Service Center里面的Factory允许开发者查看哪些应用是可用的。开发者也可以监视环境和由平台和应用程序生成的检查日志。开发者可以配置环境。开发者可以收集不同类型的分析。
LifeTime
LifeTime允许我们跨多个环境管理完整的应用生命周期,LifeTime也是一个WebApplication,作为Service Center的能力扩展。它允许开发者查看应用的生命周期的不同阶段。从开发环境到质量检查环境到生产环境。你也可以管理每个用户或团队在他们的环境或应用中拥有的权限等级。你可以看到基础设施处于的环境和管理它们如何被放置于生命周期中。你可以收集你的应用程序的性能分析,那样你可以更简单地查明和解决性能瓶颈。
架构总结
我们可以看到不同的组件围绕着platform server,允许我们开发自己的应用程序,允许我们管理应用程序。
让我们看看典型的企业,我们将会有一个开发服务器和一个生产服务器。所有这些工具(LifeTime、Service Center、Server Studio、Integration Studio)可以连接各自的服务器(infrastructure)。LifeTime可以管理所有服务器的生命周期,不管你有多少服务器。
你在官网上也可以找到forge和community来帮助你使用outsystems进行工作。forge是一个类似于market的东西,在上面可以下载开源组件让你加快开发速度。community是一个社区,你可以和别人交换tips、讨论话题、问问题,不管你是outsystems新手还是outsystems老手。
企业级的outsystems应用架构
outsystems的能力
1.integration with everything
你可以用outsystems集成你现有的代码,成为一个outsytems项目,然后在Service Studio中可以找到你所集成的东西。
2.visual design and development
应用程序界面、逻辑、数据库的开发都是可视化的。
3.standard and optimized
当你完成了代码的部署,程序会自动生成标准的html、css、js、.net代码以运行Web或者Mobile application。
4.continuous integration and delivery
在完成了部署之后,outsystems会对你的代码进行一个整合检查,确保程序依赖的正确和所有的代码都是可用的。
outsystems会对不同角色进行权限管理,让不同的角色都有正确的权限访问和控制他们所需的资源。
outsystems对代码的监控,让你看到代码执行的效率。
5.flexible infrastructure
你可以将代码部署在本地或云服务器上。(我不确定是不是这个意思。官方教程的原话是:"The infrastructure can either run on the cloud or on-premise, so you have the flexibility of deploying the application' code to any kind of infrastructure and set of servers.“)
outsystems开发流程总结
开发流程
Modular Programming
在这章,我将会介绍模块编程以及app在outsystems中的结构是怎么样的。producer和consumer mudules是什么。
模块化编程:
每个module应该封装所有的东西,以便在模块中执行app众多功能中的一个功能。同时将app将分离出多个独立的功能和潜在的可替换代码片段。
在outsystems,modules是存放用户图形界面、业务逻辑代码的地方。
modules有几种类型
App Type(Phone app or web app)
用于创建用户图形界面。你的用户图形界面是什么,取决于你选择了什么类型的App Type。Blank Modules
里面什么都没有的module。Service Modules
在里面实现服务,专注于domain和面向服务的架构。Library Modules
创建高度可重复使用的组件。Extension modules
集成数据库或你自己的C#代码。
Modules被创建与应用一起生存,如下图。
图中Finance、Projects等5个圆圈代表Module
Modules可以分享元素给其他modules,如下图。
不同app之间的modules
分享者被称为producer modules,使用者被称为consumer modules。
producer和consumer可以来自于不同的app
Service Studio
Service Studio是主开发平台。
Three Types of Programming Models
Service Studio可以创建三种不同的应用(different programming model),分别是Reactive Web、Tablet Web、Mobile App,其中Mobile App分为Tablet App 和Phone App。下面将讲解Reactive App和Mobile App的特性和能力以及如何选择创建哪种应用。
Reactive Web Application
在电脑、平板、手机上均可使用。
终端用户的访问方式:用户通过浏览器访问
限制:无法访问硬件。
创建的界面为响应式布局界面,适应各种大小的屏幕。
获取最新版本应用的方式:刷新网页
Mobile Application
Mobile App创建的界面为手机页面,只能在手机终端使用。
终端用户的访问方式:通过手机App/平板App访问或支持PWA的浏览器访问。
可以访问硬件,比如Touch ID。
获取最新版本应用的方式:自动更新机制。
共同点
无论采用Reative Web App还是Mobile App,在屏幕背后,outsystems都会在应用发布后生成HTML、CSS、JS组成应用。
在运行的时候,JavaScript负责生成HTML元素,执行客户端逻辑,而数据在需要时是通过异步获取的。
它们运行的效率没有区别,因为它们都用相同的技术栈。
如何选择创建Mobile App还是Reactive Web App?
应用是否需要有离线功能,Mobile App支持离线数据存储,而Reactive Web App不支持。
应用是否需要发布到AppStore,如果需要,那么Mobile App是最好的选择。
跟着软件自带的教程创建Mobile App
打开了主界面后先跟着软件自带的教程走一遍,创建Mobile App。
service studio主界面
创建一个Reative Web App
在上一章跟随了软件自带的教程创建了一遍Mobile App,现在来学习如何创建Reative Web App。跟着我的步骤走,你就可以创建出一个Reative Web App。
点击New Application

点击Start from scratch(从零开始的意思)

点击Reative Web App

修改icon、app名称、app描述后,点击create app,当然你也可以什么也不修改,只填写app名称也可以创建。

点击create module

创建模块后自动进入到模块当中
开始构建app
先创建一个Screen(screen在这里大概是模板的意思)

outsystems提供多种screen给你选择,但在本教程中我们选择创建Empty screen

当你修改完你创建的screen之后(在本教程中我加了Hello、Hello World到screen中),点击那个被绿色包围的大大的“1”,那么你的应用就会被Service Studio上传到outsystems的platform server。接着platform server会编译、部署代码

当应用被发布完成了之后,点击蓝色的按钮就会自动打开浏览器并跳转到部署好的web页面现

在你可以访问网页了,当然访问该网页也是需要翻墙的。
Data
Modeling Data
这一节我将介绍在outsystems中建模数据(model data)的一些思路。注意在这里我说“建模数据”而不是“创建数据”,我认为二者是不一样的,建模数据代表只是创建真实数据应该遵守的存储规则,而不是直接创建真实的数据。
当我们在讨论建模数据的时候,我们要知道真实数据它是要包含app重要信息的,这些信息通常需要在databases中被存储和恢复(retrieve)。app真实数据代表不同业务模型,在outsystems,那些模型被建模(be modeled)并被entities引用。
让我们使用一个购物app的数据模型创建作为例子。一个购物app需要有跟踪订单以及查询订单中有哪些产品的业务。订单要有一个优先级的概念,要有状态的概念。我们可以让这些业务模型展示成一个个entities,这些entities会被定义和创建在我们的开发环境中。这些entities会有属性,entity和entity之间会有关系。
下面这张图展示购物app的五个entities和他们之间的关系

entities在outsystems中会成为数据库的表,entities的属性会成为表的列(Column),id属性(Identifier attributes)会成为表的主键,引用类型的属性(Reference Attribure)会成为表的外键(foregin key),entities的索引会成为table的索引。entities可以有记录或实例,所以如果我们有多个customer实例,在customer表中的这些行有时候称为元组(refer to as tuples)。记录和实例/元组和行指的是同一个东西。
下面这张图展示了outsystems entities和database table之间的映射

所以创建数据实际上要先理清楚app有哪些业务,然后创建各业务对应的entitiy以及entities之间的关系,outsystems会自动帮我们将entites映射到关系型数据库(relational database)形成一张张表和关系。
Database Entities的介绍
在上一节,我们探讨了在outsystems中创建数据的一些思路。在这一节,我们将会探索entities如何拥有属性,entities属性有哪些基本数据类型(basic data types)以及特殊的ID属性。在本节最后,我会介绍一下内置实体行为(built-in entity action)。
所以什么是entity呢?entity是允许我们存储和访问app所需信息的东西。每个业务模型都应该有entity,有了entity我们就可以存储数据到app和访问app中的数据,每一条你在entity中插入的记录将会被存储到database table的row中。
entities对应存储的是业务模型,entities的属性存储业务模型属性,比如在购物app中,我们有订单(Order)业务模型,在订单业务模型中有截止时间(DueDate)属性,那么首先要创建一个Order entity对应上订单(Order)业务模型,接着Order entitiy应该要有一个DueDate属性用来对应上订单(Order)的业务模型属性DueDate。
默认情况下,entity被创建时就带有id属性,它唯一标识了entity中的每一条记录(如果需要,可以修改此属性)。它作为数据库表的主键,它为entities间建立关系提供支持。除了ID,entities被要求至少拥有一个其他属性。和ID一样,其他属性被映射到database的列。
关于基本数据类型(basic data types),在outsystems中存在几种选择。
alphanumeric(含字母或数字的)类型
像text、电话号码、email均可使用numeric(数值的)类型
像integer、long、decimal、currency均属于numeric的范畴Dates(日期) and Times(时间)类型
Date和Time既可以结合在一起,又可以分开独立Logic类型
值为True或FalseLarge Object类型
像存储图片的二进制数据类型(binary data type)Referential类型
像EntityID就应使用Referential类型作为属性
这六种基本数据类型都有默认值,也就是说entitiy的属性(or say 变量)永远都有值。
下图是六种基本数据类型,以及它们的默认值

如果属性没有指定另外的值,那么属性值就是如上图所示的默认值。
在outsystems中,属性名的定义是有规范的,因为outsystems会根据你的属性名去推断你的数据类型。我们无需手动定义属性的数据类型。
下图为outsystems官方给出的数据类型推断机制

看到图中最后两种数据类型以及User Identifier数据类型,它们在官方视频教程中没有被提及,这里做一个先放着,我会另写一篇文章通过详细测试了解Entity Record、Entity Record List、User Identifier的涵义。写完了之后我会把另一篇的文章链接放在下一行,供大家理解这三种多出来的数据类型。
entity内置实体行为(built-in entity action),这些行为对应的是普通CRUD(Create、Retrieve、Update、Delete),它们对entity的行(row)进行操作。
内置实体行为在Server Studio中如下图展示

内置实体行为是outsystems自动创建的,且不可被修改。这些内置实体行为可以被使用到应用的业务逻辑中,用于操作entity中的数据。
如何创建Database Entities?
在上一节,我们认识了什么是entity、Database Entities的6种基本数据类型、outsystems根据entities属性名自动推断出对应数据类型的机制以及属性名和数据类型的推断关系表、outsystems为每个实体生成的内置实体行为。在这一节中,我将介绍如何在Server Studio中创建Database Entities。
首先到我们的开发环境(Server Studio),打开一个module,打开后看到界面的如下图

选中右上角Data标签(tab),在Data标签中,有一个叫Entities的文件夹(folder),右键点击Entities,然后点击Add Entity to Database

给新建的entity起个名,然后点击左边的三角形(expand triangle)展开

从上图展开的Entity中我们可以看到,outsystems预先在entity中创建了一个Id属性,还有一些实体行为(entity actions)。
我们右键点击Customer,创建两个实体属性(Add Entity Attribute),点击Id属性

我们可以看到在底部有Id属性的信息:名字为Id、数据类型为Long Integer、是否自动排序(Is AutoNumbered)为Yes。
点击Email属性

我们看到底下的表格中显示,Data Type自动被设置成了Eamil。
最后,创建完了entities我们要让outsystems根据entites在服务器(platform server)中生成物理数据库(physical database)的表,我们只需要点击最上方的一键发布按钮(one-click publish button)

现在entity已经创建好了,并且可以在database中存储数据。
如何用excel快速导入真实数据到entity?
这一节,我将给大家展示如何快速从excel中导入(bootstrap)真实数据到entity。
我们准备了以下这个excel文件,包含了一些customer的信息。一张表(sheet),四列(Name、Email、PhoneNumber)。

让我们用entity的预览数据看目前有哪些数据在customer entity,右键点击Customer entity,点击View Data

正如我们所看到的那样,Customer entity中没有数据
我们现在要往Customer entity中导入数据,右键点击Customer entity,鼠标覆盖在Advanced选项上,点击Create Action to Bootstrap Data from Excel选项
双击我们预先准备好的Excel文件
弹出一个提示框,里面张表格(table)
Service Studio会分析Excel文件,然后尝试匹配excel的列和entity的属性,弹出的提示框展示了Service Studio的对Excel列和Customer属性的映射执行(mapping performed)情况。如我们所见,Excel文件的PhoneNumber列没有匹配到任何属性。映射的机制是基于Excel列和entity属性名和数据类型,如果它们不匹配,那么database中对应字段将不会有数据。
点击proceed创建逻辑和定时器
Server Studio会导入数据到customer entity
点击一键发布按钮会上传编译部署模块
在成功部署模块之后,数据将会被导入到entity,让我们看看data具体的怎样被导进来的
在processes标签中,Timers文件夹包含了导入customers的定时器(timer),定时器是在我们点击procceed的时候,Service Studio自动创建的。
在outsystems,定时器是后台作业(background jobs),在这个例子中,我们可以看到timer的Schedule属性值为When Published,意味着当模块被发布的时候该timer会被执行。
双击上上图的BootstrapCustomers,Server Studio会跳转到关联了的Logic(逻辑)标签,
下图展示了如果entity已经包含数据,逻辑流程(logic flow)将只做简单地验证,如果entity中没有数据,它将会加载excel文件的每一行,在database中创建对应的customer。
让我们回到data标签再一次预览data,因为timer在我们执行一键部署的时候已经被执行,因此data被成功地插入到了database table。
如何用excel快速创建entity并导入真实数据?
上一节我们学会了如何往已经存在的entity中导入数据,这一节,我们将学习如何用用excel创建entity并导入数据。
我们准备了以下这个excel文件,里面有两张表。表一有Name、Description、Price三列,代表产品。
表二有Name一列,代表产品品类(category)

现在我们切换到Server Studio,要导入entity和它们的data,我们需要打开Data标签,右键点击Database,选择Import New Entities from Excel...

双击我们的excel文件

点击IMPORT
如我们所见,两张表被导入了进来,并且Server Studio为我们自动创建了Id属性
我们看到两个entity的名字分别为Sheet1和Sheet2,而不是Product和Category。是因为本教程准备的excel文件中表的表名为Sheet1和Sheet2,如果想导进来的Entity名字为Product和Category,需要在导入之前将Sheet1和Sheet2改名为Product和Category。由于本人的制作失误造成您的不便,望谅解。
最后只需点击一键发布按钮,ServerStudio执行BootstrapCustomers逻辑,数据就插入到了database。
Static Entities的介绍
在这节,我们将介绍什么是static entities,研究static entity拥有的相关特性——属性和记录(attributes and records) 。
什么是static entities?
static entites是entity的一种特殊类型,它们允许你创建一个预定义的值,在你的app里面使用。
让我来深入探讨什么叫”预定义值列表,在app里面使用"。
static entity类似于枚举(enumeration)——a list of items in a collection。
在outsystems,这些items称为记录(records)。static entities拥有属性和记录,鉴于它们的静态特质,记录在运行时(runtime)是无法改变的。改变记录只能发生在开发设计应用的时候。
下面我们来创建一个static Entity
static entites的一个特性让它们与普通entity区别开来。static entity只有一个entity action——the Get<Entity> action。因为这些entities是静态的,它们不能在运行时被改变,只能被读取。所以当其他entities有多个entity action的时候,static entity只有一个
默认情况下,当我们创建一个static entity,Server Studio自动帮我们创建了四个属性——Id、Label、Order、Is_Active,你可以改变这些默认的属性
在本教程中,我们往static entity中添加一个属性Color Code,你也可以添加其他的属性
右键点击Records文件夹,创建三条记录High、Medium、Low
每一条记录都有一个标识(Identifier)

每个记录都属性

回到最初的问题:什么是预定义值,在app里面使用。
static entity由记录和属性组成,记录和属性的关系是属性决定记录,不同的属性值造就不同的记录。对于outsystems应用来说,记录就是一个个值,存放在database里面,万年不变,提供给app使用。
UI Development
在这一节,我们来讲讲screens——outsystems的UI构建模块之一。我们会向你展示screens如何被创建,screens的内容和元素,变量和输入。最后,我们会在screen的层面上说一些关于客户端的逻辑。
在进入screen定义和详情之前,让我们来看看大概。
App由多个screens组成,它们本质上是app的不同的页面,提供不同的功能或者数据。
这些screens之间可以相互连接
让终端用户浏览整个应用程序。
所以screen是什么?screen是outsystems中UI模块的构建工具之一。
每个screens由几个其他的元素组成,这些元素可能包含links、buttons、images、tables等等。这些元素在screen中的不同组合让开发者创造出多种多样的screen。
screen可以从空空如也的canvas开始
screen也可以从预先做好了的screen(outsystems提供的模板)开始,然后定制你的具体需求。
我们来看看screen更详细的部分。我们可以看到这里展示了很多不同的元素,这些元素在outsystems中叫小部件(widget)
我们来剖析这些元素
如我们所见,一个简单的screen由很多的元素组成,而上图展示的这些元素只是outsystems中提供的一小部分。
另一个重要且有趣的方面关于小部件是响应能力(responsiveness)。同样的页面会由不同的表现,取决于什么用户使用app。比如手机端用户会如下图展示
这种转变是自动的。
正如我们前面所提到的,终端用户在不同的screens间浏览,在一些情况下,用户需要在不同的screens之间传递信息,数据传递将最终在一个特定的页面向用户给展示数据。
举个例子,为了从产品列表导航到特定产品的详细信息,我们需要唯一标识那个我们想看的产品。这种信息(可以通过输入参数(input parameters)在screens中被传递。
本地变量对暂时存储上下文相关信息(relevant contextual information)是有用的,注意,不论发生什么情况,本地变量只在当前screen中存在。所以当我们切换到其他screen,目标screen不会有权限访问到前一个screen的本地变量。这些本地变量的作用域和它所在的screen绑定。
button或链接生效的时候可以触发逻辑的执行。例如,点击Save button会触发逻辑流(logic flow)更新数据库里的数据。
这些流或者screen actions定义在screen级别(screen level),它们的执行跑在客户端,可能转而调用(invoke)其他在服务端执行的逻辑。
screen里的元素、小组件自动对数据的更改做出反应。所以,如果变量值在逻辑流中被改变,UI将会立刻自动更新反馈这些改变。
我们已经看到button可以触发执行逻辑流,而链接和button非常的类似,两者都可以通过点击触发action。它们两者主要的不同在于它们在界面级(interface level)的展示方式。
如何使用Expression小部件
这一节,将教你学会如何使用expression小部件,准备的材料有:一个被创建好的screen。
为了添加expression小部件,我们简单地从左边的工具箱中拖拽然后把它扔到screen里面。
Service Studio自动弹出一个expression编辑器(editor),里面的框框是实际输入expresison的地方。expression将会在运行时计算,然后生成文本(text)展示给终端用户。

下面我来演示一些简单的基于expression的运算,比如数字的乘法。

点击Done,然后发布模块。

看看浏览器中显示的结果。

如我们所见,在运行时,终端用户可以在浏览器中看到文本,该文本为计算后的值。
我们可以在Service Studio中调整预览,用example属性(properties)。example属性只有开发的时候才能被用,example用来改变Service Studio的预览值,在运行时是没有作用(no effect)的。
我们再来添加一个表达式在screen上,我们也可以连接字符串值来创建一系列的字符串,数字和字符串可以被连接到一起形成另一个字符串。
我们再来添加一个表达式,我们有一个if条件,如果布尔测试为true,会展示true对应的值,为false则把false对应的值放在screen上。
让我们来再一次发布模块来看三种表达式在运行时的表现。
如何使用Button和link小部件?
这一节,你将会学习到如何使用Button和link小部件。准备材料:空白的screen,命名为Calculator。
现在,我们来添加一个输入参数到screen,然后设置它为non-mandatory,这个输入用于接收我们制造的计算结果。
现在创建一个button,用于让Number变量增加。选择button,在右下角中我们看到On Click property(属性),设置该属性可指定当button被点击时执行什么action。我们选择继续停留在当前screen,并且增加Number输入参数。
现在我们来展示number输入参数的值,所以我们可以测试这个场景,然后看screen的结果,拖拽一个变量或者输入参数到screen创建expression来展示它的值,让我们来发布应用然后在浏览器中测试。
正如我们期望的那样,每点击一次increment按钮数字就加一。

回到Service Studio,我们删除expression,添加另一个按钮取名为submit,这个按钮会导航到另一个screen,传递当前的输入参数number值。

创建一个screen,名字取为Result。在这个里面screen我们来添加一个输入参数取名为Value,数据类型设置为整型。拖拽输入参数到screen canvas会创建一个表达式展示值。

一键部署
我们在浏览器中点击increment button三次,点击submit button。应用会将当前number值参数到result screen,在result screen中显示数字3

接着我们来学习links,我们在result screen中添加一个link取名为Restart。Onclick property设置为Caculator screen
一键部署后,我们在浏览器中看一下效果
点击restart回到Calculator screen,Number变量恢复为0。
如何使用If小部件?
在本节,你将学习到如何使用If小部件。材料:一个screen取名为IfWiget;两个button,一个取名为Increment,一个取名为Decrement;一个本地变量取名为Number。一个If小部件
双击If小部件的If
在If小部件的False分支里面再次创建一个If形成嵌套
里面的If的条件为Number > 0
现在再加入一些simple text
给Increment和Decrement分别加上两个Action用于让本地变量Number的增减,由于action超出了这节教程的范围,暂不给予展示。
最后一键部署后效果
Logic
在这一节,我们将
讨论行为(actions),以及如何定义logic,以及如何使用它们达到代码重用。
为了重用代码,我们将要创建actions,逻辑流(logic flow)是含于actions的,actions里面的流程就叫logic flow。
本质上,Action是在outsystems中logic可以被定义的地方。取决于action的类型,logic将会被执行于server端或client端。
让我们来看看什么是action flow。每个action的里面都有一个logic flow。
如下图,所有的节点加起来就是一个logic flow,而每一个节点(node)都是一个工具(tool),每一个工具都可以用来建造logic。从某种程度上来说,可以将一个tool/node等同于成一个logic。在actions中,每个logic flow都至少有一个Start node和End ndoe作为程序的开始点和结束点。

我们可以有ifs/switches来创建条件路径,或者assigns来设置已存在变量的值。在Screen actions中,可以有Destination node来重定向用户到指定的screen。Download node可以用于某些情景,比如在web apps中,用来允许终端用户下载文件。这段我们讲的都是tools的例子和功能,在实际应用开发中我们可以使用它们。
Actions的种类和特性
Screen Action
Screen actions在screen中定义,它们通常绑定screen中的widgets。比如,当按钮按下去的时候,screen action也许被执行。
因为它们定义在screen里面,它们的作用域只包含screen里面定义的东西。所以,除了screen widgets,作用域也包含定义在screen里面的aggregates(下一个章节中会讲)、screen中的输入参数、本地变量。
Client action
它们和Screen action类似,然而,client actions在整个module中都具有作用域,它的作用域不绑定具体的Screen。这两种类型都是在浏览器或设备中执行。
Server action
它允许在server端中定义logic。
代码重用是什么?
当我们说代码重用,我们实际上是指logic部分的代码重用。在这三种actions中,都可以使用logic,logic就是那个被封装起来了可重用的东西。使用logic可让开发者更好地维护它们的代码和聚焦于更小的问题去解决。
Actions中可以定义的变量
在一个应用中,你将定义几个screen actions、client actions、server actions。而不同actions中允许定义的变量是不一样的。
在screen actions中你可以在里面定义输入参数(input parameters)、本地变量(local variables),
而在client actions和server actions你可以定义input parameters、local variables、output parameters。
Actions之间的调用规则
在一个app模块里面,将会有几个screens,在每个screen中可以定义多个screen actions,screen actions可以调用在同一个screen底下的其他screen actions,但不能使用其他screens底下的screen actions。
相似地,client actions可以调用其他client actions。server actions可以调用其他server actions。
Screen actions虽然不能调用其他的screen里面的screen actions,但是能调用client actions。
Client actions虽然不能调用screen actions但是能调用server actions。
Screen actions和client actions都可以调用server actions,但是需要通过网络去调用。
Actions定义的位置
Screen actions在interface标签下面创建。
Client Actions在Logic标签下面的Client Actions文件夹中创建。
Server Actions在Logic标签下面的Server Actions文件夹中创建。
Client actions和server actions都可以被设置成function。当actions被设置成了function,这约束它们只能有一个output parameter,使它们可以在expressions中被使用。它们和普通的actions的flow是一样的,当一个client action被设置成function,它只能调用其他client actions,不能调用server actions。
Variables
在这一节,我们将讨论变量和它们如何被使用,包括所有的变量类型,比如input parameters、output parameters、local variables。我们还会讨论两种复合数据类型(compound data types): structures和lists。
Variables可以保存任何数据类型的数据。被定义的变量将会存在于一个特定的作用域中,这意味着只有在该作用域中才可以访问和修改该变量。
如果执行离开了作用域范围,那么系统会销毁变量。因此,当action完成,执行(execution)移动到action外面的作用域,变量就会被销毁。
在outsystems中,只有三种类型的变量:input parameter、local variable和output parameter。
我们可以看一个server actions的例子,
在该BookRoom action中,定义了三种类型的变量,让我们来更详细地讨论它们。
变量的类型
Input parameter
Input parameter允许你从调用者的作用域传递值到目标元素(screen或action)的作用域。
每个input parameter有几个属性,其中一个属性用于定义input parameter是否是强制性需要的的。
Output parameter
Output parameter允许你从目标action的作用域返回值到调用者的作用域。
在output parameter定义的作用域里面,output parameter应该被赋值。
Input parameter和output parameter之间除了传递的方向不一样外,还有变量生存范围的区别,output parameter会继续存在于调用者的作用域中,而当execution跳出了目标元素(screen或action)时,input parameter就会被销毁。
Local variables
这些变量会唯一存在于目标元素的作用域中。它们可以被赋值,在作用域中被使用,这就是为什么我们称它为local variable。类似于input parameters,local variables被销毁当execution跳出目标元素作用域。
我们看到了不同类型的变量,一个重要的方面是outsystems语言是强类型的,这意味着所有的变量必须被定义数据类型,而且在整个程序执行过程中,数据类型不会改变。
outsystems中支持的数据类型
Basic Types: Integers, Text, Dates等等
Compound types: Entities, Structures
Lists
下面来介绍Structure数据类型。
在Structure数据类型中,可以定义多个数据,其中这些数据的数据类型可以选择Basic Types或Compound types或Lists。
Structure数据类型的变量,用于存储一个或多个数据在存储器中,而Basic Data Types只能存储一个数据在存储体中。
Structure数据类型和Entity数据类型的区别是,在Structure数据类型中,你可以通过给变量赋值从而达到将数据存储到存储体中的目的,而在Entity数据类型中,你可以通过给变量赋值从而达到将数据存储到关系表中的目的。
为了创建一个structure数据类型变量,我们只需要在Data标签下,右击Structures文件夹,点击Add Structure。
除了设置structure名字,你还可以设置它的Public属性为Yes或No,如果为Yes,则在其他modules中也可以使用该structure,如果为No,则在其他modules中不可用该structure。
现在来设置一下你的structure吧,假如你设置该structure的data是否为mandatory(强制性需要)设置为No,那么structure中的data可以设置default值。
下面来介绍List数据类型
List数据类型定义的位置在变量的Data Type Editor中。如下图。
Lists负责存储相同数据类型元素。
DEMO
How to Create Loops

How to Use JavaScript in Logic

图中用了属于JavaScript API的FeedbackMessage,它的在outsystems官方documentation中可以找出:
Exceptions
在这一节中,我们将讨论Exception如何自动或手动地产生或者抛出。
我们还会介绍我们如何用异常处理流(exception handler flow)优雅地处理它们,或者使用全局exception handler。
当应用在运行中一个操作失败了就会产生exception。当在运行时抛出,一个exception将会当前logic flow被中断,然后execution跳到exception handler中。程序继续excecute exception handler flow,而且不会返回到原来的flow。Exception可能会从平台中自动产生,例如,当data在database里面被操作时违反了约束。
Exception还可以从代码中被显式地产生,例如,当终端用户提供的数据是不可用的。
所以什么叫做execution跳到exception handler flow?它意味着无论exception产生于什么时候,execution跳到最具体的exception handler,然后它将会执行handler流。所以如果一个database exception出现了,然后存在一个database exception handler flow的话,那么execution就会跳到后者。理解这一点非常重要,因为一个action可以有多个exception, security, database, communication以及custom user exceptions。
在action flow中不强制必须有exception handler。
当没有在action flow中找到匹配的exception handler flow,execution会跳到外部区域。换而言之,它会一直冒泡直到找到匹配的handler。
这就是为什么需要定义有全局exception handler。默认情况下,一个存在于外部公共UI流,但是它可以被修改以满足应用需求。全局exception handler是最外层的区域,因此它应该可以处理所有exceptions。
Aggregates
我们将会定义Aggregates是什么,然后我们会聊Aggregates的能力。我们将会聊Aggregates的源,过滤数据,分类数据。
Aggregate是outsystems中的一个可视化元素,它允许你从entities种查询数据。你可以定义一个entities源,根据一些规则查询数据,或者按需分类数据。大多数应用都需要从database中获取数据。Aggregates允许我们以我们以可视化的方式创建查询。
下面这张表是一个从是一个Aggregate Editor,它允许我们可视化地定义一个database查询。在Editor的最顶部是四个标签,允许我们添加不同的data source, create filters, define sorting, test query。
Aggregate的创建和维护相对较简单,因为editor的查找有点像excel电子表格,我们不需要SQL的详细知识。
Aggregate的四个能力的详情:
我们进入到了editor,我们可以通过展开source标签来查看source entities。在Aggregate中的sources将会决定数据从哪里被获取。
Aggregate支持超过一个entity作为source,虽然在这个例子里面我们只用一个Customer entity。

一旦我们定义了Aggregate的souces,我们接下来要做的是定义Aggregate filters。所以除了获取所有的customers,如果我们只想要获取那些特定的customers的名字——名字长度长于某个值,我们可以添加如下表达式。

当你加了filters, the preview data会自动地更新,然后只展示匹配所有filters的记录(records)。
多个filters可以被叠加,在preview中展示"所有过滤器为真"的records。
每个filter通常有一些logical operator,比如=, <>等等。
我们也许会希望使用一些内置functions来操作dates或其他值,比如CurrDateTime(),这对于获得未来日期的date record是非常有用的。
在过滤数据后,我们来做一些sorting。
我们可以来到sorting标签,然后添加一个或多个sorts,在这个例子中,哪一列是需要我们排序的呢?
Sorting可以有上升(Ascending)和下降(Descending)。
我们可以添加多个sorts。如果我们添加了多个sorts,第一个sort优先,第二个sort在第一个sort的基础上排序,以此类推,最终显式所有排序完成后的结果。
我们也可以设置test values,让我们来到test values标签。

Aggregate中使用的任何变量都将显式为输入字段(input field)。如果我们给input field一个值,它将会执行查询,然后刷新data preview。这个test value与application的执行是分离的。这只是为了让我们看到在特定的例子中output可能是什么,当我们打开编辑器的时候。
Aggregate Outputs and Properties
在这节中,我们将会讲Aggregates的outputs和properties,然后我们看看Aggregate如何翻译成sql。
当我们谈到Aggregates的outputs。

第一,Aggregate根据定义 的查询返回records list,records list有几个重要的properties,比如iterator相关properties、Length和Empty。这些properties允许你迭代(iterate)Aggregate返回的records list。
The Current匹配定义在source标签下的entities的type。而EOF和BOF是两个boolean fields(布尔值类型的字段),它们代表iterator的end和beginning。current row number从0开始,一直到list中records总数-1。另一个重要的方面是Current默认会指向list中第一个record。如果没有record,current会包含它数据类型的默认值。至于Length,这是list中所有records的总数。如果没有records匹配Aggregate中定义的规则,the empty将会为true。最后,Aggregate有Count output parameter,Count是所有匹配查询规则的records数量,而不是像List中的Length一样是所有records的总数不管是否匹配查询规则。
每一个Aggregate除了那些定义为sources filters和sorting外,还有一些其他属性。

在Max.Records中可以限制Aggregate返回的最大数量records。注意这个records的最大值属性不影响我们之前看到的Count output parameter,底层的sql查询被改变查询数量的records数量的多少。我们同样有执行sql的read-only property,它允许我们查看这个Aggregate生成的sql。生成的sql是database特定的,它由outsysmtes负责生成(take care)。
在最后,虽然创建Aggregate不需要sql知识,下面是对sql查询的翻译。它发生于编译步骤(当module被发布的时候)。

不管在sources标签下发生了什么,将会以FROM子句结束。而filters以多个ands拼接到WHERE子句结束,sort规则被翻译到ORDER BY子句,以Aggregate中定义的Aggregate。test values在编译期被忽略,因为它们只在开发过程中被使用到而在运行时中不起作用。
对于select部分,outsystems编译器自动地检测哪个attributes需要被获取基于application代码。比如,如果只有customer名字在application代码中被使用,outsystems编译器会通过代码分析检测到,然后Aggregate查询将会只获取该attribute。如果你改变你的app代码然后使用更多或更少的attributes,编译器会重新分析代码,然后select子句会重新计算需要查询的attribute。
DEMO
How to Fetch Data in an Aggregate
在这个DEMO中,我们将会向你展示如何在outsystems中创建一个Aggregate,用于获取data从database。在这个module中,我们已经定义了Customer entity,它已经包含了一些数据。让我们切换到Logic标签然后在GetCustomer Server Action中创建一个Aggregate
有两种方式可以创建Aggregate:
第一种方式
step1

step2

第二种方式
step1

How to Filter Data in an Aggregate


How to Sort Data in an Aggregate

How to Test Values in an Aggregate

Buiding Screens with Data
在这一节我们讲如何在Screen中创建data并展示data。
两种获取data的方式:直接从database,或在其他高级案例中从其他sources获取,然后我们将像你介绍如何将获取到的data展示到screen上(使用一些内置widgets)。我们使用的大多数app都需要有将data展示到screen上的功能,无论它的data是在database中持久化的还是在其他扩展systems上存在。data在screen中可以有多种形态展示。在outsystems中,我们有几种widgets可以用来去展示data。
Two Ways to Fetch Data
在展示data之前,我们需要将data从data source中获取。有两种方式获取数据,第一种是使用Screen Aggregates,第二种是通过Data Actions。两种类型的data sources都可以通过在指定的screen图标点击右键来添加到screen里面。

这两种都是可以获取数据的方式,对于Data Actions,可以用于更多更高级的场景,你可以定义自定义的server-side logic在Data Actions里面,你在logic中可以调用扩展的REST web services,或者执行高级SQL查询。Data Action的outputs可以在screen widgets中被使用。
Aggregates和Data Actions存活在Screen上面,那么触发data获取的时机是什么呢?默认情况下,Aggregates和Data Actions的获取数据都是自动触发的,当屏幕正在初始化时它们开始执行。Aggregates和Data Actions都是以异步的方式运行的,当它们的数量不止一个时,它们以并行的方式获取data。当data可以被使用时,当Aggregate或Data Action完成了execution,Screen就会自动地展示返回的data。对于多Aggregates和Data Actions,每当data更新,它们两都会完成execution并返回data给screen。
Two Widgets to Render Data
为了让数据可用,我们需要widgets去展示它们。在这个例子中,我们有一些员工信息,以不同的方式被展示到了screen上。
在service studio中,我们可用看到screen的预览,比如为展示employees我们有List,

然后展示员工的名字,我们有Expression。

How to Combine Data and Widgets Together?
既然我们有data和widgets,那么我们需要绑定它们到一起。那么当screen呈现的时候,widget知道展示什么数据。

为了展示数据,比如使用List或者Table,widgets有一个叫Source的property,它的期望值是一个List(它希望调用者传一个List给它)。

在这里,我们可以直接使用Aggregate或Data Action返回的结果。在这个例子中,我们使用员工Aggregate的返回的List。为了展示表中具体的字段,比如员工的名字。我们可以使用Expressions。
Expression中的Value property期望值是我们想要具体展示的字段名。在这个例子中,我们选择了展示员工的名字。对于相同或不同widgets的结合,我们可以以多种方式展示信息。
How to Use the List and ListItem?


How to Add Swipe Actions to Lists
在这节中,我们将会看如何在Lists中使用Swipe Actions。


How to Use the Table Widget
Modeling Data Relationships
在这节,我们将会给你一个outsystems中数据关系(data relationship)的快速预览。我们会以讲述为什么data relationship如此重要作为本节的起点。然后,我们将学习entity和引用id(reference identifier),它们将帮助我们定义relationships。最后,我们将了解outsystems中entity relationships的种类。

当我们在app中建模数据,让某个数据孤立是非常罕见的。大多数的app的业务概念都是相互有联系的,所以对数据间创建关系是非常常见的。这些关系实际上与数据本身同样重要。在上图中,我们可以看到一个数据模型(data model)的例子,

在这之中有几个entities。其中有Customer Entity,Customer Entity中有几个字段比如Name, Email, Address作为它的attributes。Order Entity的Description, Due Date, date of shipping作为Order Entity的attributes。除了这些普通独立的attributes定义每个entity,还有引用ID属性(Reference Identifier)同样定义entity。我们知道customer可以下单多个orders,而一个order对应只有一个customer。在上面这个框起来的data model中,我们有两个entities:Customer和Order,以及它们的relationship。对于customer的信息,定义在Order entity的其中一个attribute——RequestedBy中。
Entity Identifier
创建relationships是基于Entity identifier attributes的。要创建relationship,一个entity必须得有一个Identifier。当Entity被创建的时候,当Entity被创建的时候,Entity Identifier是自动被创建的,Entity Identifier被红色条纹图标和它的名字ID所标识。

· 默认情况下,Id被创建为long integer然后被设置为自动编号,那样每当有新的record加到database将会有一个唯一的数字来标识它。
· 默认情况下,ID attribute是强制性的。所有的这些properties可以被改变,madatory property可以为Yes或No,data type可以为text, integer,甚至其他entity identifier。改变Entity Identifier类型或许会要求ID值被显式设置,因为也许不再支持自动编号。
· Identifier Attribute可以被删除如果需要的话,但是该entity将不可能被用于创建relationship。
· The Entity Identifier代表database table的主键(primary key)。Outsystems支持简单的primary key但不支持复合主键(composite keys)。这意味着一张table中只有一个attribute可以作为identifier。
Entities可以被引用通过它的identifier。为了引用Entity,我们需要为Entity创建一个Entity identifier类型的attribute。

在上面这张图中,Order表中有一个Priority Identifier Attribute,而Priority是我们data model中的Entity。这种Reference Identifier类型的attributes可以被mandatory,或者不被mandatory。
· Static Entities只能应用其他Static Entities。
· Entity可以拥有多个reference attributes。这些reference attributes代表着database tables中的外键(foreign key)。
· 所有的reference attributes都有NullIdentifier()作为它的默认data type。
Types of Relationships
OutSystems支持三种类型的data relationships:one to many, one to one, many to many。

One to many

创建one to many relationship的方法:添加reference attribute到entity中。
在上图这个例子中,因为业务是:一个customer可以下单多个orders,而每个order只属于一个customer。所以建模数据关系的流程是:让Order Entity里面的一个名为RequestBy的attribute的Data Type为Customer Identifier。

One to one

创建one to one relationship的方法:设置其中一张表的identifier的数据类型为另一张表的identifier。
在上图这个示例中,因为业务是:一个Order只能有一个收据,所以建模数据关系的流程是:将OrderReceipt的Identifier的Data Type设置为Order Identifier。
以这种方式,Order的Id将会还会标识它的receipt。
Many to many

创建many to many relationship的方法:创建第三个Entity,该Entity有自己独立的Identifier,以及两个reference Identifier。
在这个例子中,因为业务是:一个Order可以有多个products,一个product又可以提供给几个多个orders。所以建模数据关系的流程是:将OrderProduct的两个attribute的Data Type分别设置为Order Identifier和Product Identifier,并拥有自己独立的Identifier。