Creating Small Programs in Sports Circle with Small Programs and Cloud Development

Posted by dotsc on Thu, 05 Sep 2019 06:22:46 +0200

Table Tennis Circle Program

Partnership with friends wrote a small program, wrote a platform to share information and exchange of table tennis - table tennis circle. We used Wechat's cloud development to accomplish data and background functions. Lease servers are removed.

I am mainly responsible for database design and cloud functions to achieve data acquisition and trigger functions and simple two pages.

text

Functional Display

Page analysis

  • Guide page

When the user is not authorized, it will pop up. Click on the fingerprint image below, it will pop up the authorization box. After authorization, if not registered, it will enter the home page after registration.

  • Three modules in tabbar

    The three modules are home page, circle friend and personal module.

  • Three Functions of Home Page

  1. Tongcheng Circle
    You can see the shared hall in the same city circle, and you can share the hall by clicking on the plus sign.
  2. Sign in
    - Check-in rules increase points
  3. Ranking List
    -- Look at the scoreboard.

Page flow is roughly divided into

- Guide page
    - Home page
        - Tongcheng Circle
        - punch in
        - List
    - Circle Friends Page
        - Friends in the same city
        - Message List
    - Personal Page
        - Personal data

data base

Starting from the above functions, my database design idea is like this.
The objects are as follows:

  • personal
  • Arena
  • dialogue

There are only about three objects, but for the sake of the simplicity of data operation, I divide my personal information into two object tables, and separate the dialogue in the message into one table. So the last table is as follows:

  • Personal Basic Information
  • Personal details (table tennis related)
  • Arena
  • dialogue
  • Message message

Type Selection of Object Attributes

Firstly, the database provided by applet is an object-oriented database based on mangoDB, which is different from general relational database such as mysql. The difference between them and my understanding will be summarized.

Information is a kind of reflection of object state.

I think the attributes of database storage can be roughly divided into three categories

  • Basic information data
    - is the basic data that needs to be stored without any processing of data that can be output directly, such as name, etc.
  • Functional data
    It may be necessary to process the data represented by the transition, such as membership level (vip,svip)
  • Marked data
    - is a special tag, such as an open Id, etc.

But a lot of information takes into account the above several kinds, such as: student number (that is, marked, but also basic information)

After confirming the basic attributes of the object, we should consider the relationship between the object, such as people and dialogue, message and dialogue information.
There are one-to-one (1-1), one-to-many (1-n), and many-to-many (m-n).

In relational database, one-to-one relationship can only add an attribute to a record, such as personal information and personal details, and add a unique field of personal representation to personal details.
In a one-to-many relationship, you need to add an attribute to most records or create a separate table to store the relationship.
For example, for individuals and objects, the first is to add an owner object to the object, or to establish a table of affiliation.
Many-to-many relationships can only be achieved through a single relationship table, for example: students and courses, need a separate course selection table to express the relationship.

One-to-many and many-to-many relationships in object-oriented database can be accomplished by an array field in the object, such as students and courses, adding a field of selected courses to store course ID, and adding field of selected students to store student number in the course, which completes many-to-many relationship links.

The object structure is as follows

Personal Basic Information:

openId:{type:String}//openId primary key
name:{type:String}//Name, default to Wechat Name
avatarUrl:{type:String}//Avatar, default Weixin Avatar
context:{type:String,default:"The man was lazy and left nothing behind."}//Personal Profile
//The following items should be pipeline data, but a table should be put in. Here is a simple chart to put in the basic information table.
intergal:{type:Number,default:0}//Points are used to rank and upgrade
level:{type:String,default:"New people"}//Grade
sign:{type:Array,default:[]}//Record the date of check-in
month:{type:Number}//Record the month in which the last card was checked in, and use it to empty the check-in form every month.

Personal details (table tennis related)

openId:{type:String}//openId primary key
years:{type:String}//years of participation in a ball sport
phone:{type:Array}//Telephone
bat:{type:String}//Racket
board:{type:String}//floor
context:{type:String}//Frontal rubber
intergal:{type:Number,default:0}//Reverse rubber

Arena

id:{type:String}//Primary key, automatically generated by database
address:{type:String}//address
arena:{type:String}//Location area, such as the name of the stadium
persons:{type:Array}//Stadium activists need to change the field in the database to circle
city:{type:String}//City where the stadium is located
img:{type:String}//Picture Address of Gymnasium
latitude:{type:Number}//longitude
longitude{type:Number}//latitude
table:{type:Number}//Table Number
time:{type:String}//Opening Hours

