概念
在微信小程序中,子组件向父组件传递数据可以通过自定义事件的方式实现。以下是子传父的通信步骤:
- 在子组件中,使用 triggerEvent 方法触发自定义事件,并携带需要传递的数据。示例如下:
// 子组件的.js文件
Component({
// ...
methods: {
sendDataToParent() {
const data = 'Hello, parent!'; // 要传递给父组件的数据
this.triggerEvent('customEventName', data); // 触发自定义事件并传递数据
},
},
});
- 在父组件中,使用 bind:customEventName 绑定子组件触发的自定义事件,并在对应处理函数中获取子组件传递的数据。示例如下:
<!-- 父组件的.wxml文件 -->
<child-component bind:customEventName="handleChildData"></child-component>
// 父组件的.js文件
Page({
// ...
handleChildData(event) {
const data = event.detail; // 获取子组件传递的数据
console.log(data); // 处理子组件传递的数据
},
});
当子组件调用 sendDataToParent 方法时,将会触发自定义事件 customEventName,并将数据传递给父组件。父组件通过 handleChildData 函数来处理子组件传递的数据。
通过这种方式,子组件就能将数据传递给父组件,实现了子传父的通信。
在微信小程序中,父组件向子组件传递数据可以通过属性传递的方式实现。以下是父传子的通信步骤:
- 在父组件中,在使用子组件的地方,通过属性将数据传递给子组件。示例如下:
<!-- 父组件的.wxml文件 -->
<child-component data="{
{dataFromParent}}"></child-component>
// 父组件的.js文件
Page({
data: {
dataFromParent: 'Hello, child!', // 要传递给子组件的数据
},
});
- 在子组件中,可以通过 properties 字段定义接收父组件传递数据的属性,并在需要的地方使用该属性。示例如下:
// 子组件的.js文件
Component({
properties: {
data: String, // 定义接收数据的属性
},
// ...
});
<!-- 子组件的.wxml文件 -->
<view>{
{data}}</view> <!-- 在子组件中使用父组件传递的数据 -->
父组件通过将 dataFromParent 数据传递给子组件的 data 属性,子组件可以接收到父组件传递的数据并在需要的地方使用。
通过这种方式,父组件就能将数据传递给子组件,实现了父传子的通信。
实战
消息通知系统:在一个消息通知系统中,父组件是消息列表,子组件是单个消息。父组件可以向子组件传递需要展示的消息内容,子组件负责展示该消息并提供交互功能(如标记已读)
<!-- 父组件的.wxml文件 -->
<view>
<message-list messages="{
{messages}}" bind:markRead="onMarkRead"></message-list>
</view>
// 父组件的.js文件
Page({
data: {
messages: [
{
id: 1,
title: "消息标题 1",
content: "消息内容 1",
isRead: false
},
{
id: 2,
title: "消息标题 2",
content: "消息内容 2",
isRead: false
}
]
},
onMarkRead(e) {
const messageId = e.detail;
// 标记消息为已读
const messages = this.data.messages.map(item => {
if (item.id === messageId) {
item.isRead = true;
}
return item;
});
this.setData({
messages });
console.log(`已标记消息 ${
messageId} 为已读`);
}
});
// 子组件的.js文件
Component({
properties: {
message: Object,
},
methods: {
markRead() {
const {
id } = this.properties.message;
// 触发标记已读事件
this.triggerEvent('markRead', id);
},
}
});
<!-- 子组件的.wxml文件 -->
<view>
<view>
<text>{
{message.title}}</text>
<text>{
{message.content}}</text>
</view>
<button wx:if="{
{!message.isRead}}" bind:tap="markRead">标记已读</button>
</view>
如上,父组件通过属性 messages 将消息列表传递给子组件 message-list。子组件接收到消息后,根据每个消息的内容展示相应的标题和内容,并提供标记已读的按钮。
点击子组件的「标记已读」按钮时,子组件触发 markRead 事件,并将消息的 id 作为参数传递给父组件。父组件接收到事件后,根据消息的 id 更新相应消息的状态为已读。
同时也可以在 onMarkRead 方法中编写处理已读操作的逻辑,例如向后端发送请求更新消息状态。
多层级导航菜单:在一个多层级导航菜单中,父组件是一级菜单,子组件是二级菜单。父组件可以向子组件传递选中的一级菜单项,在子组件中展示对应的二级菜单。
<!-- 父组件的.wxml文件 -->
<view>
<menu-list menus="{
{menus}}" bind:selectMenu="onSelectMenu"></menu-list>
</view>
// 父组件的.js文件
Page({
data: {
menus: [
{
id: 1,
name: "菜单项 1",
subMenus: [
{
id: 11,
name: "二级菜单项 1-1"
},
{
id: 12,
name: "二级菜单项 1-2"
}
]
},
{
id: 2,
name: "菜单项 2",
subMenus: [
{
id: 21,
name: "二级菜单项 2-1"
},
{
id: 22,
name: "二级菜单项 2-2"
}
]
}
],
selectedMenuId: null
},
onSelectMenu(e) {
const menuId = e.detail;
this.setData({
selectedMenuId: menuId });
console.log(`选中一级菜单 ${
menuId}`);
}
});
// 子组件的.js文件
Component({
properties: {
menus: Array
},
methods: {
selectSubMenu(e) {
const menuId = e.currentTarget.dataset.menuid;
// 触发选中菜单事件
this.triggerEvent("selectMenu", menuId);
}
}
});
<!-- 子组件的.wxml文件 -->
<view>
<view wx:for="{
{menus}}" wx:key="{
{index}}" bind:tap="selectSubMenu" data-menuid="{
{item.id}}">
<text>{
{item.name}}</text>
</view>
</view>
如上,父组件通过属性 menus 将一级菜单传递给子组件 menu-list。子组件接收到菜单后,根据每个菜单项的内容展示菜单名称。
点击子组件的菜单项时,子组件触发 selectMenu 事件,并将菜单的 id 作为参数传递给父组件。父组件接收到事件后,更新选中的一级菜单项。
同时也可以在 onSelectMenu 方法中编写处理菜单选中操作的逻辑,例如根据选中的菜单项加载对应的二级菜单。
电子商务平台:在一个电子商务平台中,父组件是商品列表,子组件是商品卡片。父组件可以向子组件传递商品信息,以便在子组件上展示商品名称、价格等详细信息。
<!-- 父组件的.wxml文件 -->
<view>
<product-list products="{
{products}}"></product-list>
</view>
// 父组件的.js文件
Page({
data: {
products: [
{
id: 1,
name: "商品 1",
price: 100,
image: "image_url_1"
},
{
id: 2,
name: "商品 2",
price: 200,
image: "image_url_2"
}
]
}
});
// 子组件的.js文件
Component({
properties: {
products: Array
}
});
<!-- 子组件的.wxml文件 -->
<view>
<view wx:for="{
{products}}" wx:key="{
{index}}">
<image src="{
{item.image}}"></image>
<text>{
{item.name}}</text>
<text>价格:{
{item.price}}</text>
</view>
</view>
如上,父组件通过属性 products 将商品列表传递给子组件 product-list。子组件接收到商品列表后,根据每个商品的信息展示商品名称、价格和图片等详细信息。
也可以根据实际需求,在子组件的模板文件中展示更多的商品信息,比如商品描述、评分等。
表单验证:在一个表单验证页面中,父组件是表单,子组件是各个表单字段。父组件可以将验证规则传递给子组件,子组件根据规则进行实时验证并将验证结果返回给父组件进行整体表单验证。
<!-- 父组件的.wxml文件 -->
<view>
<form>
<form-field name="username" label="用户名" rules="{
{usernameRules}}" bind:validate="onValidate"></form-field>
<form-field name="password" label="密码" rules="{
{passwordRules}}" bind:validate="onValidate"></form-field>
<button bind:tap="submitForm">提交</button>
</form>
</view>
// 父组件的.js文件
Page({
data: {
usernameRules: [
{
required: true,
message: "请输入用户名"
},
{
min: 6,
max: 20,
message: "用户名长度在6-20个字符之间"
}
],
passwordRules: [
{
required: true,
message: "请输入密码"
},
{
min: 8,
message: "密码长度至少8个字符"
}
]
},
onValidate(e) {
const {
name, valid } = e.detail;
console.log(`${name} 字段验证结果为: ${valid}`);
// 根据具体情况处理验证结果,比如更新数据或显示错误提示等
},
submitForm() {
// 在这里可以进行整体表单的验证逻辑,根据各字段的验证结果进行处理
// 若全部字段验证通过,则执行表单提交操作
}
});
// 子组件的.js文件
Component({
properties: {
name: String,
label: String,
rules: Array
},
methods: {
validateField(value) {
const rules = this.data.rules;
let valid = true;
let message = "";
for (let i = 0; i < rules.length; i++) {
const rule = rules[i];
if (rule.required && !value) {
valid = false;
message = rule.message;
break;
}
if (rule.min && value.length < rule.min) {
valid = false;
message = rule.message;
break;
}
if (rule.max && value.length > rule.max) {
valid = false;
message = rule.message;
break;
}
}
this.triggerEvent("validate", {
name: this.data.name, valid, message });
},
onInput(e) {
const value = e.detail.value;
this.validateField(value);
}
}
});
<!-- 子组件的.wxml文件 -->
<view>
<text>{
{label}}</text>
<input bindinput="onInput"></input>
</view>
如上,父组件定义了两个表单字段的验证规则 usernameRules 和 passwordRules,并将这些规则传递给子组件 form-field。子组件接收到规则后,根据用户输入的值实时进行验证,并将验证结果通过 validate 事件返回给父组件。
父组件可以根据接收到的验证结果进行相应的处理,比如更新数据或显示错误提示等。当用户点击提交按钮时,可以执行整体表单的验证逻辑,根据各字段的验证结果决定是否执行表单提交操作。
视频播放器:在一个视频播放器页面中,父组件是播放器控制条,子组件是视频播放器区域。父组件可以向子组件传递视频播放状态和进度,子组件根据传递的数据进行相应的播放操作。
<!-- 父组件的.wxml文件 -->
<view>
<video-controls bind:play="playVideo" bind:pause="pauseVideo" bind:seek="seekVideo" bind:timeupdate="updateProgress"></video-controls>
<video-player src="{
{videoSrc}}" bind:ended="videoEnded"></video-player>
</view>
// 父组件的.js文件
Page({
data: {
videoSrc: "https://example.com/video.mp4", // 视频地址
isPlaying: false, // 视频播放状态
currentTime: 0, // 当前播放时间
duration: 0, // 视频总时长
},
playVideo() {
this.setData({
isPlaying: true
});
},
pauseVideo() {
this.setData({
isPlaying: false
});
},
seekVideo(e) {
const {
value } = e.detail;
const videoContext = wx.createVideoContext("videoPlayer");
videoContext.seek(value);
},
updateProgress(e) {
const {
currentTime, duration } = e.detail;
this.setData({
currentTime,
duration
});
},
videoEnded() {
this.setData({
isPlaying: false,
currentTime: 0
});
}
});
// 子组件的.js文件
Component({
properties: {
src: String
},
methods: {
onPlay() {
this.triggerEvent("play");
},
onPause() {
this.triggerEvent("pause");
},
onSeek(e) {
const {
value } = e.detail;
this.triggerEvent("seek", {
value });
},
onTimeUpdate(e) {
const {
currentTime, duration } = e.detail;
this.triggerEvent("timeupdate", {
currentTime, duration });
},
onEnded() {
this.triggerEvent("ended");
}
}
});
<!-- 子组件的.wxml文件 -->
<view>
<video id="videoPlayer" src="{
{src}}" bindplay="onPlay" bindpause="onPause" bindseeked="onSeek" bindtimeupdate="onTimeUpdate" bindended="onEnded"></video>
</view>
如上,父组件包含一个视频控制条 video-controls 和一个视频播放器区域 video-player。父组件通过属性绑定将视频地址 videoSrc 传递给子组件。
子组件是一个简单的视频播放器,当用户点击播放按钮时,触发 play 事件,父组件监听该事件并更新播放状态 isPlaying。同理,暂停按钮触发 pause 事件,父组件也进行相应的状态更新。进度条拖动时触发 seek 事件,父组件监听并进行视频进度跳转。
视频播放过程中,子组件会触发 timeupdate 事件,将当前播放时间和视频总时长传递给父组件,父组件通过监听 timeupdate 事件更新播放时间和总时长。当视频播放完成时,子组件触发 ended 事件,父组件重新设置播放状态和当前播放时间。
需要注意的是,示例中使用了微信小程序的视频组件,并通过 wx.createVideoContext 方法获取到视频上下文,从而实现视频跳转操作。