Everything you need to know about WeChat Mini-Program component

And how it differs from the template

David Yu
Shanghai Coders
Published in
8 min readJan 5, 2020

--

WeChat Mini-Program has evolved in the past couple of years. Every time I dig into their official documents, I can still learn something new.

In this article, we will dive deep into everything that WeChat Mini-Program’s component can do.

Prerequisites

  • Basic understanding of WeChat Mini-Program
  • Basic JavaScript knowledge

When to use a component?

  • When you find yourself copying and pasting the same code
  • When you want something to be reusable
  • When you want a more maintainable codebase

How to use a component?

Usually, I create a folder named, “components”

And inside of the “components” folder, I will have the individual component names as the folder name.

  1. By right-clicking on the empty folder in DevTool, you can choose “New Component”

2. By typing in the component will generate the four files necessary for a component. Personally, I prefer Pascal Casing for component just because it looks nicer, but you can use whatever naming pattern.

3. And let’s say you want to use it in the “index” page. In index.json include

{ "usingComponents": {  "NewComponent": "/components/NewComponent/NewComponent" }}

4. Within the index.wxmlinclude the component as a tag

<view> <NewComponent/></view>

Lifecycle of Component

Similar to the pages of Mini-Program, component has its own lifecycles.

Component({
lifetimes: {
// When component is initialized
created() {},
// When component is attached to parent and this.setData is available
attached() {},
// When component is rendered
ready() {},
// When component is moved to other node, never used this lifecycle before
moved() {},
// When page is removed on page stack
detached() {},
error(e) {} },
})
The order of which lifecycles is executed

Note: You might have seen these methods written outside of the lifetimes object, on the official doc, they mention that it’s the old way. Not sure when they will deprecate it.

How to pass data to a component?

  1. Define the properties in the JS of the component
