鴻蒙應用開發(fā)從入門到入行
第五天 - 組件化開發(fā)思想開發(fā)鴻蒙案例(詳解父子組件傳值)
導讀:在本篇文章里古胆,您將掌握組件化開發(fā)、組件傳值等相關(guān)知識纺弊。并能徹底弄懂鴻蒙父子組件數(shù)據(jù)的同步機制牛欢。本篇干貨很多,特別是有些關(guān)于組件的細節(jié)淆游,需要好好掌握傍睹。
本次整體學習目標介紹
最近比較忙隔盛,不過好在本文也是緊趕慢趕的弄出來了。
話不多說拾稳,我們先回顧一下我們需要做的案例
我們發(fā)現(xiàn)吮炕,這是一個綜合性比較強的案例,涉及了布局访得、狀態(tài)切換龙亲、列表渲染、數(shù)據(jù)新增悍抑、側(cè)滑刪除等功能鳄炉。非常適合入門時的綜合練手。
接下來搜骡,我們分析一下這個案例布局大致的劃分
Progress的使用
咱們本次案例中需要用到一個進度緩拂盯,如上圖
這種效果
ArkUI
已為我們提供了內(nèi)置組件,即Progress
-
使用方法
Progress( { value: 當前值, total: 總值 } )
-
例
Progress( { value: 4, total: 10 } ) // 總量10记靡,已完成4
默認情況下谈竿,Progress產(chǎn)生的是水平進度條,因此簸呈,上述代碼會得到如下圖效果
-
一些細節(jié):
默認情況下榕订,value為0,total為100
當Progress的寬度大于或等于高度時水平顯示(如上圖)
當Progress的寬度小于高度時(不包含等于蜕便,必須小于高度)垂直顯示(如下圖)
-
type參數(shù)
-
用法
Progress( { value: 5, total: 10, type: ProgressType.Ring } )
通過使用Progress傳入type屬性劫恒,可以修改Progress的樣式
-
該參數(shù)需要傳入
ProgressType
類型的枚舉,一共有5種樣式轿腺,分別如下ProgressType.Linear:線性樣式两嘴,默認值。樣式為上邊兩張圖效果族壳。
ProgressType.Ring:環(huán)形樣式(無刻度)憔辫,樣式如下
-
- ProgressType.ScaleRing:環(huán)形樣式(有刻度),樣式如下
- ProgressType.Eclipse:圓形樣式
- 和ProgressType.Capsule:膠囊樣式(通過寬高設(shè)置水平或水平樣式仿荆,參照ProgressType.Linear)
-
修改顏色贰您,默認情況下,進度條底色為灰色拢操,前景色(進度值的色)為漸變藍色锦亦。如果需要改,可以分別通過
backgroundColor
(背景色)與color
(前景)改Progress({ value: 3, total: 10, type: ProgressType.Ring }) .width(80) .height(80) .backgroundColor(Color.Red) .color(Color.Pink)
- 這里是把背景改為紅令境,進度值為粉色杠园,如下圖
組件化開發(fā)
- 組件:組成頁面的一部分
- 組件化開發(fā):把頁面的每一部分當成一個組件,然后把這些組件像搭積木一樣搭在一起即為組件化開發(fā)
- 組件化開發(fā)優(yōu)勢:代碼分門別類舔庶,頁面與邏輯內(nèi)聚抛蚁,方便閱讀與維護陈醒、方便復用,等等瞧甩,總之好多好多優(yōu)點钉跷,這就不說了。
項目準備
- 我們來看看亲配,這個年度待辦案例尘应,我們本次分幾個區(qū)域(幾個組件)
通過上圖發(fā)現(xiàn),我們需要三大區(qū)域吼虎,分別對應
頭部區(qū)(統(tǒng)計)
犬钢、輸入?yún)^(qū)
、列表展現(xiàn)區(qū)
思灰,因此新建三個組件玷犹,然后集中到Index.ets
來用-
步驟
新建項目,過程略
把側(cè)滑顯示的刪除圖標放到
entry/src/main/resources/base/media
文件夾里(點我下載)在
pages
同級目錄洒疚,新建view
文件夾-
在
view
文件夾歹颓,新建三個ets
文件,分別起名叫TodoHeader
油湖、TodoInput
巍扛、TodoMain
,里面乏德,每個組件里放一個Text
作為暫時的顯示撤奸,且導出這個組件,這里僅貼TodoHeader代碼喊括,其他兩個組件類似@Component export struct TodoHeader { build () { Text('TodoHeader') } }
-
然后來到
pages/Index.ets
胧瓜,導入這三個組件,并依次寫到Column
里(因為這三個組件如上圖所示郑什,就是從上到下依次排列)- 技巧:上篇說過府喳,不用導入,只要在組件寫了
export
的情況下蘑拯,直接寫組件名出提示后按回車钝满,會自動生成導入代碼 - Index.ets代碼如下
import { TodoHeader } from '../view/TodoHeader' import { TodoInput } from '../view/TodoInput' import { TodoMain } from '../view/TodoMain' @Entry @Component struct Index { build() { Column({ space: 20 }) { TodoHeader() TodoInput() TodoMain() } .height('100%') .width('100%') .backgroundColor('#f2f3f5') } }
- 技巧:上篇說過府喳,不用導入,只要在組件寫了
注意:根據(jù)效果圖發(fā)現(xiàn)整個頁面背景是灰色的,因此記得給整個Column加背景色申窘,每個區(qū)域之間有間距弯蚜,因此加space
至此,項目準備工作完成
TodoHeader - 布局實現(xiàn)
- 分析布局如圖
-
根據(jù)上圖很明顯能發(fā)現(xiàn)偶洋,就是一個Row(水平布局)熟吏,里面一個Text顯示標題距糖,一個Stack放層疊布局(里面再放Progress與Text玄窝,具體后面說)
- 記得給Row寬度牵寺、高度與背景色、Text要加粗恩脂、內(nèi)容居中等
-
基于此帽氓,代碼如下
@Component export struct TodoHeader { build() { Row() { Text('今年目標') .fontSize(30) .fontWeight(FontWeight.Bold) .margin({ right: 20 }) Stack() { Progress({ value: 0, total: 10, type: ProgressType.Ring }) .width(80) .height(80) Text(`0 / 10`) } } .width('100%') .height(110) .justifyContent(FlexAlign.Center) .backgroundColor(Color.White) } }
這里其他屬性都很容易,如果有不懂的可以評論區(qū)留言
主要解釋下
Stack
環(huán)形進度條那里
如上圖解釋的:這里其實有兩個組件
Progress
用來顯示環(huán)形進度條俩块,但是它沒有文字黎休,所以還要搭一個Text
而如果不加堆疊,則他們會并排顯示玉凯,如下圖
- 所以此時我們是期望能讓
3 / 10
這個Text
能疊在Progress
上面势腮,所以這種層疊效果,需要套一個Stack
來實現(xiàn)(注意漫仆,默認情況下后面的在最上層捎拯,因此Text必須放在最后。除此外盲厌,默認都是居中堆疊署照,所以剛好Text就在環(huán)形中間了)
- 注意:本代碼里的寬高顏色都可以由各位根據(jù)預覽效果自行設(shè)置,不必強求與貓林老師一致
TodoInput - 布局實現(xiàn)
如上圖所示吗浩,Row里放
TextInput
即可建芙,記得讓TextInput不用鋪滿Row。因為將來必然會需要拿到輸入框里的內(nèi)容懂扼,因此聲明個變量禁荸,并與之雙向綁定-
代碼如下
@Component export struct TodoInput { @State newFlag: string = '' build() { Row() { TextInput({ placeholder: '請輸入新目標', text: $$this.newFlag }) .width('90%') } .width('100%') .height(80) .backgroundColor(Color.White) .justifyContent(FlexAlign.Center) } }
TodoMain - 布局實現(xiàn)
-
發(fā)現(xiàn)這個區(qū)域就是一堆上一篇文章里教大家封裝的組件,直接拿來用即可
- 注:上一篇里貓林老師不小心起錯名字微王。其實規(guī)范的名字應該叫
TodoItem
屡限,上一篇寫成了ToDoItem
,這里正好勘誤炕倘,用更規(guī)范的寫法
- 注:上一篇里貓林老師不小心起錯名字微王。其實規(guī)范的名字應該叫
-
步驟
-
來到
view
新建TodoItem.ets
文件钧大,然后寫如下代碼(分析過程略,見上篇)@Component export struct TodoItem { build() { Row() { Checkbox() .margin({ left: 20, right: 20 }) Text('跟貓林學鴻蒙') } .width('100%') .height(40) .backgroundColor(Color.White) .borderRadius(20) } }
-
然后來到
TodoMain
組件罩旋,進行導入與使用啊央,為了暫時能看到一堆內(nèi)容,我們用ForEach
先循環(huán)生成10個涨醋,TodoMain
代碼如下import { TodoItem } from './TodoItem' @Component export struct TodoMain { @State todoList: number[] = [1, 2, 3, 4, 5, 6, 7] build() { Column({ space: 10 }) { ForEach(this.todoList, (item: number) => { TodoItem() }) } .width('100%') } }
- 這里
TodoMain
的根容器是Column
瓜饥,因為它需要一行一行從上到下來顯示
- 這里
-
初始數(shù)據(jù)與說明
-
初始數(shù)據(jù)如下
class TodoModel { text: string = '' finished: boolean = false } @State totalFlags: Array<TodoModel> = [ { text: "月入5萬", finished: false }, { text: "中彩票500萬", finished: false }, { text: "找個富婆", finished: false }, { text: "買套別墅", finished: false }, { text: "改掉愛做夢的習慣", finished: false }, ];
-
解釋
- 每個新年目標都是個對象,有兩種屬性:text(目標文字)浴骂、finished(是否完成)
- 以下是對語法的解釋乓土,會TS的可跳過下面這三段
- 因為ArkTS是一種類型嚴謹?shù)恼Z言,因此需要對這種對象做一個類型定義,即聲明一個類叫TodoModel趣苏,它里面有兩個這樣的屬性
- totalFlags即為這種對象類型的數(shù)組狡相,例如Array<number>代表數(shù)組每個元素都是數(shù)值類型的數(shù)組,所以上面寫的Array<TodoModel>代表數(shù)組每個元素都是TodoModel類型的數(shù)組
- 數(shù)組也可以簡寫為
number[]
食磕、本例中的Array<TodoModel>
可簡寫為TodoModel[]
按照官方的編碼規(guī)范指導尽棕,這種項目中用到的數(shù)據(jù)對應的類,要寫到
viewsmodel
文件夾(與pages平級)彬伦,我們新建好文件夾后滔悉,也在此文件夾再新建TodoModel.ets
文件,聲明這個類单绑,并導出
- 文件內(nèi)代碼如下
export class TodoModel {
text: string = ''
finished: boolean = false
}
-
根據(jù)功能分析回官,本案例的年度目標必然是一個數(shù)組,且可對其增搂橙、刪孙乖、改。但是這個數(shù)組在本案例中三大組件里都需要用:
- TodoHeader 需要數(shù)組來統(tǒng)計總目標和已完成目標
- TodoInput 需要給數(shù)組添加新內(nèi)容
- TodoMain 需要展示數(shù)組內(nèi)容份氧,并且將來側(cè)滑時需要能刪除數(shù)組內(nèi)容
-
基于上面需求唯袄,提問:數(shù)組應該放在哪?
- 沒錯蜗帜,應該放到他們共同的父組件恋拷,本案例即為Index.ets里。
- 來到
Index.ets
厅缺,導入上面聲明的類蔬顾,并聲明一個數(shù)組
...... import { TodoModel } from '../viewmodel/TodoModel' @Entry @Component struct Index { @State totalFlags: Array<TodoModel> = [ { text: "月入5萬", finished: false }, { text: "中彩票500萬", finished: false }, { text: "找個富婆", finished: false }, { text: "買套別墅", finished: false }, { text: "改掉愛做夢的習慣", finished: false }, ]; build() { ...... } }
組件傳參 - 父傳子
-
此時數(shù)據(jù)有了,但還沒交給
TodoMain
去顯示湘捎,此時相當于要把Index
里的數(shù)據(jù)給TodoMain
诀豁,且因為Index
是父組件,TodoMain
是子組件窥妇,因此這種數(shù)據(jù)傳遞方式叫父傳子舷胜,即把父的數(shù)據(jù)傳遞給子組件- 注:本章節(jié)主要是為了學習語法,最終部分代碼不會出現(xiàn)于
年度待辦案例
活翩,因此此時先把TodoMain
與TodoItem
代碼做一個備份烹骨。然后再開始練下面語法
- 注:本章節(jié)主要是為了學習語法,最終部分代碼不會出現(xiàn)于
-
語法步驟:
- 子里聲明一個成員變量
- 父里使用組件時,在小括號里傳入
例如材泄,本案例中我們有
TodoMain
和TodoItem
沮焕,因為TodoMain
包含了TodoItem
。所以TodoMain
是父TodoItem
是子拉宗。我們就用這兩個組件試試父傳子
-
代碼步驟:
-
TodoItem
里聲明一個變量叫name
峦树,并在Text
里使用辣辫,代碼如下export struct TodoItem { // 聲明個成員變量,等待父傳魁巩,注意:此時沒加任意裝飾器 name: string = '' build() { ....... Text(this.name) } }
-
TodoMain
里聲明一個變量name络它,并傳遞給TodoItem
@Component export struct TodoMain { ....... @State name: string = 'abc' build() { Column({ space: 10 }) { ........ ForEach(this.todoList, (item: number) => { // 這里是傳參,把父的name傳遞給了子里的name TodoItem({ name: this.name }) }) } .width('100%') } }
此時會發(fā)現(xiàn)歪赢,正因為把父的
name
,也即數(shù)據(jù)為abc
单料,傳遞給了子埋凯,所以此時TodoItem
顯示的即為abc
,如下圖
-
注意扫尖,此時雖能成功父傳子白对,但是父的數(shù)據(jù)一旦改變,子并不會跟著改變
-
我們此時可以測試一下换怖,在
TodoMain
里甩恼,添加一個Button,并在Button里修改掉name
的值沉颂,如下代碼export struct TodoMain { .... @State name: string = 'abc' build() { Column({ space: 10 }) { Button('我改').onClick((event: ClickEvent) => { this.name = 'Good貓林' }) ......... } ...... } }
- 點擊按鈕后會發(fā)現(xiàn)条摸,子組件也即
TodoItem
上沒有任何變化
- 點擊按鈕后會發(fā)現(xiàn)条摸,子組件也即
-
如果希望實現(xiàn)父的數(shù)據(jù)改變,子的數(shù)據(jù)能隨著改變铸屉,需要在子的變量前加@Prop裝飾器
-
修改
TodoItem
里的name钉蒲,前面加上@Prop,再來點擊按鈕看變化export struct TodoItem { // 此時加了@Prop修飾 @Prop name: string = '' build() { Row() { Checkbox() .margin({ left: 20, right: 20 }) Text(this.name) } ....... } }
此時點擊按鈕彻坛,讓父的數(shù)據(jù)改變顷啼,子里的也跟著變
-
- 到目前,我們已經(jīng)學了兩個用來修飾成員變量的裝飾器昌屉,分別是
@State
钙蒙、@Prop
,我們對它總結(jié)一下- @State: 主要是裝飾給組件自己使用的數(shù)據(jù)间驮,效果:能讓成員變量的值改變后,界面也能刷新
- @Prop: 主要是用在作為子組件時烤咧,用來裝飾由父傳遞過來的數(shù)據(jù)抢呆,效果:能讓父的數(shù)據(jù)改變子也能接收到
- 注意:在
ArkTS
中煮嫌,即使父傳遞的是引用類型的數(shù)據(jù)昌阿,若不加@Prop
修飾,一樣會導致父的數(shù)據(jù)改變子里不會變,同學們有興趣可以自行測試
- 注意:在
組件傳參 - 父傳子雙向同步
上面我們講到懦冰,子里的成員變量加
@Prop
后刷钢,即可讓父的數(shù)據(jù)改變,子隨之改變伴澄,也即父的數(shù)據(jù)自動同步到子非凌。-
但是痰滋,目前無法實現(xiàn)子同步到父,也即子里改變了這個父傳進來的數(shù)據(jù),子里自身能改變,但是父的無法改變最盅。也即Vue框架里的
單向數(shù)據(jù)流
例:在
TodoMain
里用一個Text顯示name的值涡贱,并在TodoItem
里給Row
加點擊事件并修改name的值问词,我們可觀察效果-
TodoMain代碼
export struct TodoMain { .... @State name: string = 'abc' build() { Column({ space: 10 }) { Button('我改').onClick((event: ClickEvent) => { this.name = 'Good貓林' }) Row() { // 顯示name Text(this.name) } ......... } ...... } }
-
TodoItem代碼
export struct TodoItem { // 此時加了@Prop修飾 @Prop name: string = '' build() { Row() { ...... } .onClick(() => { this.name = '學鴻蒙' }) ....... } }
-
發(fā)現(xiàn)當我們點擊
TodoItem
時激挪,TodoItem
自身的name數(shù)據(jù)了學鴻蒙
垄分,但是父里的還是abc
薄湿,如圖
如果希望實現(xiàn):父改了數(shù)據(jù),自動同步到子听诸。以及子改了數(shù)據(jù)蚕泽,也同步到父须妻,需要把子里的
@Prop
修改成@Link
-
注意:如果用
@Link
修飾了成員變量璧南,則成員變量不可初始化司倚,例export struct TodoItem { // 此時加了@Link动知,不用初始化 @Link name: string build() { ....... } }
雖然僅僅只是把
@Prop
改成了@Link
盒粮,且把初始化值的部分刪了丹皱,但此時已經(jīng)完成了雙向同步摊崭,我們來點擊一下Row測試一下效果呢簸,會發(fā)現(xiàn)如下圖所示根时,子和父都變成了學鴻蒙
- 總結(jié)
@Prop
與@Link
- 相同點:
- 都是用在子組件蛤迎,用來接收父傳遞過來的數(shù)據(jù)忘苛,
- 都可以實現(xiàn)父改變數(shù)據(jù)后同步給子
- 不同點:
- 初始化值不同:
- @Prop需要初始化值扎唾,相當于給默認值胸遇≈侥鳎可以實現(xiàn)逗威,父如果傳了就用父的數(shù)據(jù),如果沒傳則用默認值
- @Link不能初始化概耻,相當于必須要父傳遞數(shù)據(jù)了才有數(shù)據(jù)
- 同步給父不同
- @Prop修飾的數(shù)據(jù),子里改變了不會同步給父
- @Link修飾的數(shù)據(jù)厌杜,子里改變了會同步給父
- 初始化值不同:
- 相同點:
年度目標案例 - 數(shù)據(jù)展示
回到我們之前最初的需求:要把
Index
里的數(shù)據(jù)給TodoMain
计螺,經(jīng)歷了上面組件傳參的學習后登馒,我們能很快完成谊娇。-
步驟:
注意:先把之前備份的TodoMain與TodoItem恢復
-
TodoMain
里济欢,聲明成員變量接收Index
傳遞過來的數(shù)組法褥,之前備份的文件里其實已經(jīng)聲明過半等,只不過是number類型的,改成TodoModel
即可谬擦,代碼如下@Link todoList: TodoModel[] = []
- 解釋為什么用
@Link
修飾符- 因為在
TodoMain
里將來需要改變數(shù)組(例如側(cè)滑刪除等)惨远,需要同步給父組件(即Index)北秽,因此用@Link
- 因為在
- 解釋為什么用
-
Index
里傳遞(這里Index的數(shù)據(jù)在上面初始數(shù)據(jù)與說明
章節(jié)已聲明過)TodoMain({ todoList: this.totalFlags })
此時已經(jīng)把數(shù)組從
Index
傳遞到TodoMain
,但是TodoMain
里又需要遍歷這個數(shù)組去產(chǎn)生TodoItem
辙培,并且需要把數(shù)組每一項交給TodoItem
去渲染虏冻,所以這里又要父傳子,思路示意圖如下
-
來到
TodoItem
鸥鹉,聲明一個TodoModel
的對象毁渗,用來接收數(shù)組每一項灸异,并且把item
里的finished
屬性綁定給Checkbox
肺樟,text
屬性綁定給Text
去顯示import { TodoModel } from '../viewmodel/TodoModel' @Component export struct TodoItem { @Prop item: TodoModel = new TodoModel() // 本次代碼 build() { Row() { Checkbox() .select(this.item.finished) // 本次代碼 .margin({ left: 20, right: 20 }) Text(this.item.text) // 本次代碼 } .width('100%') .height(40) .backgroundColor(Color.White) .borderRadius(20) } }
-
TodoMain
里使用ForEach
渲染import { TodoItem } from './TodoItem' import { TodoModel } from '../viewmodel/TodoModel' @Component export struct TodoMain { @Link todoList: TodoModel[] build() { Column({ space: 10 }) { // 本次代碼 ForEach(this.todoList, (item: TodoModel, index: number) => { // 這里用了ES6簡寫,完整寫法是 TodoItem({ item: item })代表把ForEach里的item傳遞給TodoItem需要的item TodoItem({ item }) }) // 以上是本次代碼改動 } .width('100%') } }
此時便完成了數(shù)組的渲染
年度目標案例 - 目標完成打勾
- 需求如下:
-
根據(jù)上面數(shù)據(jù)說明田柔,打勾與否需要與每一項的
finished
屬性綁定硬爆,因此先來到TodoItem
給Checkbox
的select
屬性做雙向綁定Checkbox() .select($$this.item.finished)
- 注:
- Checkbox的
select
屬性方法是用來設(shè)置它是否打勾的摆屯。傳true代表打勾虐骑,傳false代表不打勾 - 這里我們除了要數(shù)據(jù)能影響Checkbox以外廷没,也需要當我們操作組件后結(jié)果能影響到數(shù)據(jù)颠黎,因此加
$$
做雙向綁定
- Checkbox的
- 注:
-
然后根據(jù)
finished
的值狭归,做不同的處理如果是true过椎,給文字加刪除線亡鼠,否則不加任何線
-
如果是true敷待,給文字加opacity為半透明勾哩,否則不透明
- 解釋:通過需求圖可以看到思劳,要讓文字和刪除線都變灰敢艰,最快的方式是給整個Text加透明度钠导,配合白色底就會呈現(xiàn)灰色票堵。而如果單獨知識改文字顏色也即
fontColor
只會讓文字變灰措伐,刪除線不變提前,還得在decoration
里加color才有用扇雕,不方便
- 解釋:通過需求圖可以看到思劳,要讓文字和刪除線都變灰敢艰,最快的方式是給整個Text加透明度钠导,配合白色底就會呈現(xiàn)灰色票堵。而如果單獨知識改文字顏色也即
-
代碼如下
Text(this.item.text)// 本次代碼 .opacity(this.item.finished ? 0.4 : 1) .decoration({ type: this.item.finished ? TextDecorationType.LineThrough : TextDecorationType.None })
- 注:decoration就是Text設(shè)置文字線條的樣式,可以設(shè)置刪除線掌桩、下劃線等,具體可查官方文檔
插播 - 目前鴻蒙開發(fā)的缺陷
目前雖然實現(xiàn)了數(shù)據(jù)展現(xiàn)與目標完成的打勾,但此時存在數(shù)據(jù)傳遞的問題
通過最終效果圖我們知道阿宅,我們需要統(tǒng)計出已完成的目標數(shù)量展現(xiàn)到進度條里他膳。但此時误甚,我們在
TodoItem
里打勾了后窑邦,finished的值已經(jīng)改變(通過界面有變化能證明)冈钦,但是沒有傳入到父瞧筛,導致父里的finished還是false-
例
-
我們在父里(TodoMain)里寫一個Row輸出數(shù)組里下標0的元素的finished
....... build() { Column({ space: 10 }) { Row() { Text(this.todoList[0].finished + '') } .......... } } .....
注:這里的
+ ''
是為了把finished
轉(zhuǎn)為字符串類型较幌,因為Text
只能用字符串然后給第一項打勾乍炉,會發(fā)現(xiàn)如下圖效果
-
- 大家思考一下:為什么會這樣呢底循?
- 請思考
- 請思考
- 請思考
- 請思考
- 請思考
- ..........
- 沒錯熙涤,就是因為子里用的是
@Prop
灭袁,我們上面說過,@Prop
只能實現(xiàn)父數(shù)據(jù)同步到子显沈,子里變了無法同步到父涤浇,此時就是這原因?qū)е?/li>
-
如何解決只锭?
- 相信同學們都能想到用
@Link
蜻展,父子雙向同步纵顾。但此時施逾,鴻蒙的缺陷體現(xiàn)出來了汉额!當你把TodoItem
里的item
改成@Link
裝飾后闷愤,驚訝地發(fā)現(xiàn):在TodoMain
里報錯了!如下圖
- 相信同學們都能想到用
原因:語法限制旬渠,@Link只能接收父組件里聲明的第一層成員變量
-
什么叫第一層?
- 就好比一個數(shù)組损谦,數(shù)組里全是對象颅湘。對于數(shù)組而言即為第一層闯参,數(shù)組里的每個對象稱之為第二層鹿寨,以此類推
- 再好比一個嵌套對象脚草。即對象里有個屬性又是對象涩蜘,那么外層的稱之為第一層同诫,里面的屬性即為第二層误窖,以此類推
所以上述報錯里寫的
item
相當于就是數(shù)組里的對象霹俺,也即第二層,所以報錯
- 出現(xiàn)這個語法限制的根本原因是:目前的鴻蒙開發(fā)中想际,默認情況下無法監(jiān)聽到第二層的改動胡本。而@Link又要實現(xiàn)雙向同步侧甫,你都無法監(jiān)聽到改動,又如何完成雙向同步呢守屉?
- 所以鴻蒙也給了解決方案:使用
@Observed
加@ObjectLink
來解決胸梆。但是碰镜,貓林老師這里不打算講它绪颖。因為這個解決方案其實用起來也很麻煩繁瑣柠横,非常不人性化牍氛。- 有興趣的同學可以自行去學習Observed與ObjectLink搬俊,貓林老師這里只講最實用的技術(shù),幫助你更快開發(fā)鴻蒙玩祟!
- 題外話空扎,從API12開始,鴻蒙提供了
@ObservedV2
黑忱,力求解決監(jiān)聽這種監(jiān)聽屬性的問題甫煞。但貓林老師嘗鮮過抚吠,也不是很方便喊式,這里贊不推薦 - 題外話2:不要一聽貓林老師吐槽一句鴻蒙開發(fā)中存在這種不太方便的點岔留,就覺得鴻蒙不行。任何語言剛推出時總有些坑或者不太方便的地方需要等待后續(xù)更新何址。當初蘋果的Swift剛開始推出時也有許多地方有待完善里逆,蘋果也是慢慢更新使之越來越好。所以大家要保持好信心用爪,未來可期原押。而且,華為官方也確實不斷的對鴻蒙開發(fā)做著改進偎血,就像上面提到的
@ObservedV2
。所以鴻蒙開發(fā)未來必然會越來越方便烁巫,越來越容易好用署隘。可是到那時越容易就會越多人涌進亚隙,但是不要怕磁餐。你們在此刻開始進入鴻蒙的人,都算得上是鴻蒙的元老級程序員阿弃。(畢竟現(xiàn)在連純血鴻蒙的正式版都還沒發(fā)布诊霹,僅僅是測試版呢),所以對你們渣淳,更是抓住風口的弄潮兒脾还,必將成為吃到紅利的一波人!
待辦列表 - 解決缺陷 - 實現(xiàn)打勾改動同步給父
-
那入愧,貓林老師這里怎么解決上述缺陷呢鄙漏?首先,因為
@Link
目前不能用棺蛛,那咱們就把它換回@Prop
先..... @Component export struct TodoItem { @Prop item: TodoModel ..... }
可是
@Prop
又確實無法讓父的數(shù)據(jù)同步改變怔蚌,該怎么辦呢?
既然子里無法改動到父旁赊,那就換個思路桦踊。讓父,自己改V粘籍胯!
-
整體思路是:
讓父提供一個修改數(shù)據(jù)的方法竟闪, 子里要修改時調(diào)用父的方法即可修改!
如圖
-
實現(xiàn):
-
來到
TodoMain
杖狼,聲明一個方法如下changeStatus(item: TodoModel, index: number) { this.todoList[index] = { text: item.text, finished: !item.finished } }
- 解釋:
- 本方法需要傳入被點的
item
以及被點的item
的索引 - 通過索引的方式改掉數(shù)組中這一項炼蛤,文字不變,但是完成狀態(tài)取反即可
- 這時候可能有老鐵有疑問:
- 為什么不直接 item = !item.finished
- 還是那個問題:目前不支持監(jiān)聽第二層數(shù)據(jù)改變本刽,直接改item還是第二層鲸湃。
- 但數(shù)組是第一層赠涮,因此你用
數(shù)組[索引]
的方式子寓,就是在改第一層數(shù)據(jù),這是能被監(jiān)聽到的
- 本方法需要傳入被點的
- 解釋:
-
此時需要把這個方法傳遞給
TodoItem
笋除,因此TodoItem
需要聲明一個成員方法來接收export struct TodoItem { ...... onChange: () => void = () => {} ..... }
- 解釋:
- 方法名叫
onChange
斜友,它的類型是一個無參數(shù)無返回值的函數(shù),初始化值是一個空函數(shù) - 一般情況下垃它,這種由外界傳入的方法不需要加裝飾器
- 方法名叫
- 解釋:
-
然后給
Select
組件加onChange
事件鲜屏,這個事件是當Select
發(fā)生勾選狀態(tài)改變就會調(diào)用的事件,在里面調(diào)用傳入的onChange
方法Checkbox() .select(this.item.finished) .margin({ left: 20, right: 20 }) .onChange(() => { // 本次代碼 this.onChange() })
-
回到
TodoMain
做方法傳遞:此時調(diào)用TodoItem除了之前要傳入的item国拇,現(xiàn)在還要多一個onChange
ForEach(this.todoList, (item: TodoModel, index: number) => { // 此時調(diào)用TodoItem TodoItem({ item, onChange: () => { this.changeStatus(item, index) } }) })
- 解釋參數(shù):item即為當前變動的數(shù)據(jù)洛史,index即為當前數(shù)據(jù)對應的下標(都是changeStatus需要用到的內(nèi)容)
- 注意看:這里我沒有直接把
this.changeStatus
這個方法傳遞給onChange
,而是聲明了一個新的箭頭函數(shù)酱吝,只不過在箭頭函數(shù)里的函數(shù)體里調(diào)用了this.changeStatus
也殖,這么做的原因是this.changeStatus
方法里有this.todoList
這樣的代碼,我們都知道this是指當前環(huán)境上下文务热,它在TodoMain
里忆嗜,就代表TodoMain
這個上下文,所以它修改它的todoList沒毛病崎岂。但如果你是直接把this.changeStatus
傳遞給onChange
捆毫,那它相當于是在todoItem
里調(diào)用,同樣的this也會變成todoItem
上下文冲甘,此時它是沒有todoList
數(shù)組的绩卤,所以這里利用箭頭函數(shù)保留當前上下文的特點,在todoMain
里用箭頭函數(shù)再包一層江醇,即可保證this依然指向todoMain
------- 這段請可能理解濒憋,因為文字有其局限性,實在不理解等以后貓林老師出視頻教程再來看 - 附:有懂JS的同學可能會問:那可以
onChange: this.changeStatus.bind(.....)
這樣的方式傳遞嗎嫁审?似乎也能綁定當前this指向呀跋炕?- 答:不能!ArkTS中不允許使用
call
律适、apply
辐烂、bind
- 具體可查看當前最新文檔遏插,點我進入,打開頁面后搜:不支持Function.bind
- 答:不能!ArkTS中不允許使用
-
至此纠修,狀態(tài)改變同步給父已完成胳嘲。
鑒于篇幅關(guān)系以及一篇文章顆粒度太大也影響大家閱讀。所以
年度目標
這個案例的剩余部分我在下一篇文章講完扣草,其中下一篇也會以需求驅(qū)動的方式為大家講到很多干貨新知識了牛,請拭目以待!
總結(jié)今日內(nèi)容
- 組件
- 組件化開發(fā)思想
- 父子組件數(shù)據(jù)傳遞與同步
- 本篇干活與細節(jié)略多辰妙,需要多認真思考學習鹰祸。
- 忠告:我們是以需求驅(qū)動學習知識點。特別是本篇含有部分需要理解與實操的內(nèi)容密浑。鴻蒙零基礎(chǔ)的同學蛙婴,一定要好好的跟著敲本案例代碼才能理解
課后練習
- 判斷題
- 一個Progress的寬度是100,高度是150尔破,此時環(huán)形圖一定是垂直方向的線性樣式滾動條
互動簡答題
- 思考:本案例中街图,最終
TodoItem
里的數(shù)據(jù)打勾變化后(完成狀態(tài)變化),TodoMain
已經(jīng)能成功收到改動了懒构。那么它的父組件餐济,最早持有數(shù)組的Index
有收到改動嗎?請說出你的理由胆剧,打在評論區(qū)
判斷題答案
- 錯絮姆。原因自己思考,實在不懂可以評論區(qū)留言找一起學的同學幫助