一百天挑战学会HarmanyOS——组件的双向绑定与事件监听

386 阅读4分钟

大家好我是牛牛,一名软件开发从业者,无意中接触到了鸿蒙移动端开发,对鸿蒙操作系统产生了极大的兴趣,作者将从无到有开发出一款鸿蒙原生APP。每天写一篇关于鸿蒙开发的技术文章。欢迎大家踊跃订阅➕关注,文章中有什么 不妥之处可以在评论区中指出。

注意:最好是有开发经验的伙伴来阅读系列文章。零基础的同学可以先去了解一下TypeScript从最基本的开发语言进行学起

前言

上一章节《一百天挑战学会HarmanyOS——HarmanyOS的基本组件与布局》介绍了ArkUI的基本组件与布局,初步的带大家认识了ArkUI中的基本组件,在真实的开发项目当中这些组件使用频率很高,其中还包括了一些高级组件。以我个人而言,只需了解最基本的组件使用,剩下的千篇一律,不会的直接翻阅官方文档和即可。ArkUI组件文档

本章介绍

本章节将为大家介绍ArkUI中组件双向绑定的使用,组件事件监听的使用,组件状态的使用,以及完成一个简单的系统登录的小案例。

双向绑定

什么是双向绑定?

提到双向绑定先介绍下一MVVM模型,MVVM模型在许多前端框架设计中广泛使用,即 数据-模型 image.png

MVVM

  • M : Model数据模型
  • V : View视图
  • VM: ViewModel属于打得通数据与视图的桥梁

双向绑定的含义就是,数据驱动视图的变化,数据改变,视图也随之变化,视图也可以驱动数据的变化,视图中的数据变化,即视图所绑定的数据也随之变化。

在不同框架中实现的方式各有不同,在 Vue中使用v-model进行数据的双向绑定,微信小程序使用wx:进行数据的双向绑定。那在HarmanyOS中使用什么呢?

HarmanyOS双向绑定语法

语法: $$变量

例如:

$$this.username

注意:

  • 当前$$支持基础类型变量,以及@State、@Link和@Prop装饰的变量。
  • 组件中有的双向组件是属性,有的双向绑定是参数
  • 不支持嵌套数据的双向绑定

鸿蒙NEXT版本所有支持双向绑定的组件

image.png image.png

下面使用几个常用组件的小案例介绍一下具体使用方法

TextInput组件的双向绑定

示例:

TextInput.gif

代码:

@Entry
@Component
struct MvvmPage {

  @State
  textValue:string = "";

  build() {
    Column({space:20}) {
      Text('TextInput的双向绑定')
      // 这个Text中的值会因为Input输入的值而改变
      Text(this.textValue)
      // TextInput中使用双向绑定语法
      TextInput({
        text: $$this.textValue
      })
    }
    .padding({
      left:20,
      right:20
    })
    .justifyContent(FlexAlign.Center)
    .height('100%')
    .width('100%')
  }
}

CheckBox组件的双向绑定

示例:

CheckBox.gif

代码:

@Entry
@Component
struct CheckBoxMvvmPage {
  @State
  isStatus: boolean = false;

  build() {
    Column({ space: 20 }) {
      Text('CheckBox的双向绑定')
      Row() {
        // CheckBox中使用双向绑定
        Checkbox().select($$this.isStatus)
        Text(this.isStatus ? '已选中' : '未选中')
      }

      // 使用按钮改变CheckBox的状态
      Button('修改状态').width('100%').onClick(() => {
        this.isStatus = !this.isStatus
      })
    }
    .padding({
      left: 20,
      right: 20
    })
    .justifyContent(FlexAlign.Center)
    .height('100%')
    .width('100%')
  }
}

Search组件的双向绑定

示例:

Search.gif

代码:

@Entry
@Component
struct SearchMvvmPage {
  @State
  textValue:string = "";

  build() {
    Column({space:20}) {
      Text('Search组件的双向绑定')
      Search({
        value: $$this.textValue
      })
      Text(this.textValue)
    }
    .padding({
      left:20,
      right:20
    })
    .justifyContent(FlexAlign.Center)
    .height('100%')
    .width('100%')
  }
}

