# 子组件直接调用父组件方法

我们先写一个header组件,组件左侧为返回按钮,中间为标题,正常情况左侧按钮被点击时应该返回上一级,但某些场景下,我们可能希望它不要返回上一级,而是跳转到指定页面。

需求有了,我们来梳理实现思路,父组件可以给子组件传一个跳转的方法,子组件返回按钮被点击,我们先判断父组件是否有传进来返回方法,如果有直接调用父组件的方法,如果没有直接返回上一级。

# 子组件

示例代码所以样式就没写,样式也不是重点

  • wxml
<view class="header">
	<view class="back" bindtap="handleBack">返回</view>
	<view class="content">
		<slot></slot>
	</view>
</view>
1
2
3
4
5
6
  • js
Component({
  properties: {
    // 接收自定义返回函数,不限制类型,默认值undefined
    customBack: {
      type: null,
      value: undefined,
    }
  },
  methods: {
    // 返回
    handleBack() {
      const { customBack} = this.data;
      // 这里其实也应该再加个判断customBack是否是函数
      if (customBack) {
        customBack();
        return;
      }
      wx.navigateBack();
    },
  }
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 父组件

json文件引入也不写了,这是基础

  • wxml
<view class="page">
	<Header customBack="backFn">页面标题</Header>
</view>
1
2
3
  • js
Page({
  /**
   * 页面的初始数据
   */
  data: {
    backFn() {
      console.log('这就是要给子组件传的返回方法,这个方法必须得写在data中,作为普通方法写在data外面,经测试无法传进子组件');
      wx.redirectTo({
      	url: '填写你要跳转的路径,用哪个路由跳转方法随意,redirectTo是我随便写的'
      })
    },
  },
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 将常规组件改造为通过JS调用

有没有遇到过产品设计的提示框或者确认框,与小程序本身的不一样得情况。

写一个自定义提示组件,来通过js的来调用。

# 子组件

示例代码,无样式

  • wxml: modal.wxml
<view class="container" wx:if="{{isShow}}">
	<view class="contant">
		<view class="title">{{title}}</view>
		<view class="text">{{content}}</view>
		<view class="btn" catchtap="hidden">我知道了</view>
	</view>
</view>
1
2
3
4
5
6
7
  • js: modal.js
Component({
  data: {
    isShow: false, // 控制组件显示,默认隐藏
    title: '', // 提示框的标题
    content: '', // 自定义提示内容
  },
  methods: {
    // 调用组件显示 接收一个一个对象
    show({title = '温馨提示',content = ''}) {
      this.setData({
        isShow: true,
        title,
        content
      })
    },
    // 组件隐藏
    hidden() {
      this.setData({
        isShow: false,
        title: '',
        content: '',
      })
    }
  }
})
// 对外提供个方法 第二个参数为当前页面this
export const showModal = (options, self) => {
  const { id = '#modal' } = options; // 取组件id, 默认modal
  const ctx = self.selectComponent(id); // 拿组件实例
  ctx.show(options); // 调用组件方法 把数据传进去
  return ctx; // 最后把组件实例返回,以方便页面对组件做其他处理
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 父组件

json引入组件就不写了

  • wxml
<view class="page">
	<!-- 组件id很重要 -->
	<modal id="modal"></modal>
	<view class="btn" bindtap="handleBtnClick">弹出提示框</view>
</view>
1
2
3
4
5
  • js
import {showModal} from 'modal.js';
Page({
  handleBtnClick() {
  	// 如果组件的id与默认id不同,就需要在传个id进去
  	showModal({title: '提示', content: '您的操作很危险'}, this);
  }
})

1
2
3
4
5
6
7
8

# 小程序的计算属性observers

observers 是小程序自带的数据监听器,在某些场景下是可以做为计算属性来使用的。小程序也提供了计算属性的扩展包 ,但不在这块讲解范围内。

在做表单提交的时候,我们经常需要在表单所有的必填项填完之后才允许用户点击提交按钮,在填完之前表单一直是禁用状态,此时就可以使用observers

# 示例模拟代码

  • wxml
<view class="page">
	<view class="form">
		<view class="item">
			<view class="label">姓名:</view>
			<input data-key="name" bindinput='handleInput'/>
		</view>
		<view class="item">
			<view class="label">简介:</view>
			<input data-key="description" bindinput='handleInput'/>
		</view>
		<button disabled="disabled">提交</button>
	</view>
</view>
1
2
3
4
5
6
7
8
9
10
11
12
13
  • js

Component({
  data: {
  	name: '', // 姓名
  	description: '', // 简介
  	disabled: true, // 是否禁用
  },
  observers: {
    // 监听name和description的变化,接收变化后的值
    'name,description': function(name,description) {
      // 对其进行取反,name和description都填写之后不禁用,否则禁用
      this.setData({disabled: !Boolean(name && description)});
    }
  },
  methods: {
    handleInput({detail: {value}, target: {dataset: {key}}}) {
      this.setData({
        [key]: value
      })
    }
  }
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 通过Component()来定义页面

像上个例子的observers 用来处理表单提交非常好用,但遗憾的是这个方法只能在组件中使用,不能再page中使用。而且小程序的自定义组件behaviors ,page中就不能使用,所以我们就想怎么使用Component() 来构造页面。幸好腾讯提供了使用Component() 构造页面的方法,接下来我们就把上个例子改成用Component() 构造。

# 第一步

在页面的页面json 文件中添加usingComponents 内容

{
  "usingComponents": {}
}
1
2
3

# 第二步

普通页面是page() 构造的,我们将page 改成component

// 将这里改了,本应是page
Component({
  data: {
  	name: '', // 姓名
  	description: '', // 简介
  	disabled: true, // 是否禁用
  },
  observers: {
    // 监听name和description的变化,接收变化后的值
    'name,description': function(name,description) {
      // 对其进行取反,name和description都填写之后不禁用,否则禁用
      this.setData({disabled: !Boolean(name && description)});
    }
  },
  methods: {
    handleInput({detail: {value}, target: {dataset: {key}}}) {
      this.setData({
        [key]: value
      })
    }
  }
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 第三步

正常从其他页面通过路径传过来的参数我们通过onLoad(options){} 来接收,通过component 构造的页面接收参数就需要properties 来接收,例如:

访问页面 /pages/detail/index?id=123&type=2 ,对于参数我们就需要这么接收

// 将这里改了,本应是page
Component({
  properties: {
    id: Number,
    type: Number,
  },
})

1
2
3
4
5
6
7
8

# 第四步

页面的声明周期在普通页面是与data 平级的,在使用component 构造页面后,页面的生命周期 就需要写在methods 中。

onLoad: function() {
      this.data.paramA // 页面参数 paramA 的值
      this.data.paramB // 页面参数 paramB 的值
    }
Component({
  properties: {
    id: Number,
    type: Number,
  },
  methods: {
    onLoad() {},
    onShow() {},
    // ...
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 父组件覆盖子组件样式

# 子组件使用 externalClasses 接收外部样式

# 使用 ~ 引用页面样式、使用 ^ 引用父组件或祖先组件样式

# 小程序的抽象节点

Last Updated: 2020-4-27 22:56:04