properties: { dataFromParent: {  type: String,  value: "" // Default value }}

You can also use the shorthand if you don’t need a default value

properties: { dataFromParent: String}

2. In the parent, you can pass in data like so

<NewComponent dataFromParent="test"/>

Or dynamic data, like so

<NewComponent dataFromParent="{{dynamicData}}"/>

You can access the property in this.data once the component is attached.

How to pass data back to the parent component?

In most cases, data should only flow in one direction.

But if you must, there’s a way to trigger a method of a parent from the child.

  1. Bind the parent method to the component
<NewComponent bindparentmethod="parentMethod" />

2. Use this.triggerEvent in the component to trigger the parent method

methods: { triggerParent() {  this.triggerEvent("parentmethod", {dataForParent: 'hello'}); }}

Notice that whatever is after bind becomes the event name for the first param of this.triggerEvent

3. Pass data in the second param of this.triggerEvent

The second param of this.triggerEvent takes an object.

4. Get data from detail of the event object

In the parent’s method, it will return an event object

parentMethod({detail}) {  const { dataForParent } = detail;  this.setData({   dynamicData: dataForParent  })}

Note: The third param of this.triggerEvent takes an option object

The options object takes bubbles and composed

this.triggerEvent('parentmethod', {}, { bubbles: true })

If bubbles is set to true, this triggers the parent component’s parentmethod

this.triggerEvent('parentmethod', {}, { bubbles: true, composed: true })

If composed is set to true, it will trigger the method within the component with the same event name, parentmethod

Opinion: I suggest to stay away from bubbling events because it leads to unexpected behavior.

How to use a component as a wrapper?

As mentioned above, a component can wrap another component.

But what if you want the content of the component to be dynamic?

That’s where <slot/> can be useful.

For example, if you have a component called NewComponent

<!-- components/NewComponent/NewComponent.wxml --><text>{{dataFromParent}}</text><button bindtap="triggerParent">Trigger Parent Method</button><slot/>

In the parent where NewComponent is used, whatever you insert inside of NewComponent will show up in the component as <slot/>

<view>
<NewComponent dataFromParent="{{dynamicData}}" bindparentmethod="parentMethod">
<text>Inserted content</text>
</NewComponent>
<navigator url="../other/other">Go</navigator>
</view>
You would get something like this

So what if you have different sets of elements that you want to insert in different places of the component?

In the component’s options, add multipleSlots: true

Component({
options: {
multipleSlots: true
},
properties: { /* ... */ },
methods: { /* ... */ }
})

And inside the component, you can define <slot/> with name

<text>{{dataFromParent}}</text><slot name="first"/><button bindtap="triggerParent">Trigger Parent Method</button><slot name="second"/>

When using the component, add slot="slotName" on the element

<view>
<NewComponent dataFromParent="{{dynamicData}}" bindparentmethod="parentMethod">
<text slot="first">Inserted content</text>
<text slot="second">Inserted content 2</text>
</NewComponent>
<navigator url="../other/other">Go</navigator>
</view>

What’s a generic selectable component?

Usually, if you want to include component within a component, using usingComponents in the .json file and use wx:if to determine if the component should show is good enough.

But if you want to add more abstraction to your code, you can use <selectable/>

<selectable/> let you inject component to another component when you declare it

In the .json file of the component where you want to inject the component, write

"componentGenerics": {  "selectable": true}

And in the same component’s .wxml file, you can add <selectable/>

<!-- components/NewComponent/NewComponent.wxml --><selectable />

In the page where you plan to use the component, add BOTH components to .json file

{  "usingComponents": {   "NewComponent": "../components/NewComponent/NewComponent",   "ChildComponent": "../../components/ChildComponent/ChildComponent"  }}

Then you can use ChildComponent to take place of <selectable/>

<NewComponent dataFromParent="{{dynamicData}}" generic:selectable="ChildComponent"/>

Note: If you want a default component to take place of <selectable/> , you can define a default in the component’s .json file

{
"componentGenerics": {
"selectable": {
"default": "path/to/default/component"
}
}
}

Special Note: generic:xxx="yyy" , the name of the component can only be a static value.

How to use behavior?

A behavior in Mini-Program is a set of logic that can be shared across components. It has its own set of data , properties , methodsand life cycles.

In other frameworks, it is similar to mixins.

You can define a behavior like so

// my-behavior.js
export default Behavior({
behaviors: [],
properties: {
myBehaviorProperty: {
type: String
}
},
data: {
myBehaviorData: {}
},
attached: function(){},
methods: {
myBehaviorMethod: function(){}
}
})

You can use a behavior like so

import MyBehavior from "../../behaviors/MyBehavior";Component({
behaviors: [MyBehavior]
})

Currently, there are two built-in behaviors

  1. wx://form-field

This is used to manipulate the <form/> tag.

You could use it to validate your form, sanitize your data, etc.

2. wx://component-export

This allows you to use a component as a function

In the component’s .js file, you can write

// MyComponent
Component({
behaviors: ['wx://component-export'], export: function() { return { myField: 'myValue' } }})

In the parent’s .wxml file, add in the component with id

<MyComponent id="the-id" />

In the parent’s .js file, you can access the export function

Page({
onLoad: function () {
console.log(this.selectComponent('#the-id').myField)
// should output "myValue"
},
})

As you can see, this export behavior allows you to access the values and methods of the child if you expose them.

Note: You can use this.selectComponent to access the element.

How to use relations?

Relations define the relationship between components and its nested components.

You might never need to use them. Unless you plan to publish a package that everyone can use.

Say you have a list component that nests other components

<custom-ul>
<custom-li> item 1 </custom-li>
<custom-li> item 2 </custom-li>
</custom-ul>

Then for <custom-ul/> , you can add some logic in linked , linkChanged , and unlinked

// path/to/custom-ul.js
Component({
relations: {
'./custom-li': {
type: 'child', // can be parent, child, ancestor, descendant
linked: function(target) {
// Execute whnenever custom-li is inserted, target is the element being included,triggered after the attached life cycle
},
linkChanged: function(target) {
// Every time custom-li is moved, triggered after the moved life cycle
},
unlinked: function(target) {
// When custom-li is remove, triggered after the detached life cycle
}
}
},
methods: {
_getAllLi: function(){
// Get all the nodes ofcustom-li if you need
var nodes = this.getRelationNodes('path/to/custom-li')
}
},
ready: function(){
this._getAllLi()
}
})

You can also do the same thing for the child component, where you swap the type to parent

How to use observers?

Observer listens to the changes of this.data

And since a component’s properties are bound to this.data of the component, you can listen to the value changes from the parent too.

For example,

Component({
observers: {
'dataFromParent': function (field) {
// field is the new value of dataFromParent
if(field !== this.data.localData) {
console.log('new data', field)
// Data changed, do something here
}
this.setData({
localData: field
})
}},
})

Note: Do not setData for the value that you are listening to, this will create an infinite loop.

Some other syntax for observers

Component({
observers: {
'some.subfield': function(subfield) {
// Any time some or some.subfield is changed
},
'arr[12]': function(arr12) {
// Any time arr[12] or arr is changed
},
}
})

To listen to all this.setData

Component({
observers: {
'**': function() {
// Trigger every setData
},
},
})

Pure data = private data

Pure data is data that will not be evaluated in .wxml which also means it can only be used within the component

Component({
options: {
pureDataPattern: /^_/ // All keys that start with _ are pure data
},
data: {
a: true, // normal data
_b: true, // pure data, but still accessible in this.data
},
methods: {
myMethod() {
this.setData({
c: true, // normal data
_d: true, // pure data
})
}
}
})

In .wxml

<view wx:if="{{a}}"> This will display </view>
<view wx:if="{{_b}}"> This will not display </view>

Sharing Styles

The Mini-Program component is defaulted to isolating its own styles.

Component({
options: {
styleIsolation: 'isolated'
}
})

You can also set it to apply-shared which the page’s styles will apply to the component, but the component’s style doesn’t apply to the page.

Or set it to shared if you want all the styles to share both ways.

If you want app.wxss styles to apply to your component use

Component({
options: {
addGlobalClass: true,
}
})

Conversely, if you don’t want app.wxss to apply, use page-isolate , page-apply-shared , or page-shared

If you want to pass styles from parent to component, you can do it with externalClasses

/* custom-component.js */
Component({
externalClasses: ['my-class']
})

In component’s .wxml

<!-- custom-component.wxml -->
<custom-component class="my-class">Text here is styled by external class</custom-component>

In the page where the component is used

<custom-component my-class="red-text" />
<custom-component my-class="large-text" />

Links

Repo for component example:

https://github.com/davidyu37/WeChat-Mini-Program-Component-Example

If you already have WeChat DevTool opened, click here:

https://developers.weixin.qq.com/s/KR93v9mN7seH

Official Doc:

https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/

Call to Action

If you want to level up your WeChat Mini-Program skills, my friend George and I have a program called Advance Mini-Program which we update every month on all the Mini-Program tips and tricks.

--

--

Shanghai Coders
Shanghai Coders

Published in Shanghai Coders

Sharing software development required in China

David Yu
David Yu

Written by David Yu

Full-stack developer based in Shanghai. I help people turning their ideas into reality.

No responses yet