dialogue

message:{type:Array,default:[]}//Message content array, storing the id of the message
my_id:{type:String}//Creator openId
other_id:{type:String}//Receiver's openId

Message message

id:{type:String}//Primary key, automatically generated by database
msg:{type:String}//Message
my_id:{type:String}//Creator openId
other_id:{type:String}//Receiver's openId
time:{type:Date}//time stamp

Cloud function reading database and partial front-end implementation

1. Boot Page


When the first login area is shown above, the cloud function is used to get personal information in the database through openId after login, and if not, the registration process is defaulted.
The default nickname is Wechat nickname (which can be changed on a personal page), the avatar is Wechat's Avatar (no change is provided for the time being), and the rest is the default value.
Boot page js

const QQMapWX = require('../../libs/qqmap-wx-jssdk.js');// Connect Tencent Map
const qqmapsdk = new QQMapWX({
  key: 'HMGBZ-U5XCX-TUX4Y-ZPUH3-7RRX5-BZBCW'
});
const app = getApp()
Page({
  data: {
    login: false
  },
  getUserInfo(e) {
    if (e.detail.userInfo && !this.data.login) {
      console.log('Login in')
      let the_first = false;
      // Use openId as the unique identifier instead of the user information function
      wx.cloud.callFunction({
          name: "getPersonInfo",
        })
        .then(res => {
          // To judge whether it is empty or not, empty means the first entry.
          if (res.result.data.length == 0) {
            the_first = true
          } else {
            // It has been registered and retrieved information is put into app.globalData global data. 
            app.globalData.personInfo = res.result.data[0];
            console.log(app.globalData.personInfo);
            wx.cloud.callFunction({
              name: "getpingpang_info",
              success: res => {
                console.log('Successful login')
                // console.log(res.result.data)
                app.globalData.ping_personInfo = res.result.data[0]
                wx.setStorage({
                  key: 'login',
                  data: true
                })
                wx.switchTab({
                  url: '../home/home',
                })
              }
            })
          }
        }).then(() => {
          // Enter the registration process.
          return new Promise((resolve, reject) => {
            if (the_first) {
              // Getting User Information
              wx.getUserInfo({
                lang: "zh_CN",
                success: res => {
                  app.globalData.userInfo = res.userInfo;
                  resolve();
                },
              })
            }
          })
        })
        .then(() => {
          if (the_first) {
            // Nicknames and avatars required for user registration
            const data = {
              name: app.globalData.userInfo.nickName,
              avatarUrl: app.globalData.userInfo.avatarUrl,
            };
            // Display Loading
            wx.showLoading({
              title: 'Authorized login',
            })
            // User registration functions, with the exception of nicknames and avatars, are set to the lowest or empty
            wx.cloud.callFunction({
                name: "pingpang_init",
                data: data
              }).then(res => {
                // The database has been registered.
                console.log("Completion of registration")
              })
              .then(() => {
                // Get user information once registration is completed
                wx.cloud.callFunction({
                  name: "getPersonInfo"
                }).then(res => {
                  app.globalData.personInfo = res.result.data[0];
                  console.log(res.result.data[0])
                  // Hidden Loading
                  app.globalData.ping_personInfo = {
                    openId: app.globalData.personInfo.openId,
                    phone: '***********',
                    years: '0 year',
                    bat: 'Right-handed horizontal racket',
                    board: 'Novice appliances',
                    infront_rubber: 'Novice appliances',
                    behind_rubber: 'Novice appliances'
                  }
                  wx.hideLoading();
                  // Prompt registration to complete
                  // wx.showModal({
                  //   title:'Registration',
                  //   content:'Registration completed',
                  // })
                  wx.setStorage({
                    key: 'login',
                    data: true
                  })
                  wx.switchTab({
                    url: '../home/home',
                  })
                })
              })
          }
        })
    }
  },
  onLoad() {
    wx.getStorage({
      key: 'login',
      success: (res) => {
        if (res.data) {
          this.setData({
            login: true
          })
          app.timeout = setTimeout(() => {
            wx.showLoading({
              title: 'lodaing',
            })
          }, 3000)
          app.neterror = setTimeout(() => {
            wx.hideLoading()
            wx.showModal({
              title: 'A sad reminder',
              content: 'The network is lost...',
              showCancel: false
            })
          }, 20000)
        }
      }
    })
  }
})

