基本概念
NativeScript 中的数据绑定
数据绑定是指ViewModel(模型)和用户界面(UI)之间的连接(绑定)和数据流。
它通过三个步骤激活
- 创建一个扩展
Observable
类的 ViewModel(我们称之为 DataModel)类 - 通过设置
page.bindingContext
=new DataModel()
使 DataModel 可用于 UI - 使用 mustach 语法
{{ }}
,将 UI 组件的属性绑定到DataModel
实例的成员。
当你查看一个新项目时,你会看到这些步骤的示例。
有各种方法可以管理数据流,通常被称为数据绑定
- 单向(到 UI)数据绑定 - 这是最常见的数据绑定形式。一个例子是将模型中的字符串属性绑定到标签组件。
<Label text="{{ name }}" />
export class HelloWorldModel extends Observable {
name = 'John Doe'
}
<Button text="Submit" tap="{{ onTap }}"/>
<Button text="Button 1" tap="{{ onTap2('Button 1') }}" />
<Button text="Button 2" tap="{{ onTap2('Button 2') }}" />
<Button text="Button 3" tap="{{ onTap3('Button 3', counter) }}" />
export class HelloWorldModel extends Observable {
public counter: number = 42
onTap(args: EventData) {
//
}
onTap2(source: string) {
return function fnOnTap2(args: EventData) {
console.log('onTap2.source =', source)
}
}
onTap3(source: string, num: number) {
return (args: EventData) => {
console.log('onTap3.source =', source)
console.log('onTap3.num =', num)
this.counter--
}
}
}
- 双向数据绑定 - 数据从模型流向 UI,反之亦然。一个典型的例子是一个
TextField
,它从模型读取其值,并根据用户输入更改模型。
<TextField text="{{ name }}"/>
export class HelloWorldModel extends Observable {
name = 'John Doe'
}
访问父 bindingContext
父 UI 组件和子 UI 组件可以具有不同的绑定上下文,子组件可能需要将其属性绑定到其父级的 bindingContext 中的源属性。
通常,绑定上下文是可继承的,但当组件是根据某些数据源动态创建时除外。例如,ListView 根据 itemТemplate
创建其子项,该模板描述了 ListView 组件的外观。当此组件添加到可视化树时,对于绑定上下文,它将从 ListView
项目数组(具有相应的索引)中获取元素。
此过程为子项及其内部 UI 组件创建新的绑定上下文链。因此,内部 UI 组件无法访问 ListView
的绑定上下文。为了解决这个问题,NativeScript 绑定基础结构有两个特殊关键字
$parent
:表示直接父级可视组件的绑定上下文$parents
:可以用作数组(带数字或字符串索引)。这使您能够选择 UI 嵌套的 N 级或获取具有给定类型的父级 UI 组件。
注意
如果 ListView
的 items
属性的值是像前面的示例中那样是普通元素(数字、字符串、日期)的数组,则使用 $value
变量来访问数组的当前项。
如果它是一个对象数组,则使用当前对象属性名称作为变量名称。
使用绑定表达式
NativeScript 支持以下多态绑定表达式
属性访问
要访问存储在 bindingContext 的对象属性中的值,请使用属性访问表达式
user = {
names: {
first: 'Sara',
},
}
<Label text="{{ user.name.first }}" textWrap="true" />
数组访问
<Label text="{{ fruits[0] }}" textWrap="true" />
逻辑运算符
可以使用非(!
)运算符来反转绑定上下文属性的逻辑状态。
<Label text="{{ !isUserLoggedIn }}" textWrap="true" />
支持的运算符:&&
、||
和 !
。
一元运算符
<Label text="{{ +age }}" textWrap="true" />
age = '33'
将属性值转换为 number
。要将属性转换为 number
并对其取反,请使用 -
运算符。
比较运算符
<Label text="{{ prop1 > prop2 }}" textWrap="true" />
支持的运算符:>
、<
、<=
、>=
、==
、!=
、===
、!==
。
三元运算符
<Label text="{{ prop1 ? prop2 : prop3 }}" textWrap="true" />
分组括号
<Label text="{{ prop1*(prop2 + prop3) }}" textWrap="true" />
函数调用
<Label text="{{ someMethod(p1,p2,...,pN) }}" textWrap="true" />
比较运算符
<Label text="{{ property1 > property2 }}" textWrap="true" />
其他支持的运算符是:<
、<=
、>=
、==
、!=
、===
、!==
。
注意
特殊字符需要转义如下
- 双引号:
"
→"
- 单引号:
'
→'
- 小于号:
<
→<
- 大于号:
>
→>
- 和号:
&
→&
使用数据转换器
模型中的数据通常以适合搜索、替换等任务的最佳性能的方式存储。不幸的是,计算机存储数据的方式与人类可读格式有很大不同。一个合适的例子是 Date 对象。在 JavaScript 中,Date 实际上是一个非常大的数字,代表从 01.01.1970
开始的毫秒数,这对任何人都没有多大意义。这就是数据转换器发挥作用的地方。数据转换器本质上是将模型中的数据格式化为人类可读格式以在 UI 中显示的函数。
<StackLayout class="p-20 bg">
<Label text="{{ date | dateConverter('dd-mm-yyyy') }}" textWrap="true" />
<Label text="{{ name | toUpperCaseConverter }}" textWrap="true" />
<Label text="{{ title | toTitle }}" textWrap="true" />
</StackLayout>
export class HelloWorldModel extends Observable {
name = 'nandee'
title = 'hello world!'
date = Date.now() // number
dateConverter = this.formatDate()
toUpperCaseConverter = this.toUpperCase()
toTitle = this.convertToTitle()
formatDate() {
return {
toView(value: number, format: string) {
const date = new Date(value)
const day = date.getDate().toString().padStart(2, '0')
const month = (date.getMonth() + 1).toString().padStart(2, '0') // months are zero based in JS.
const year = date.getFullYear().toString()
return format
.replace('dd', day)
.replace('mm', month)
.replace('yyyy', year)
},
}
}
toUpperCase() {
return {
toView(value: string) {
return value.toUpperCase()
},
}
}
convertToTitle() {
return {
toView(str: string) {
return str.replace(/(^|\s)\S/g, function (t) {
return t.toUpperCase()
})
},
}
}
}
停止绑定
通常不需要显式停止绑定,因为 Observable 对象使用弱引用,这可以防止任何内存泄漏。但是,如果你需要解除绑定视图的数据,请使用目标属性名称作为参数调用 unbind() 方法。
targetTextField.unbind('text')