8.7 发布 - WinterCG 合规性第 1 部分
了解更多

数据绑定是指ViewModel(模型)和用户界面(UI)之间的连接(绑定)和数据流。

它通过三个步骤激活

  1. 创建一个扩展 Observable 类的 ViewModel(我们称之为 DataModel)类
  2. 通过设置 page.bindingContext = new DataModel() 使 DataModel 可用于 UI
  3. 使用 mustach 语法 {{ }},将 UI 组件的属性绑定到 DataModel 实例的成员。

当你查看一个新项目时,你会看到这些步骤的示例。

有各种方法可以管理数据流,通常被称为数据绑定

  • 单向(到 UI)数据绑定 - 这是最常见的数据绑定形式。一个例子是将模型中的字符串属性绑定到标签组件。
xml
<Label text="{{ name }}" />
ts
export class HelloWorldModel extends Observable {
  name = 'John Doe'
}
  • 单向(到模型)数据绑定 - 绑定根据 UI 中的某些操作更新模型。这方面的最佳例子是 点击 事件。可以使用 函数柯里化 传递静态和数据绑定参数。这是一个 工作示例
xml
<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) }}" />
ts
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,它从模型读取其值,并根据用户输入更改模型。
xml
<TextField text="{{ name }}"/>
ts
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 组件。

注意

如果 ListViewitems 属性的值是像前面的示例中那样是普通元素(数字、字符串、日期)的数组,则使用 $value 变量来访问数组的当前项。

如果它是一个对象数组,则使用当前对象属性名称作为变量名称。

使用绑定表达式

NativeScript 支持以下多态绑定表达式

属性访问

要访问存储在 bindingContext 的对象属性中的值,请使用属性访问表达式

ts
user = {
  names: {
    first: 'Sara',
  },
}
xml
<Label text="{{  user.name.first  }}" textWrap="true" />

数组访问

xml
<Label text="{{  fruits[0]  }}" textWrap="true" />

逻辑运算符

可以使用非(!)运算符来反转绑定上下文属性的逻辑状态。

xml
<Label text="{{  !isUserLoggedIn  }}" textWrap="true" />

支持的运算符:&&||!

一元运算符

xml
<Label text="{{  +age  }}" textWrap="true" />
ts
age = '33'

将属性值转换为 number。要将属性转换为 number 并对其取反,请使用 - 运算符。

比较运算符

xml
<Label text="{{  prop1 > prop2  }}" textWrap="true" />

支持的运算符:><<=>===!====!==

三元运算符

xml
<Label text="{{  prop1 ? prop2 : prop3  }}" textWrap="true" />

分组括号

xml
<Label text="{{  prop1*(prop2 + prop3)  }}" textWrap="true" />

函数调用

xml
<Label text="{{  someMethod(p1,p2,...,pN)  }}" textWrap="true" />

比较运算符

xml
<Label text="{{  property1 > property2  }}" textWrap="true" />

其他支持的运算符是:<<=>===!====!==

注意

特殊字符需要转义如下

  • 双引号:""
  • 单引号:''
  • 小于号:<<
  • 大于号:>>
  • 和号:&&

使用数据转换器

模型中的数据通常以适合搜索、替换等任务的最佳性能的方式存储。不幸的是,计算机存储数据的方式与人类可读格式有很大不同。一个合适的例子是 Date 对象。在 JavaScript 中,Date 实际上是一个非常大的数字,代表从 01.01.1970 开始的毫秒数,这对任何人都没有多大意义。这就是数据转换器发挥作用的地方。数据转换器本质上是将模型中的数据格式化为人类可读格式以在 UI 中显示的函数。

xml
<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>
ts
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() 方法。

ts
targetTextField.unbind('text')
上一个
XmlParser