The above process can be divided into the following steps.

1. Judging whether there is a cache or not by onLoad lifecycle

First, call wx.getStorage to query the cached login information. If successful, skip the boot page and change the current login status identifier (default false) to true.
setTimeout controls prompt information and timeout detection.

2. Uncached, Enter Login Registration Function

If you get the login status in the cache. Get authorization information getUserInfo first to determine that the obtained user information exists and is logged in.

the_first judges whether it is the first time to enter the registration process (insurance effect).

Call the cloud function getPersonhInfo to get user information. If the result set is not empty, the information is stored in the global state app.globalData. Next, call the cloud function getpingpang_info to get personal details and put them into the global state as well as write the cache information.
Wx.setStorage ({key:'login', data: true}) so that the next test is not needed, and finally wx.switchTab ({url:'. / home / home',}) is used to jump to the home page.

If the previous step is unsuccessful, it is judged to be the first login. After entering the registration process, the user's nickname and avatar are obtained. The cloud function pingpang_init is called to register in the background, and the initial value is put into the global state, then the user jumps to the home page.

Necessary Cloud Function

1. getPersonhInfo

// Cloud function entry file
const cloud = require('wx-server-sdk')

cloud.init()

const db = cloud.database();
const personinfo = db.collection("pingpang_personinfo")
const _ = db.command;
// Cloud function entry function
exports.main = async(event, context) => {
  let {
    openIdarr,
    openId,
    all,
    city
  } = event;
  if (all) {//Access to all information
    return await personinfo.get()
  } else if (city) {//Obtain all user information for the given city
    return await personinfo.where({
      city
    }).get()
  } else if (openIdarr) {//Get all user information for openId in the given array
    console.log(openIdarr)
    return await personinfo.where({
      openId: _.in(openIdarr)
    }).get()
  } else {//Get the user information given to openId or itself
    return await personinfo.where({
      openId: openId || event.userInfo.openId
    }).get()
  }
}

This cloud function is to get user information.
Firstly, we deconstruct the parameters from users to judge the data we need. openIdarr - acquired by openId array, openId - acquired by openId, city - acquired by user's city, all - acquired all users. None of the four above can obtain the current user's information.

Note: event.userInfo.openId can only be obtained when the user program calls the cloud function directly. When the cloud function calls the cloud function, the called cloud function cannot obtain the object attribute of userInfo.

2. getpingpang_info

// Cloud function entry file
const cloud = require('wx-server-sdk')

cloud.init()
const db = cloud.database();
// Cloud function entry function
exports.main = async(event, context) => {
  return await db.collection("pingpang_info").where({
    openId: event.openId || event.userInfo.openId
  }).get()
}

This cloud function simply obtains user details in two ways (by giving openId or defaulting to itself)

3. pingpang_init

// Cloud function entry file
const cloud = require('wx-server-sdk')

cloud.init()

// Cloud function entry function
exports.main = async (event, context) => {
  const fun1 = await cloud.callFunction({
    name: "addPersonInfo",
    data: {
      openId: event.userInfo.openId,
      name: event.name || "No name was obtained",
      avatarUrl: event.avatarUrl,
      city: event.city,//City
      level: "New people",
      intergal: 0,
      context: "",
      activitiew: [],
      circle: []
    }
  })
  const fun2 = await cloud.callFunction({
    name: "addpingpang_info",
    data: {
      openId: event.userInfo.openId,
      phone: '***********',
      years: '0 year',
      bat: 'Right-handed horizontal racket',
      board: 'Novice appliances',
      infront_rubber: 'Novice appliances',
      behind_rubber: 'Novice appliances'
    }
  })
  return { fun1, fun2 }
}

This function is an initialization function, which adds the initial data of new users to the database.

2. Check-in function

