鴻蒙應用開發(fā)從入門到入行 - 篇5:組件化開發(fā)思想開發(fā)鴻蒙案例(詳解父子組件傳值

鴻蒙應用開發(fā)從入門到入行

第五天 - 組件化開發(fā)思想開發(fā)鴻蒙案例(詳解父子組件傳值)

導讀:在本篇文章里古胆,您將掌握組件化開發(fā)、組件傳值等相關(guān)知識纺弊。并能徹底弄懂鴻蒙父子組件數(shù)據(jù)的同步機制牛欢。本篇干貨很多,特別是有些關(guān)于組件的細節(jié)淆游,需要好好掌握傍睹。

本次整體學習目標介紹

  • 最近比較忙隔盛,不過好在本文也是緊趕慢趕的弄出來了。

  • 話不多說拾稳,我們先回顧一下我們需要做的案例

image.png
  • 我們發(fā)現(xiàn)吮炕,這是一個綜合性比較強的案例,涉及了布局访得、狀態(tài)切換龙亲、列表渲染、數(shù)據(jù)新增悍抑、側(cè)滑刪除等功能鳄炉。非常適合入門時的綜合練手。

  • 接下來搜骡,我們分析一下這個案例布局大致的劃分

Progress的使用

image.png
  • 咱們本次案例中需要用到一個進度緩拂盯,如上圖

  • 這種效果ArkUI已為我們提供了內(nèi)置組件,即 Progress

  • 使用方法

    Progress( { value: 當前值, total: 總值 } )
    
  • Progress( { value: 4, total: 10 } ) // 總量10记靡,已完成4
    
  • 默認情況下谈竿,Progress產(chǎn)生的是水平進度條,因此簸呈,上述代碼會得到如下圖效果

image.png
  • 一些細節(jié):

    • 默認情況下榕订,value為0,total為100

    • 當Progress的寬度大于或等于高度時水平顯示(如上圖)

    • 當Progress的寬度小于高度時(不包含等于蜕便,必須小于高度)垂直顯示(如下圖)

image.png
  • type參數(shù)

    • 用法

      Progress( { value: 5, total: 10, type: ProgressType.Ring } )
      
    • 通過使用Progress傳入type屬性劫恒,可以修改Progress的樣式

    • 該參數(shù)需要傳入ProgressType類型的枚舉,一共有5種樣式轿腺,分別如下

      • ProgressType.Linear:線性樣式两嘴,默認值。樣式為上邊兩張圖效果族壳。

      • ProgressType.Ring:環(huán)形樣式(無刻度)憔辫,樣式如下

image.png
- ProgressType.ScaleRing:環(huán)形樣式(有刻度),樣式如下
image.png
- ProgressType.Eclipse:圓形樣式
image.png
- 和ProgressType.Capsule:膠囊樣式(通過寬高設(shè)置水平或水平樣式仿荆,參照ProgressType.Linear)
image.png
  • 修改顏色贰您,默認情況下,進度條底色為灰色拢操,前景色(進度值的色)為漸變藍色锦亦。如果需要改,可以分別通過backgroundColor(背景色)與 color(前景)改

            Progress({ value: 3, total: 10, type: ProgressType.Ring })
              .width(80)
              .height(80)
              .backgroundColor(Color.Red)
              .color(Color.Pink)
    
    • 這里是把背景改為紅令境,進度值為粉色杠园,如下圖
image.png

組件化開發(fā)

  • 組件:組成頁面的一部分
  • 組件化開發(fā):把頁面的每一部分當成一個組件,然后把這些組件像搭積木一樣搭在一起即為組件化開發(fā)
  • 組件化開發(fā)優(yōu)勢:代碼分門別類舔庶,頁面與邏輯內(nèi)聚抛蚁,方便閱讀與維護陈醒、方便復用,等等瞧甩,總之好多好多優(yōu)點钉跷,這就不說了。

項目準備

  • 我們來看看亲配,這個年度待辦案例尘应,我們本次分幾個區(qū)域(幾個組件)
image.png
  • 通過上圖發(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)

  • 分析布局如圖
image.png
  • 根據(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)形進度條那里

image.png
  • 如上圖解釋的:這里其實有兩個組件Progress用來顯示環(huán)形進度條俩块,但是它沒有文字黎休,所以還要搭一個Text

  • 而如果不加堆疊,則他們會并排顯示玉凯,如下圖

image.png
  • 所以此時我們是期望能讓 3 / 10 這個Text能疊在Progress上面势腮,所以這種層疊效果,需要套一個Stack來實現(xiàn)(注意漫仆,默認情況下后面的在最上層捎拯,因此Text必須放在最后。除此外盲厌,默認都是居中堆疊署照,所以剛好Text就在環(huán)形中間了)
  • 注意:本代碼里的寬高顏色都可以由各位根據(jù)預覽效果自行設(shè)置,不必強求與貓林老師一致

TodoInput - 布局實現(xiàn)

image.png
  • 如上圖所示吗浩,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)

