微信小程序:自定義組件的數據傳遞


一、前言

如果小程序中有可復用的UI且具有一定的功能性,就可以使用自定義組件將其封裝起來。下面介紹一個簡單的組件和一個復雜的組件。

二、簡單的組件(計數器)

圖片描述

1. 組件功能介紹

這個組件常見於外賣軟件中,用於記錄想要購買的商品的數量。初始化的時候只有一個加號,點擊加號以后出現數字和減號,並最后將數字傳到組件外供外部使用。

2. 創建組件

首先在根目錄創建components文件夾(或者你喜歡的地方),然后創建num-controller文件夾(我取的組件名字),在這個文件夾上點擊右鍵新建一個component,名字依然叫做num-controller。

圖片描述

num-controller.wxml

<view class="num-controller">
  <view class="iconfont icon-jianshao sub-btn" hidden="{{num<1}}" bindtap="sub"></view>
  <view class="goods-num" hidden="{{num<1}}">{{num}}</view>
  <view class="iconfont icon-zengjia add-btn" bindtap="add"></view>
</view>

這段代碼就是加減兩個按鈕和一個數字,因為我使用的是字體圖標所以view里什么都沒有。

num-controller.json

{
  "component": true,
  "usingComponents": {}
}

這個文件在創建component的時候會自動寫入這段代碼。

num-controller.js

Component({
  /**
   * 組件的屬性列表
   */
  properties: {
    num: Number
  },

  /**
   * 組件的初始數據
   */
  data: {

  },

  /**
   * 組件的方法列表
   */
  methods: {
    add() {
      
      this.setData({
        num: this.data.num + 1
      })
      this.triggerEvent('numChange', this.data.num);
    },
    sub() {
      if(this.data.num > 0) {
        this.setData({
          num: this.data.num - 1
        })
      }
      this.triggerEvent('numChange', this.data.num);
    }
  }
})

組件內部接收一個參數num,類型是Number;
點擊加號觸發add方法,首先把init狀態變為false,然后數字+1,同時觸發numChange方法將改變的數字傳到組件外部
點擊減號觸發sub方法,數字-1,如果數字為0則把init狀態變為true,同時觸發numChange方法將改變的數字傳到組件外部

將組件數據傳到外部的方法為this.triggerEvent('方法名',{要傳遞的數據})

3. 引入組件

假如我要在index.wxml里引入組件:

<num-controller num="{{num}}" bindnumChange="numChange"></num-controller>

index.json

{
  "usingComponents": {
    "num-controller": "/components/num-controller/num-controller"
  }
}

在json文件里注冊組件。

index.js

data: {
    num: 1
},
numChange(e) {
    const numi = e.detail;
    
 }

data里的num是從組件外傳入的num,在numChange方法里用e.detail可以拿到組件內部通過this.triggerEvent傳出來的數據,然后根據業務需求做邏輯修改。

三、復雜的組件(篩選面板)

圖片描述

一個二級菜單,點擊左邊(一級)會改變右邊(二級)的展示。

1. 創建組件並引入

組件內部:

// filter-panel.wxml

<view class="filter-panel">
  <view class="panel-container">
    <view class="panel-left">
      ...
    </view>

    <view class="panel-right">
      ...
    </view>
  </view>
</view>
// filter-panel.json

{
  "component": true
}

組件外部:

// index.wxml

<filter-panel></filter-panel>
index.json

{
  "usingComponents": {
    "filter-panel": "/components/filter-panel/filter-panel-component"
  }
}

這樣就成功引入組件啦~(說真的組件化做好了非常舒服,后期會省很多力氣)

2.組件與外部的數據傳遞(重點)

(1) 固定數據渲染

// filter-panel-component.js

Component({
  /**
   * 組件的屬性列表
   */
  properties: {
    mode: String,
    panel: String,
    text: Array,
    active: Array
  },

  /**
   * 組件的初始數據
   */
  data: {
    filterActive:[]
  },

  /**
   * 組件的方法列表
   */
  methods: {
    ...
  }
})
// index.wxml

<filter-panel mode="mode1" panel="panel1" text="{{panel1Text}}" active="{{panel1Active}}"></filter-panel>
// index.js

panel1Text: [
  {
    'location': '附近',
    'choice': ['不限', '1km', '2km', '3km']
  }, {
    'location': '地鐵站',
    'choice': ['江漢路', '光谷廣場', '陶家嶺', '六渡橋']
  }
],
panel1Active: [0, 0]