Check-in function pages are not written by me, so I can only provide ideas and cloud functions.
Check-in storage is stored as an array in a field sign of personal information. When clicking on check-in, we first determine whether the month of check-in is the same as the month of last check-in (person's month field). If different, we leave the sign data blank and update the month field to the current month, and then store the date of check-in. day,

Cloud function

// Cloud function entry file
const cloud = require('wx-server-sdk')

cloud.init()
const db = cloud.database();
const personInfo = db.collection("pingpang_personinfo")

// Cloud function entry function
exports.main = async(event, context) => {
  let data = personInfo.where({
    openId: event.openId || event.userInfo.openId
  })
  let info = await data.get()//Acquisition first
  let sign = info.data[0].sign || [] //Put in a new array
  //Judge whether the new month has arrived
  if (info.data[0].month != new Date().getMonth()) {
    sign = [];
    var month = new Date().getMonth()
  }
  //Replacement Array
  return await cloud.callFunction({
    name: "setPersonInfo",
    data: {
      personInfo: {
      //event.date is a one-time playback date array for easy write management debugging
      sign: sign.concat(event.date || [new Date().getDate()]),
        month:month
      }
    }
  })
}

3. List

The list is very simple and there are many ways to do it:

1. The first one is to query the owner of the same city and sort them by integral, and output the ranking list by a certain number of users in front of the area.

  • Advantages: No other resources are needed to store, no space is occupied, and no redundant processing is needed to modify the rankings.
  • Disadvantage: Can not support a large number of users, when the number of users increased to a certain number, a single query time will become very slow, query concurrent number will be problematic, because the query is the same table.

So this method can only be applied in the case of fewer users.

2. The second is to create a table with the city ranking as the object. The attributes of the objects stored in the table are roughly as follows.

  • Advantages: It reduces the processing of a large amount of data after query. Single query only needs to process a corresponding amount of data at a time, and does not need to traverse all data.
  • Disadvantage: Additional storage space is needed. If the user openId is stored, the query speed is still slow. If the user object is stored, the query speed only needs to query the time of a single table. When modifying the ranking, the array fields need to be processed separately, which is more troublesome.

This method can be widely used in the case of large but scattered users.

city:{
    type:String
},
//Store rankings, a certain number of user openId, or user objects
list:{
    type:Array,
    default:[]
},
minIntergal:{
    type:Number,
    default:0
}

3. The third is to use each city as a table and store points to reach the ranking target.

  • Advantages: One point is that it solves the problem of processing quantity, and the concurrency problem is also solved. When people in a single city process a table, the concurrency number will decrease.
  • Disadvantage: Large footprint

This approach can be applied when the number of users is very large.

Overall plan: The best way is to store a single table in a large number of users'cities, while the remaining small cities are stored in the remaining tables. The only disadvantage is to judge the trouble of processing. When there are more users in a city, a new table needs to be added to the database. This needs to be done. Manual solution, change the background processing judgment, you can use the strategy mode to solve.

4. Message function

Message function is one of the main functions of this small program. The purpose is to have an initial communication platform for table tennis enthusiasts with the same interests.
To create a message, you need to find the appropriate user in the circle friend (same city), click on the head image, pop up the details, and then click the message button, which will jump to the message dialog page.

There are two kinds of messages, one is that there are messages before, there are messages, the other is the first dialogue, there are no messages before.
First, you can add message information to it just by querying its existence. The second one needs to be created before adding.

The first one has no problem, adding directly to the message array of the dialog object, and the second one needs to create a dialog object.

Specific process: First, inquire all the dialogue objects in the message page, which is the first case, you can jump to add messages directly, the second is click the message button in the details page of the object in the circle friend page, which will first inquire about the realization of the dialogue, if it does not exist, it will jump to the blank dialogue page, otherwise it will jump to the previous message. Object.
This method is not very good.

The shortcomings are as follows:

  • If you click on the message but leave no message, you will create a blank dialogue object. Users may mistakenly touch the button. There will be many blank messages, which is a big defect.
  • Because this is controlled by the front end, there is a certain delay, which is caused by multiple asynchronous operations.

Recommendation: Whether there is dialogue cashing before clicking on the message, read when there is, jump to the blank page if there is no message, and create an object if a message is sent. In this way, the above shortcomings can be solved. But there is still a problem that real-time communication can not be achieved without socket.

Cloud function

  1. Get the conversation object
// Cloud function entry file
const cloud = require('wx-server-sdk')

cloud.init()
const db = cloud.database();
const dialoguedb = db.collection("pingpang_dialogue")
const _ = db.command;
// Cloud function entry function
exports.main = async (event, context) => {
  let {
    my_id,
    other_id
  } = event;
  if (!my_id) my_id = event.userInfo.openId
  // let data1 = await dialoguedb.where({
  //   my_id,
  //   other_id
  // }).get()
  if (!other_id) return await dialoguedb.where({
    my_id
  }).get()
  return await dialoguedb.where({
    my_id,
    other_id
  }).get()

}

  // console.log(data1)
  // console.log('\n',data2)
  // return data2;

The general functions of the cloud function are as follows:
First, the structure passes the parameters my_id (the ID of the current user) and other_id (the ID of the message object). Then it judges whether my_id exists, openId is given to the current user if it does not exist, and finally it judges that if ohter_id does not exist, it queries all the dialogues of the previous user.

  1. Add message content
// Cloud function entry file
const cloud = require('wx-server-sdk')

cloud.init();
const db = cloud.database();
const messagedb = db.collection("pingpang_message");
const dialoguedb = db.collection("pingpang_dialogue");
const _ = db.command;

// Cloud function entry function
exports.main = async(event, context) => {
  let {
    message,
    my_id,
    other_id,
    msg
  } = event;
  console.log(other_id)
  if (!my_id) my_id = event.userInfo.openId;
  // console.log(other_id)
  let message_id = await messagedb.add({
    data: message || {
      my_id,
      other_id,
      msg,
      time: new Date()
    }
  })
  // console.log(message_id)
  const res = await cloud.callFunction({
    name:"getpingpang_dialogue",
    data:{
      my_id:my_id,
      other_id:other_id
    }
  })
  let myTo = res.result.data[0];
  await dialoguedb.doc(myTo._id).update({
    data: {
      message: myTo.message.concat([message_id._id])
    }
  })

  const res1 = await cloud.callFunction({
    name: "getpingpang_dialogue",
    data: {
      my_id: other_id,
      other_id: my_id
    }
  })
  let otherTo = res1.result.data[0];
  await dialoguedb.doc(otherTo._id).update({
    data: {
      message: otherTo.message.concat([message_id._id])
    }
  })

}

The functions of the cloud function mentioned above are as follows:
First, structure parameters, message (message object, including the following parameters), my_id, other_id and MSG (message content). Next, determine whether my_id exists or not, and use the openId of the current user if it does not exist. Then a new data message db. add is added to the message table. Finally, the dialogue object is obtained and the message content is added to the message array in the dialogue.

5. Personal module

Personal module has no complicated logic, that is, data rendering page, but the page structure is written by me, you can talk about the page.

Personal Page

There is no more fancy style operation in personal pages, only simple basic css and html, so here's a simple structure.

Presumably what you need to say is click to switch to an input box, and the following selection bar becomes a component

Page (Part)

//Personal Profile
<view class="infocard" bindtap='typeInfo'>
  <input type="text" wx:if="{{changecontext}}" placeholder='' bindblur='setcontext' focus='true' value="{{personInfo.context}}" maxlength='18'></input>
        <view class="context" wx:else bindtap='changecontext'>Personal Signature:{{personInfo.context}}</view>
  </view>
  
  
  //Personal Data Box
  <view class="project collections" bindtap="ToPage" data-name="pingpang_info">
      <image class="image" src="https://636f-coldday-67x7r-1259123272.tcb.qcloud.la/person.svg?sign=73135fcd2247e0a00ca78c131fa0d7d6&t=1559030458" />
      <view class="title">personal data</view>
      <text class='cuIcon-right righticon text-grey'></text>
    </view>

JS (part)

data:{
    changecontext: false
}
changecontext() {
    this.setData({
      changecontext: true
    })
  },
 setcontext(event) {
    this.setData({
      changecontext: false
    })
    if (event.detail.value != "") {
      this.setData({
        "personInfo.context": event.detail.value,
        context: event.detail.value
      })
    }
else {
      this.setData({
        "personInfo.context": "This guy doesn't leave any footprints after playing.",
        context: "This guy doesn't leave any footprints after playing."
      })
    }
  },
ToPage(event) {
    wx.navigateTo({
      url: `../${event.currentTarget.dataset.name}/${event.currentTarget.dataset.name}`,
      fail: () => {
        wx.showModal({
          title: '(ಥ_ಥ)',
          content: 'Coming soon!',
          showCancel: false
        })
      }
    })
  },

The switching between text and input box is controlled by wx:if, so that two blocks of similar size occupy the same place. When clicking on text, the changecontext variable in data source becomes a ture page to re-render. The value of input box is displayed as a personal profile in data source, and the text is hidden when the input box is clicked. When the focus is lost, the value value in the input box is written into the data source, then the change context becomes false, the page is rendered again, and the profile is changed.

There are three schemes for submitting modified data

  1. Direct submission after modification
  2. Submit after the page is hidden or closed
  3. After the page is hidden or closed, it is submitted to determine whether the content has been modified or not.

Both the first and the third can be widely used. The first way is recommended, because most users do not modify these things too frequently, but the page is basically accessed many times every time they log in. Frequency and concurrency are the first.

Personal details

Personal details are ordinary pages. There are no complex cloud functions, only one access, one submission and modification, and neither function is complex.

The racket and age in the details page are picker with widgets and the rest are info-section with custom components

page

<view class="container">
 <view class="cu-form-group">
        <view class="title">years of participation in a ball sport</view>
        <picker bindchange="PickerAgeChange" value="{{indexAge}}" range="{{pickerAge}}">
            <view id='picker' class='picker'>
                {{indexAge?pickerAge[indexAge]:personInfo.years}}<text class='cuIcon-title' style='opacity:0'></text>
            </view>
        </picker>
    </view>
  <section title="Telephone" info="{{personInfo.phone}}" infoname="phone" bind:changend="getinfo" type='number'/>
  <!-- <section title="years of participation in a ball sport" info="{{personInfo.years}}" infoname="years" bind:changend="getinfo" type='number'/> -->
  <!-- <section title="Hold the beat" info="{{personInfo.bat}}" infoname="bat" bind:changend="getinfo" /> -->
  <section title="Use the floor" info="{{personInfo.board}}" infoname="board" bind:changend="getinfo" />
  <section title="Forehand rubber" info="{{personInfo.infront_rubber}}" infoname="infront_rubber" bind:changend="getinfo" />
  <section title="Backhand rubber" info="{{personInfo.behind_rubber}}" infoname="behind_rubber" bind:changend="getinfo" isbottom="true" />
  <view class="cu-form-group">
        <view class="title">Hold the beat</view>
        <picker bindchange="PickerChange" value="{{index}}" range="{{picker}}">
            <view id='picker' class='picker'>
                {{index?picker[index]:personInfo.bat || 'Click Select'}}
            </view>
        </picker>
    </view>
  <button  class='button' bindtap="submit">Click Submit</button>
</view>  

js

const app = getApp();
Page({

  /**
   * Initial data of pages
   */
  data: {
    personInfo: {},
    picker: ['Right-handed horizontal racket', 'Right-hand direct shot', 'Left-handed horizontal racket', 'Left-handed direct shot']
  },
  PickerChange(e) {
    let personInfo = this.data.personInfo;
    this.setData({
      index: e.detail.value
    })
    personInfo.bat = this.data.picker[this.data.index];
    this.setData({personInfo})
  },
  PickerAgeChange(e) {
    let personInfo = this.data.personInfo;
    this.setData({
      indexAge: e.detail.value
    })
    personInfo.years = this.data.pickerAge[this.data.indexAge];
    this.setData({personInfo})
  },
  getinfo() {
    this.setData({
      personInfo: app.globalData.ping_personInfo,
    })
  },
  submit() {
    let personInfo = this.data.personInfo;
    if(personInfo.phone.length != 11){
      wx.showModal({
        title: 'Tips',
        content: 'Invalid telephone number',
        showCancel:false
      })
      personInfo.phone = '';
      this.setData({
        personInfo
      })
      return
    }
      const that = this;
    console.log("Start submitting")
    wx.showLoading({
      title: 'Submission in progress',
    })
    let info = this.data.personInfo
    wx.cloud.callFunction({
      name: "setpingpang_info",
      data: info
    }).then(res => {
      wx.hideLoading();
      wx.showToast({
        title: "Successful submission",
        duration: 1000,
      })
      console.log(res, "Successful revision")
      wx.navigateBack({

      })
    })
  },
  /**
   * Life Cycle Function -- Listening for Page Loading
   */
  onLoad: function (options) {
    this.setData({
      personInfo: app.globalData.ping_personInfo
    })
    let pickerAge = []
    for (let i = 0; i < 51; i++) {
      pickerAge.push(i + 'year')
    }
    this.setData({ pickerAge })
  }
})
Custom component info-section

page

<view class="cu-form-group">
  <view class="title">{{title}}</view>
  <input type='{{type}}' wx:if="{{changeinfo}}" bindblur='changend' value='{{info}}' placeholder="Please enter information" focus='true'></input>
  <view class='info' wx:else>
    <input value='{{info?info:"Novice appliances"}}' disabled='true'></input>
  </view>
  <view class='icon-con' bindtap='changeinfo'>
    <image src="https://636f-coldday-67x7r-1259123272.tcb.qcloud.la/change-1.png?sign=c8936111328dcb2ee416201369716380&t=1559030699" class='icon'></image>
  </view>
</view>

<font size= 3>js</font>

// components/info-section/section.js
Component({
  /**
   * List of properties of components
   */
  properties: {
    title: {
      type: String,
      value: "Property name"
    },
    info: {
      type: String,
      value: "Attribute value"
    },
    infoname: {
      type: String,
      value: ""
    },
    isbottom: {
      type: Boolean,
      value: false
    },
    type:{
      type:String,
      value:'text'
    }
  },

  /**
   * Initial data of components
   */
  data: {
    changeinfo: false,
  },

  /**
   * Method List of Components
   */
  methods: {
    changeinfo() {
      this.setData({
        changeinfo: true
      })
      this.triggerEvent("changeinfo");
    },
    changend(event) {
      this.setData({
        changeinfo: false
      })
      getApp().globalData.ping_personInfo[this.properties.infoname] = event.detail.value
      //Throw events to facilitate parent component response
      this.triggerEvent("changend")
    }
  }
})

The communication between parent and child components must pay attention to throwing events in the child components and triggering events in the parent components to achieve.

summary

Development summary

The importance of good communication

In the process of developing small programs with friends, we have noticed the following problems. Communication is the most important. In the process of our development, because there is no good communication, the front-end and back-end function development docking is not perfect. Some functions are not well allocated. Some functions can be solved separately with the front-end or back-end. The lack of communication leads to the situation that both sides have done or both sides have not done. Although everyone has their own things, most of the time they are developed separately. But these problems should be done in the code development process, which is a problem I understand.

Personal Thinking

Structure of the program

The structure of the program is roughly divided into three parts: front-end page, back-end server and database. In the MVVM structure of small programs, the front-end plays an important role.

The ratio of front-end and back-end to database is roughly n:1:1, so when the number of users is large, most operations should be processed in the front-end. This is why mvvm is now called the mainstream. Back-end mainly manages the overall data or important pipeline data processing, and needs to provide a large number of APIs for front-end access. Data,
This can greatly relieve the pressure of the database.

Comparison of Relational Database and Object-Oriented Database

Relational database is a traditional database. Now mysql and microsoft sql server are mainly used. Object-oriented database is a new database, now it uses mangoDB and so on.

In relational databases, the most unique and important thing is that the higher the level of the planning paradigm in relational databases, the lower the integrity of the data, and the redundancy will gradually decrease.
A student user may be divided into multiple tables to store relevant information. In relational databases, the relationship between two tables is also the main one, which usually has to be stored in one table.

In object-oriented database, the biggest difference between traditional relational database and object-oriented database is that it is stored as an object, and the attributes of the object are defined by itself. Its attributes can store an object (function, array). This greatly increases the operability, we can store the relationship as an attribute of the object, for example: the relationship between students and courses, the relationship between them is many-to-many. Originally, in the relational data, we need to establish a course selection table to store, now we only need to add a course selection field to the course object. Store the id array of the selected course students, and add a selected course field to the student object, the relationship between the two will be linked. In object-oriented database, the attributes of objects can usually be clustered together. An object class is a table. This will cause the concurrency problem of having a large amount of data in each table. Therefore, it is better for each object class to divide the attributes, make the data access more average, and reduce each object table at the same time. Number of visits.

What's your feeling?

There are many problems when writing small programs with others. Sometimes the efficiency is not as high as that of writing alone. But I find that writing with others is more motivating. Everyone's ideas are colliding, which can improve their programming level and communication ability with others quickly.

Source address

https://github.com/TencentClo...

If you want to share your technical stories / experiences in developing CloudBase using the cloud, please leave a message to contact us.

Topics: node.js Database SDK Attribute MySQL