暂且演示这几个组件的双向绑定,剩下的组件可以在实际开发中得到实际的运用

事件监听

监听原生组件的事件和设置属性的方式是一样的都是链式调用,值得注意的是,我们注册事件必须使用箭头函数的写法,Next版本禁止使用匿名函数的形式来给组件注册事件

  • 匿名函数 function () {}

尝试给一个TextInput和一个按钮注册一个值改变事件和点击事件

示例:

Click.gif

代码:

@Entry
@Component
struct ClickPage {
  build() {
    Row() {
      Column({ space: 15 }) {
        Row() {
          TextInput({ placeholder: '请输入用户名' })
            .backgroundColor('#f4f5f6')
            .width('100%')
            // onChange 监听输入框变化的事件
            .onChange((value) => {
              // 一变化就弹窗
            AlertDialog.show({
              message: value
            })
          })
        }.padding({
          left: 20,
          right: 20
        })

        Row() {
          Button("登录")
            .width('100%')
            // 按钮的点击事件
            .onClick(() => {
              // 一点击就弹窗
              AlertDialog.show({
                message: '点击了按钮'
              })
            })

        }.padding({
          left: 20,
          right: 20
        })

      }
      .width('100%')
    }
    .height('100%')
  }
}

请注意: 在注册事件中的逻辑必须使用箭头函数 () => {}

  • 因为function中this指向为undefind
  • 箭头函数中的this指向当前struct实例,可以方便的调用方法和获取属性

组件状态

在上面双向绑定的案例代码中使用了@State修饰符修饰了基本不同的变量。@State装饰的变量称为状态变量,一旦变量拥有了状态属性,就和自定义组件的渲染绑定起来。当状态改变时,UI会发生对应的渲染改变。

如何使用 @State 定义一个状态变量?

示例代码:

@Entry
@Component
struct Event {
  @State loginName: string = ""
  @State password: string = ""
}

需要注意的是,@State 修饰的类型 Object、class、string、number、boolean、enum类型,以及这些类型的数组。嵌套类型的场景请参考观察变化。类型必须被指定。不支持any,不支持简单类型和复杂类型的联合类型,不允许使用undefined和null。

下面使用这章节的所有知识点制作一个小案例。

模拟登录案例

示例:

Login.gif

代码:

import { promptAction, router } from '@kit.ArkUI';

@Entry
@Component
struct LoginPage {
  @State
  loginInfo: LoginUserInfoModel = new LoginUserInfoModel({
    username: '',
    password: ''
  })

  build() {
    Column({ space: 20 }) {
      Text('HarmanyOS Login').fontSize(30).fontWeight(FontWeight.Lighter)
      TextInput({
        placeholder: $r('app.string.accountInput'),
        text: this.loginInfo.username
      }).maxLength(16).onChange((value) => {
        this.loginInfo.username = value
      })
      TextInput({
        placeholder: $r('app.string.passwordInput'),
        text: this.loginInfo.password
      }).type(InputType.Password).maxLength(16)
      Button($r('app.string.login')).width('100%').enabled(this.getBtnStatus()).onClick(() => {
        this.loginSubmit();
        router.pushUrl({
          url: "pages/login/HomePage"
        })
      })
    }
    .justifyContent(FlexAlign.Center)
    .padding({
      left: 20,
      right: 20
    })
    .height('100%')
    .width('100%')

  }

  /** 设置按钮状态 */
  getBtnStatus(): boolean {
    if (this.loginInfo.username || this.loginInfo.password) {
      return true
    } else {
      return false
    }
  }

  loginSubmit() {
    promptAction.showToast({ message: '登录成功' })
  }
}

interface LoginUserInfo {
  username: string,
  password: string,
}

export class LoginUserInfoModel implements LoginUserInfo {
  username: string = ''
  password: string = ''

  constructor(model: LoginUserInfo) {
    this.username = model.username
    this.password = model.password
  }
}

这一期就到这里啦!为了方便大家我这一期代码放到git仓库上,建议大家动起手实操起来。 项目代码git