部件
部件(Widget)是 zin 用于构建页面的基本单元。每个不同类型的部件实际上定义了生成一段逻辑相关的 HTML 代码的规则。每个部件都有一个方法用于进行实例化,部件方法可以在调用时传入支持的参数来影响代码生成的规则,也可以传入内部部件实例实现嵌套结构。使用部件的本质是按需复用特定的 HTML,最终生成一个完整的页面。
部件方法
部件方法是使用 zin 部件的方式。
定义
每个部件都对应了一个 zin 命名空间内的方法,并且所有部件的方法定义都是相同:
widget(mixed ...$args): object
widget
为部件方法名称,$args
为可变参数,即任何部件方法都可以传入任意多个参数,也可以不传入参数。部件方法会返回一个部件实例对象。
提示
简单方法定义让 zin 使用起来得心应手,没有繁重的心智负担,一切都按照预期工作。
参数
部件方法内可以传入 0 个或多个参数。
参数类型
部件方法调用参数可以为如下类型:
纯文本
纯文本通常作为部件内的文本进行展示,不过一些定制的部件会进行特殊处理。例如:
div('你好');
<div>你好</div>
部件实例
部件内可以传入其他部件实例,这样实现了嵌套结构,详情参考 嵌套章节。例如:
div
(
span('hello')
);
<div>
<span>hello</span>
</div>
指令
指令通常用于设置部件属性或数据,详情参考文档 属性。例如:
div
(
set('class', 'text-primary')
);
<div class="text-primary"></div>
一些内容生成指令方法可以向部件内部插入特定的 HTML 源码。例如:
div
(
html('Hello, <code>zin</code>!'),
css('code {color: red;}'),
js('console.log("hello world!");')
);
<div>
hello, <code>zin</code>!
<style>code {color: red;}</style>
<script>console.log("hello world!");</script>
</div>
要了解所有可用的指令请参考文档 指令。
通过数组一次性指定多个参数
即便是单个参数也可以指定为一个数组,然后在数组内一次指定多个内容,每个内容都可以为以上类型中的任意一种。例如:
$divContents = array
(
'Hello,',
code('zin'),
set('class', 'text-primary'),
);
div
(
$divContents,
css('code {color: red;}'),
);
div
(
'Hello,',
code('zin'),
set('class', 'text-primary'),
css('code {color: red;}'),
);
<div class="text-primary">
hello, <code>zin</code>!
<style>code {color: red;}</style>
</div>
上述方式非常适合提前将一些内容通过数组保存起来,后续再进行按需修改,最后再添加到页面结构中。
其他 PHP 类型值
除了字符串、部件实例、指令定义结果、数组之外,其他的 PHP 类型值处理规则如下:
null
和bool
:直接被忽略;object
:符合以下要求的对象会被处理,其他对象会报错,对象包括stdClass
和自定义类:- 包含
render()
方法,渲染时会调用该方法并期望获得字符串结果作为 HTML 处理; - 包含
html
属性,渲染时使用此属性作为 HTML 处理; - 包含
text
属性,渲染时使用此属性作为纯文本处理; - 实现了
__toString()
方法,执行 PHP 内置方法strval
获得字符串进行处理;
- 包含
其他类型:使用 PHP 内置方法
strval
获得字符串,并当作字符串类型进行处理。
下面举例说明:
$obj = new stdClass();
$obj->html = '<code>zin</code>';
div
(
null,
true,
false,
2023,
$obj
);
<div>
2023
<code>zin</code>
</div>
顺序
部件方法参数的顺序符合大脑预期,即“所见即所得”,不同的内容按顺序添加到部件内部,同名的属性设置,后面的设置会覆盖前面的设置。下面列举一个例子:
div
(
set('title', 'Week days'),
'Monday',
set('title', 'Work days'),
'Tuesday',
'Wednesday',
'Thursday',
'Friday'
);
<div title="Work days">
Monday
Tuesday
Wednesday
Thursday
Friday
</div>
需要注意的是当使用 set
指令分多次设置类名时,会启用类名计算规则,只有明确为去掉类名才会移除类名,下面为一个例子:
div
(
set('class', 'btn primary'),
set('class', 'rounded'),
set('class', array('primary' => false))
);
<div class="rounded btn">
</div>
详细的类名计算规则参考文档 类与样式。
嵌套
部件方法调用时可以将其他部分方法实例作为参数传入来实现嵌套结构。例如:
ul
(
li('水果'),
li('蔬菜')
);
<ul>
<li>水果</li>
<li>蔬菜</li>
</ul>
嵌套没有层级限制,你可以按需嵌套下去。例如:
ul
(
li
(
'水果',
ul
(
li('苹果'),
li('香蕉')
)
),
li('蔬菜')
);
<ul>
<li>
水果
<ul>
<li>苹果</li>
<li>香蕉</li>
</ul>
</li>
<li>蔬菜</li>
</ul>
一些定制组件会自动处理嵌套的部件实例类型,并在渲染 HTML 时将子部件实例生成的 HTML 放置在合适的位置。例如:
dropdown
(
btn('选择食物'),
item('水果'),
item('蔬菜')
);
<button type="button" class="btn" data-toggle="dropdown" data-target="#dropdown-odjk83x3">选择食物</button>
<menu class="menu dropdown-menu" id="dropdown-odjk83x3">
<li class="menu-item"><span class="text">水果</span></li>
<li class="menu-item"><span class="text">蔬菜</span></li>
</menu>
部件类型
在 zin 中内置了丰富的部件,包含如下几大类,下面分别进行介绍。
HTML5 元素
用于声明 HTML5 元素,如 <div>
、<span>
等。H5 元素部件方法名与 HTML5 元素名相同,下面展示一个简单的例子:
div('Hello World');
<div>Hello World</div>
要了解所有支持的 HTML 5 部件方法请参考 HTML5 元素。
内置部件
zin 提供了一些内置部件方便构建一些特殊 HTML 结构,例如页面级的组件 page
用于生成 HTML5 页面结构:
page('Hello');
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="renderer" content="webkit">
</head>
<body>
Hello
</body>
</html>
要了解所有支持的内置部件方法请参考文档 内置部件。
ZUI 组件
zin 内置了大量 ZUI3 中的组件,包括按钮、图标、导航、数据表格等。下面展示一个简单的例子:
btn('了解更多');
<button type="button" class="btn">了解更多</button>
需要使用原生 <button>
?
如果你需要的是原始的 HTML5 按钮,可以使用 button
部件:
button('了解更多');
// 生成的 HTML:
// <button type="button">了解更多</button>
要了解所有支持的 ZUI3 部件方法请参考 ZUI3 部件。
禅道定制组件
zin 为禅道进行设计,内置了构建禅道 UI 所需的组件,例如页面头部、侧边栏、个人菜单等,下面展示一个页面顶部导航的例子:
navbar();
<nav id="navbar">
<menu class="nav">
<!-- 以下导航的内容根据禅道不同的页面决定 -->
<li class="nav-item"><a class="active" href="/my.html"><span class="text">仪表盘</span></a></li>
<li class="nav-item"><a href="/my-calendar.html"><span class="text">日程</span></a></li>
<li class="nav-item"><a href="/my-work-task.html"><span class="text">待处理</span></a></li>
<li class="nav-item"><a href="/my-audit-all--time_desc.html"><span class="text">审批</span></a></li>
<li class="nav-item"><a href="/my-project.html"><span class="text">项目</span></a></li>
<li class="nav-item"><a href="/my-execution-undone.html"><span class="text">执行</span></a></li>
<li class="nav-item"><a href="/my-contribute-task.html"><span class="text">贡献</span></a></li>
<li class="nav-item"><a href="/my-dynamic.html"><span class="text">动态</span></a></li>
<li class="nav-item"><a href="/my-managecontacts.html"><span class="text">联系人</span></a></li>
<li class="nav-item"><a><span class="text">text</span></a></li>
</menu>
</div>
要了解所有支持的禅道定制部件方法请参考 禅道组件。
部件实例
部件方法在调用之后会返回一个部件实例对象,该对象继承自部件基类 zin\wg
。在部件实例上提供了大量属性和方法,不过作为开发者通常不会需要手动调用,下面介绍一些重要的属性和方法。
实例渲染
手动渲染
部件实例提供了 render()
和 display()
方法来让部件进行渲染操作以及并向页面输出 HTML。例如:
$html = div('hello')->render(); // $html 的值为 "<div>hello</div>"
echo $html;
上面的例子可以用 display()
方法直接实现:
div('hello')->display();
适用情况
通常一个完整的页面会使用内置的 页面部件 page
,页面部件在声明之后会自动调用 display()
方法将内容输出到页面,所以用到页面部件的情况下都无需手动进行渲染。大部分情况这是合理且省事的,但一些特殊情况可能要用到手动渲染:
- 此页面只需要输出部分 HTML,没有用到页面部件,例如一些 Ajax 内容,这时就需要在根部件实例上进行手动渲染;
- 需要预先渲染出 HTML 片段供其他方法调用,此时可以作为代码生成器使用,例如生成一些代码例子等。
渲染的 HTML 约束
部件实例调用渲染方法之后会生成一段 HTML。需要注意的是每个部件最终渲染生成的 HTML 代码必须是完整的,他们随意拼接在一起是可以组成完整有效的 HTML 文档。下面的例子都是有效的 HTML 代码:
代码 | 说明 |
---|---|
<div>你好</div> | HTML 元素代码 |
<br/> | <br/> 是自闭合的 HTML 元素 |
你好 | 纯文本 |
<script>console.log('hello')</script> | <script> 元素代码 |
<style>body{background: red}</style> | <style> 元素代码 |
<div>你好</div><style>div{color: red}</style> | 混合多种类型的 HTML 代码 |
下面的例子不是有效的 HTML 代码:
代码 | 说明 |
---|---|
<div>你好 | <div> 标签缺少闭合形式的 </div> |
你好 :> | 包含 HTML 标签特殊字符 > |
console.log('hello') | 会被当作纯文本,而不是有效的 JS 代码 |
body{background: red} | 会被当作纯文本,而不是有效的 CSS 代码 |