image.png
  • 發(fā)現(xiàn)這個區(qū)域就是一堆上一篇文章里教大家封裝的組件,直接拿來用即可

    • 注:上一篇里貓林老師不小心起錯名字微王。其實規(guī)范的名字應該叫TodoItem屡限,上一篇寫成了ToDoItem,這里正好勘誤炕倘,用更規(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文件,聲明這個類单绑,并導出

image.png
  • 文件內(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)于年度待辦案例活翩,因此此時先把TodoMainTodoItem代碼做一個備份烹骨。然后再開始練下面語法
  • 語法步驟:

    • 子里聲明一個成員變量
    • 父里使用組件時,在小括號里傳入
  • 例如材泄,本案例中我們有 TodoMainTodoItem沮焕,因為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,如下圖

image.png
  • 注意扫尖,此時雖能成功父傳子白对,但是父的數(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上沒有任何變化
  • 如果希望實現(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ù)改變顷啼,子里的也跟著變

image.png
  • 到目前,我們已經(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薄湿,如圖

image.png
  • 如果希望實現(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)如下圖所示根时,子和父都變成了學鴻蒙

image.png
  • 總結(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去渲染虏冻,所以這里又要父傳子,思路示意圖如下

image.png
  • 來到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ù)組的渲染

年度目標案例 - 目標完成打勾

  • 需求如下:
image.png
  • 根據(jù)上面數(shù)據(jù)說明田柔,打勾與否需要與每一項的finished屬性綁定硬爆,因此先來到TodoItemCheckboxselect屬性做雙向綁定

          Checkbox()
            .select($$this.item.finished)
    
    • 注:
      • Checkbox的select屬性方法是用來設(shè)置它是否打勾的摆屯。傳true代表打勾虐骑,傳false代表不打勾
      • 這里我們除了要數(shù)據(jù)能影響Checkbox以外廷没,也需要當我們操作組件后結(jié)果能影響到數(shù)據(jù)颠黎,因此加$$做雙向綁定
  • 然后根據(jù)finished的值狭归,做不同的處理

    • 如果是true过椎,給文字加刪除線亡鼠,否則不加任何線

    • 如果是true敷待,給文字加opacity為半透明勾哩,否則不透明

      • 解釋:通過需求圖可以看到思劳,要讓文字和刪除線都變灰敢艰,最快的方式是給整個Text加透明度钠导,配合白色底就會呈現(xiàn)灰色票堵。而如果單獨知識改文字顏色也即fontColor只會讓文字變灰措伐,刪除線不變提前,還得在decoration里加color才有用扇雕,不方便
    • 代碼如下

      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)如下圖效果

image.png
  • 大家思考一下:為什么會這樣呢底循?
    • 請思考
    • 請思考
    • 請思考
    • 請思考
    • 請思考
    • ..........
    • 沒錯熙涤,就是因為子里用的是@Prop灭袁,我們上面說過,@Prop只能實現(xiàn)父數(shù)據(jù)同步到子显沈,子里變了無法同步到父涤浇,此時就是這原因?qū)е?/li>
  • 如何解決只锭?

    • 相信同學們都能想到用@Link蜻展,父子雙向同步纵顾。但此時施逾,鴻蒙的缺陷體現(xiàn)出來了汉额!當你把TodoItem里的item改成@Link裝飾后闷愤,驚訝地發(fā)現(xiàn):在TodoMain里報錯了!如下圖
image.png
  • 原因:語法限制旬渠,@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ù)同步改變怔蚌,該怎么辦呢?

image.png
  • 既然子里無法改動到父旁赊,那就換個思路桦踊。讓父,自己改V粘籍胯!

  • 整體思路是:

    • 讓父提供一個修改數(shù)據(jù)的方法竟闪, 子里要修改時調(diào)用父的方法即可修改!

    • 如圖

image.png
  • 實現(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
  • 至此纠修,狀態(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ū)留言找一起學的同學幫助
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赞赖,一起剝皮案震驚了整個濱河市滚朵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌前域,老刑警劉巖辕近,帶你破解...
    沈念sama閱讀 212,080評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異匿垄,居然都是意外死亡移宅,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評論 3 385
  • 文/潘曉璐 我一進店門椿疗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漏峰,“玉大人,你說我怎么就攤上這事届榄∏城牵” “怎么了?”我有些...
    開封第一講書人閱讀 157,630評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長靖苇。 經(jīng)常有香客問我席噩,道長,這世上最難降的妖魔是什么贤壁? 我笑而不...
    開封第一講書人閱讀 56,554評論 1 284
  • 正文 為了忘掉前任悼枢,我火速辦了婚禮,結(jié)果婚禮上脾拆,老公的妹妹穿的比我還像新娘馒索。我一直安慰自己,他們只是感情好名船,可當我...
    茶點故事閱讀 65,662評論 6 386
  • 文/花漫 我一把揭開白布绰上。 她就那樣靜靜地躺著,像睡著了一般包帚。 火紅的嫁衣襯著肌膚如雪渔期。 梳的紋絲不亂的頭發(fā)上运吓,一...
    開封第一講書人閱讀 49,856評論 1 290
  • 那天渴邦,我揣著相機與錄音,去河邊找鬼拘哨。 笑死谋梭,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的倦青。 我是一名探鬼主播瓮床,決...
    沈念sama閱讀 39,014評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼产镐!你這毒婦竟也來了隘庄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,752評論 0 268
  • 序言:老撾萬榮一對情侶失蹤癣亚,失蹤者是張志新(化名)和其女友劉穎丑掺,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體述雾,經(jīng)...
    沈念sama閱讀 44,212評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡街州,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,541評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了玻孟。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片唆缴。...
    茶點故事閱讀 38,687評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖黍翎,靈堂內(nèi)的尸體忽然破棺而出面徽,到底是詐尸還是另有隱情,我是刑警寧澤匣掸,帶...
    沈念sama閱讀 34,347評論 4 331
  • 正文 年R本政府宣布趟紊,位于F島的核電站质礼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏织阳。R本人自食惡果不足惜眶蕉,卻給世界環(huán)境...
    茶點故事閱讀 39,973評論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望唧躲。 院中可真熱鬧造挽,春花似錦、人聲如沸弄痹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肛真。三九已至谐丢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蚓让,已是汗流浹背乾忱。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留历极,地道東北人窄瘟。 一個月前我還...
    沈念sama閱讀 46,406評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像趟卸,于是被迫代替她去往敵國和親蹄葱。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,576評論 2 349

推薦閱讀更多精彩內(nèi)容