当前位置: 首页 > news >正文

【鸿蒙学习笔记】MVVM模式

官方文档:MVVM模式

[Q&A] 什么是MVVM

ArkUI采取MVVM = Model + View + ViewModel模式。

  1. Model层:存储数据和相关逻辑的模型。
  2. View层:在ArkUI中通常是@Component装饰组件渲染的UI。
  3. ViewModel层:在ArkUI中,ViewModel是存储在自定义组件的状态变量LocalStorageAppStorage中的数据
    在这里插入图片描述

MVVM应用示例

开发一个电话簿应用,实现功能如下:

  1. 显示联系人和设备(“Me”)电话号码 。
  2. 选中联系人时,进入可编辑态“Edit”,可以更新该联系人详细信息,包括电话号码,住址。
  3. 在更新联系人信息时,只有在单击保存“Save Changes”之后,才会保存更改。
  4. 可以点击删除联系人“Delete Contact”,可以在联系人列表删除该联系人。

Model

Address,Person,AddressBook,MyArray

ViewModel

PersonView,phonesNumber,PersonEditView,AddressBookView

Vidw

PracExample

// ViewModel classes
let nextId = 0;@Observed
export class MyArray<T> extends Array<T> {constructor(args: T[]) {console.log(`MyArray: ${JSON.stringify(args)} `)if (args instanceof Array) {super(...args);} else {super(args)}}
}@Observed
export class Address {street: string;zip: number;city: string;constructor(street: string,zip: number,city: string) {this.street = street;this.zip = zip;this.city = city;}
}@Observed
export class Person {id_: string;name: string;address: Address;phones: MyArray<string>;constructor(name: string,street: string,zip: number,city: string,phones: string[]) {this.id_ = `${nextId}`;nextId++;this.name = name;this.address = new Address(street, zip, city);this.phones = new MyArray<string>(phones);}
}export class AddressBook {me: Person;friends: MyArray<Person>;constructor(me: Person, contacts: Person[]) {this.me = me;this.friends = new MyArray<Person>(contacts);}
}// 渲染出Person对象的名称和Observed数组<string>中的第一个号码
// 为了更新电话号码,这里需要@ObjectLink person和@ObjectLink phones,
// 不能使用this.person.phones,内部数组的更改不会被观察到。
// 在AddressBookView、PersonEditView中的onClick更新selectedPerson
@Component
struct PersonView {@ObjectLink person: Person;@ObjectLink phones: MyArray<string>;@Link selectedPerson: Person;build() {Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween }) {Text(this.person.name)if (this.phones.length) {Text(this.phones[0])}}.height(55).backgroundColor(this.selectedPerson.name == this.person.name ? Color.Pink : "#ffffff").onClick(() => {this.selectedPerson = this.person;})}
}@Component
struct phonesNumber {@ObjectLink phoneNumber: MyArray<string>build() {Column() {ForEach(this.phoneNumber,(phone: ResourceStr, index?: number) => {TextInput({ text: phone }).width(150).onChange((value) => {console.log(`${index}. ${value} value has changed`)this.phoneNumber[index!] = value;})},(phone: ResourceStr, index: number) => `${this.phoneNumber[index] + index}`)}}
}// 渲染Person的详细信息
// @Prop装饰的变量从父组件AddressBookView深拷贝数据,将变化保留在本地, TextInput的变化只会在本地副本上进行修改。
// 点击 "Save Changes" 会将所有数据的复制通过@Prop到@Link, 同步到其他组件
@Component
struct PersonEditView {@Consume addrBook: AddressBook;/* 指向父组件selectedPerson的引用 */@Link selectedPerson: Person;/*在本地副本上编辑,直到点击保存*/@Prop name: string = "";@Prop address: Address = new Address("", 0, "");@Prop phones: MyArray<string> = [];selectedPersonIndex(): number {return this.addrBook.friends.findIndex((person: Person) => person.id_ == this.selectedPerson.id_);}build() {Column() {TextInput({ text: this.name }).onChange((value) => {this.name = value;})TextInput({ text: this.address.street }).onChange((value) => {this.address.street = value;})TextInput({ text: this.address.city }).onChange((value) => {this.address.city = value;})TextInput({ text: this.address.zip.toString() }).onChange((value) => {const result = Number.parseInt(value);this.address.zip = Number.isNaN(result) ? 0 : result;})if (this.phones.length > 0) {phonesNumber({ phoneNumber: this.phones })}Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween }) {Text("Save Changes").onClick(() => {// 将本地副本更新的值赋值给指向父组件selectedPerson的引用// 避免创建新对象,在现有属性上进行修改this.selectedPerson.name = this.name;this.selectedPerson.address = new Address(this.address.street, this.address.zip, this.address.city)this.phones.forEach((phone: string, index: number) => {this.selectedPerson.phones[index] = phone});})if (this.selectedPersonIndex() != -1) {Text("Delete Contact").onClick(() => {let index = this.selectedPersonIndex();console.log(`delete contact at index ${index}`);// 删除当前联系人this.addrBook.friends.splice(index, 1);// 删除当前selectedPerson,选中态前移一位index = (index < this.addrBook.friends.length) ? index : index - 1;// 如果contract被删除完,则设置me为选中态this.selectedPerson = (index >= 0) ? this.addrBook.friends[index] : this.addrBook.me;})}}}}
}@Component
struct AddressBookView {@ObjectLink me: Person;@ObjectLink contacts: MyArray<Person>;@State selectedPerson: Person = new Person("", "", 0, "", []);aboutToAppear() {this.selectedPerson = this.me;}build() {Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Start }) {Text("Me:")PersonView({person: this.me,phones: this.me.phones,selectedPerson: this.selectedPerson})Divider().height(8)ForEach(this.contacts, (contact: Person) => {PersonView({person: contact,phones: contact.phones as MyArray<string>,selectedPerson: this.selectedPerson})}, (contact: Person): string => {return contact.id_;})Divider().height(8)Text("Edit:")PersonEditView({selectedPerson: this.selectedPerson,name: this.selectedPerson.name,address: this.selectedPerson.address,phones: this.selectedPerson.phones})}.borderStyle(BorderStyle.Solid).borderWidth(5).borderColor(0xAFEEEE).borderRadius(5)}
}@Entry
@Component
struct PracExample {@Provide addrBook: AddressBook = new AddressBook(new Person("卧龙", "南路 9", 180, "大连", ["18*********", "18*********", "18*********"]),[new Person("小华", "东路 9", 180, "大连", ["11*********", "12*********"]),new Person("小刚", "西边路 9", 180, "大连", ["13*********", "14*********"]),new Person("小红", "南方街 9", 180, "大连", ["15*********", "168*********"]),]);build() {Column() {AddressBookView({me: this.addrBook.me,contacts: this.addrBook.friends,selectedPerson: this.addrBook.me})}}
}

在这里插入图片描述

相关文章:

【鸿蒙学习笔记】MVVM模式

官方文档&#xff1a;MVVM模式 [Q&A] 什么是MVVM ArkUI采取MVVM Model View ViewModel模式。 Model层&#xff1a;存储数据和相关逻辑的模型。View层&#xff1a;在ArkUI中通常是Component装饰组件渲染的UI。ViewModel层&#xff1a;在ArkUI中&#xff0c;ViewModel是…...

端、边、云三级算力网络

目录 端、边、云三级算力网络 NPU Arm架构 OpenStack kubernetes k3s轻量级Kubernetes kubernetes和docker区别 DCI(Data Center Interconnect) SD/WAN TF 端、边、云三级算力网络 算力网络从传统云网融合的角度出发,结合 边缘计算、网络云化以及智能控制的优势,通…...

java —— JSP 技术

一、JSP &#xff08;一&#xff09;前言 1、.jsp 与 .html 一样属于前端内容&#xff0c;创建在 WebContent 之下&#xff1b; 2、嵌套的 java 语句放置在<% %>里面&#xff1b; 3、嵌套 java 语句的三种语法&#xff1a; ① 脚本&#xff1a;<% java 代码 %>…...

【Python学习笔记】菜鸟教程Scrapy案例 + B站amazon案例视频

背景前摇&#xff08;省流可以跳过这部分&#xff09; 实习的时候厚脸皮请教了一位办公室负责做爬虫这块的老师&#xff0c;给我推荐了Scrapy框架。 我之前学过一些爬虫基础&#xff0c;但是用的是比较常见的BeautifulSoup和Request&#xff0c;于是得到Scrapy这个关键词后&am…...

Pycharm的终端(Terminal)中切换到当前项目所在的虚拟环境

1.在Pycharm最下端点击终端/Terminal, 2.点击终端窗口最上端最右边的∨&#xff0c; 3.点击Command Prompt&#xff0c;切换环境&#xff0c; 可以看到现在环境已经由默认的PS(Window PowerShell)切换为项目所使用的虚拟环境。 4.更近一步&#xff0c;如果想让Pycharm默认显示…...

Nginx 高效加速策略:动静分离与缓存详解

在现代Web开发中&#xff0c;网站性能是衡量用户体验的关键指标之一。Nginx&#xff0c;以其出色的性能和灵活性&#xff0c;成为众多网站架构中不可或缺的一部分。本文将深度解析如何利用Nginx实现动静分离与缓存&#xff0c;从而大幅提升网站加载速度和响应效率。 理解动静分…...

Unity3D 游戏摇杆的制作与实现详解

在Unity3D游戏开发中&#xff0c;摇杆是一种非常常见的输入方式&#xff0c;特别适用于移动设备的游戏控制。本文将详细介绍如何在Unity3D中制作和实现一个虚拟摇杆&#xff0c;包括技术详解和代码实现。 对惹&#xff0c;这里有一个游戏开发交流小组&#xff0c;大家可以点击…...

从nginx返回404来看http1.0和http1.1的区别

序言 什么样的人可以称之为有智慧的人呢&#xff1f;如果下一个定义&#xff0c;你会如何来定义&#xff1f; 所谓智慧&#xff0c;就是能区分自己能改变的部分&#xff0c;自己无法改变的部分&#xff0c;努力去做自己能改变的&#xff0c;而不要天天想着那些无法改变的东西&a…...

MySQL 代理层:ProxySQL

文章目录 说明安装部署1.1 yum 安装1.2 启停管理1.3 查询版本1.4 Admin 管理接口 入门体验功能介绍3.1 多层次配置系统 读写分离将实例接入到代理服务定义主机组之间的复制关系配置路由规则事务读的配置延迟阈值和请求转发 ProxySQL 核心表mysql_usersmysql_serversmysql_repli…...

异步主从复制

主从复制的概念 主从复制是一种在数据库系统中常用的数据备份和读取扩展技术&#xff0c;通过将一个数据库服务器&#xff08;主服务器&#xff09;上的数据变更自动同步到一个或多个数据库服务器&#xff08;从服务器&#xff09;上&#xff0c;以此来实现数据的冗余备份、读…...

论文解析——Full Stack Optimization of Transformer Inference: a Survey

作者及发刊详情 摘要 正文 主要工作贡献 这篇文章的贡献主要有两部分&#xff1a; 分析Transformer的特征&#xff0c;调查高效transformer推理的方法通过应用方法学展现一个DNN加速器生成器Gemmini的case研究 1&#xff09;分析和解析Transformer架构的运行时特性和瓶颈…...

selenium处理cookie问题实战

1. cookie获取不完整 需要进入的资损平台(web)首页&#xff0c;才会出现有效的ctoken等信息 1.1. 原因说明 未进入指定页面而获取的 cookie 与进入页面后获取的 cookie 可能会有一些差异&#xff0c;这取决于网站的具体实现和 cookie 的设置方式。 通常情况下&#xff0c;一些…...

(十五)GLM库对矩阵操作

GLM简单使用 glm是一个开源的对矩阵运算的库&#xff0c;下载地址&#xff1a; https://github.com/g-truc/glm/releases 直接包含其头文件即可使用&#xff1a; #include <glad/glad.h>//glad必须在glfw头文件之前包含 #include <GLFW/glfw3.h> #include <io…...

android中activity与fragment之间的各种跳转

我们以音乐播放、视频播放、用户注册与登录为例【Musicfragment&#xff08;音乐列表页&#xff09;、Videofragment&#xff08;视频列表页&#xff09;、MusicAvtivity&#xff08;音乐详情页&#xff09;、VideoFragment&#xff08;视频详情页&#xff09;、LoginActivity&…...

动态规划算法-以中学排课管理系统为例

1.动态规划算法介绍 1.算法思路 动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中&#xff0c;可能会有许多可行解。每一个解都对应于一个值&#xff0c;我们希望找到具有最优值的解。动态规划算法与分治法类似&#xff0c;其基本思想也是将待求解问题分解成若…...

本安防爆手机:危险环境下的安全通信解决方案

在石油化工、煤矿、天然气等危险环境中&#xff0c;通信安全是保障工作人员生命安全和生产顺利进行的关键。防爆智能手机作为专为这些环境设计的通信工具&#xff0c;提供了全方位的安全通信解决方案。 防爆设计与材料&#xff1a; 防爆智能手机采用特殊的防爆结构和材料&…...

算法学习笔记(8)-动态规划基础篇

目录 基础内容&#xff1a; 动态规划&#xff1a; 动态规划理解的问题引入&#xff1a; 解析&#xff1a;&#xff08;暴力回溯&#xff09; 代码示例&#xff1a; 暴力搜索&#xff1a; Dfs代码示例&#xff1a;&#xff08;搜索&#xff09; 暴力递归产生的递归树&…...

数据库常见问题(持续更新)

数据库常见问题(持续更新) 1、数据库范式&#xff1f; 1NF&#xff1a;不可分割2NF&#xff1a;没有非主属性对候选码存在部分依赖3NF&#xff1a;没有非主属性传递依赖候选码BCNF&#xff1a;消除了主属性对对候选码的传递依赖或部分依赖 2、InnoDB事务的实现&#xff1f; …...

定个小目标之刷LeetCode热题(40)

94. 二叉树的中序遍历 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 直接上代码吧&#xff0c;中序遍历左根右 class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> res new ArrayList<Integer>(…...

Linux--线程(概念篇)

目录 1.背景知识 再谈地址空间&#xff1a; 关于页表&#xff08;32bit机器上&#xff09; 2.线程的概念和Linux中线程的实现 概念部分&#xff1a; 代码部分&#xff1a; 问题&#xff1a; 3.关于线程的有点与缺点 4.进程VS线程 1.背景知识 再谈地址空间&#xff1a…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...