從組件外向組件內傳遞數據,直接在外部引入的組件上傳。
這里我傳入了4個數據,
mode代表篩選面板的模式,雖然這里只寫了一種,但是實際上有三種形式,我寫在了一個組件里,所以每次引入的時候都要指定mode。
panel代表是第幾個面板,雖然模式有三種,但是首頁有四個面板,其中有兩個的mode是相同的,為了區分每一個面板傳入panel。
panel1Text是渲染的數據。
panel1Active是用戶選擇的數據。

到這里組件已經可以正常展示了,但是點擊顯示選中項還未實現。

(2) 可變數據渲染

控制組件active項的是外部的數據panel1Active: [0, 0],通過組件傳到了內部,在組件內部:

// filter-panel-component.js

  properties: {
    mode: String,
    panel: String,
    text: Array,
    active: Array
  },
  data: {
    filterActive:[]
  },
  attached() {
    this.dataInit();
  },

  /**
   * 組件的方法列表
   */
  methods: {
    dataInit() {
      let active = this.properties.active;
      this.setData({
        filterActive: active
      })
    },
    _chooseMode1Left(e) {
      let mode1LeftIndex = e.currentTarget.dataset.index;
      let mode1Active = this.data.filterActive;
      if (mode1LeftIndex == 0) {
        mode1Active.fill(0, 1, 2);
      } else {
        mode1Active.fill(-1, 1, 2);
      }
      mode1Active.fill(mode1LeftIndex, 0, 1);
      this.setData({
        filterActive: mode1Active
      })
    },
    _chooseMode1Right(e) {
      let mode1RightIndex = e.currentTarget.dataset.index;
      let mode1Active = this.data.filterActive;
      mode1Active.fill(mode1RightIndex, 1, 2);
      this.setData({
        filterActive: mode1Active
      })
      wx.setStorageSync(this.properties.panel, this.data.filterActive);
      this.triggerEvent('closePanel', {});
    }
  }

dataInit方法指把外部傳進來的active數組賦給內部的私有變量filterActive,然后在內部控制點擊的active項。

// index.wxml

<view class="filter-panel mode1" wx:if="{{mode == 'mode1'}}">
  <view class="panel-container">
    <view class="panel-left">
      <view class="left-item {{filterActive[0] == idx1?'active':''}}" bindtap="_chooseMode1Left" wx:for="{{text}}" wx:for-index="idx1" data-index="{{idx1}}">{{item.location}}</view>
    </view>

    <view class="panel-right {{filterActive[0] == idx1?'':'hide'}}" wx:for="{{text}}" wx:for-index="idx1" data-index="{{idx1}}">
      <view class="right-item {{filterActive[1] == idx2?'active':''}}" bindtap="_chooseMode1Right" wx:for="{{item.choice}}" wx:for-index="idx2" data-index="{{idx2}}">
        <span>{{item}}</span>
        <view class="iconfont icon-correct" hidden="{{filterActive[1] != idx2}}"></view>
      </view>
    </view>
  </view>
</view>

_chooseMode1Left和_chooseMode1Right是兩個私有方法,官方建議是在函數名前面加上下划線以便區分。
在點擊右側的時候觸發外部的方法closePanel。

(3) 組件內數據傳到外部

triggerEvent方法可以把組件內部的數據傳到外面,觸發組件外的事件。它接收3個參數:
this.triggerEvent('myevent', myEventDetail, myEventOption);

myEventDetail和myEventOption都是對象,myEventDetail是傳到組件外的數據,myEventOption有三個參數可以設置:

bubbles    默認false 事件是否冒泡
composed 默認false 事件是否可以穿越組件邊界
capturePhase 默認false 事件是否擁有捕獲階段

但是這里我並沒有用triggerEvent將數據傳出去,因為我這么做了之后發現組件內外的數據開始同步了,這不是我想要的效果。在組件外部有可能點擊其他地方隱藏篩選面板,就算用戶點擊了左側也不改變上一次保留的數據,所以我用wx.setStorageSync來保存上一次用戶確定的數據。如果用戶點擊其他地方隱藏面板,則從Storage里取出真實的數據渲染。

在組件外部可以這樣操作內部的方法:

// index.js

onReady() {
  this.panel1 = this.selectComponent("#panel1");
},
showPanel(e) {
  this.panel1.dataInit();
},
index.wxml

<filter-panel mode="mode1" panel="panel1" text="{{panel1Text}}" active="{{panel1Active}}" bind:closePanel="closePanel" id="panel1"></filter-panel>

通過id獲取到組件,然后調用組件內的方法。可以在每次顯示篩選面板的時候初始化數據。這就是我最后使用的解決辦法。

三、總結

這個項目里倒是沒用用到組件間的數據傳遞,所以只是組件和外部的傳遞,還算是比較簡單,但是一定要思考清楚數據的變化狀態。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM