1. Method of parent component calling child component
Idea: When a user clicks on a product, the product details page appears, so it needs to call a sub-component when the parent component clicks on the product to control the display hiding of the product details page.
food component
<template>
<div class="food" v-show="foodtoShow">
</div>
</template>
<script>
export default{
props: {
selcurfood: { // User-Selected Goods
type: Object
}
},
data(){
return {
foodtoShow :false // Define the initial state of the merchandise details page to start hiding
}
},
methods:{
foodinforshow(){ // When the parent component triggers a click event, it calls the foodinforshow method to display the product details page.
this.foodtoShow = true
}
}
}
</script>
Parent component goods component
The introduction and registration are omitted here.
1. Binding events
<li @click='selectFood(food,$event)' v-for="food in item.foods" class="food-item">...
2. Application Components
<food :selcurfood='selectedfoods' ref="food"></food>
selectedfoods represent the item the user is currently clicking on
3. Establish an object that accepts the user's choice of goods
data (){
return {
goods: [],// goods json array
listHeight: [],// The height of each block stored inside the foods
scrollY:0,
selectedfoods:{} // Goods that receive clicks from users
}
},
4. Click Events
selectFood(food,event){
if (!event._constructed) {// Blocking native click events in browsers
return;
}
this.selectedfoods=food// Commodities that are imported into the user's selection exist in the selectedfoods object
this.$refs.food.foodinforshow() //Invoking methods of subcomponents with ref attributes
}
Here it is related to the commodity details page! Continue...
2. Realize scrolling of details page content
1. To achieve scrolling, you need better-scroll, so you need dom bindings to load bscroll asynchronously.
<template>
<transition name="move">
<!-- v-show Control Display Details Page ref Realization dom binding-->
<div class="food" v-show="foodtoShow" ref='foodDetail'>
</div>
</transition>
</template>
import BScroll from 'better-scroll';
export default {
props: {
selcurfood: { // User-Selected Goods
type: Object
}
},
data(){
return {
foodtoShow :false, // Define the initial state of the merchandise details page to start hiding
}
},
methods: {
show(){
this.showFlag = true;
this.$nextTick( () => { // Interface guarantees asynchronous addition of scroll binding after dom rendering
if(!this.scroll){
this.scroll=new BScroll(this.$refs.foodDetail,{
click:true
})
}else{
this.scroll.refresh()
}
})
},
foodtoHide(){ // Click on the return icon to disappear the details of the merchandise
this.foodtoShow = false
},
}
Note: All the content of better-scroll under ref. So you need to have a foodcontent to wrap the content, otherwise better-scroll won't work, attach some code.
<div class="food" v-show="foodtoShow" ref='foodDetail'>
<div class="foodcontent">
<div class="image-header">
<img :src="selcurfood.image" alt="" />
<div class="back" @click="foodtoHide">
<i class="icon-arrow_lift"></i>
</div>
</div>
<div class="content">
<h1 class="title">{{selcurfood.name}}</h1>
<div class="detail">
<span class="sell-count">Monthly sale{{selcurfood.sellCount}}</span>
<span class="rating">Favorable rate{{selcurfood.rating}}</span>
</div>
<div class="price">
<span class="newPrice">¥{{selcurfood.price}}</span>
<span class="oldPrice"v-show='selcurfood.oldPrice'>¥{{selcurfood.oldPrice}}</span>
</div>
<!--Introduce cartcontrol assembly,And use one div Wrap him up.-->
<div class="cartcontrol-wrapper">
<cart-control :foodsele='selcurfood' @add="addFood"></cart-control>
</div>
<transition name='fade'>
<!--Use.stop.prevent Prevent bubbles and default events,Avoid penetration-->
<div class="buy" v-show="!selcurfood.count || selcurfood.count===0" @click.stop.prevent="ballshow">
Add to cart
</div>
</transition>
</div>
<split-line v-show="selcurfood.info"></split-line>
<div class="info" v-show="selcurfood.info">
<h1 class="title">Commodity information</h1>
<p class="text">{{selcurfood.info}}</p>
</div>
<split-line ></split-line>
<div class="rating">
<h1 class="title">Commodity evaluation</h1>
<ratingselect
:selectType="selectType"
:onlyContent="onlyContent" :desc="desc"
:ratings="selcurfood.ratings" >
</ratingselect>
</div>
</div>
</div>
3. Shopping cart buttons
Question: Click to join the shopping cart, the ball appears, the position of parabolic animation moves from the top of the screen, not from the point of clicking on the shopping cart.
Analysis: When you click on the "ballshow" button, food.count is added to the data Vue. set (this. selcurfood,'count', 1), so the "display:none" button will be hidden (v-show), but at the same time it will execute this.$emit('add', event.target);, but this is executed asynchronously, and the asynchronously executed method adds is a parabolic ball animation calculation. Where the initial target height is, when the incoming shopping cart button is set to display:none, the initial target height of the animation can not be calculated, and the height of the parent div will be used, thus affecting the animation effect of the parabolic sphere.
Solution: Add an animation when clicking the shopping cart button disappears, so that vue has enough time to transfer data to the asynchronous execution method, so that it will not affect the initial target calculation of the animation of the parabolic ball.
<transition name='fade'>
<! - Use. stop.prevent to prevent bubbles and defau lt events, and avoid penetration - >.
<div class="buy" v-show="!selcurfood.count || selcurfood.count===0" @click.stop.prevent="ballshow">
Add to cart
</div>
</transition>
Click on the ball to appear
<! - Introduce the cartcontrol component and wrap it with a div - > and
<div class="cartcontrol-wrapper">
<cart-control :foodsele='selcurfood' @add="addFood"></cart-control>
</div>
ballshow(event){ // Click the Add Shopping Cart button, pass in the event, the ball appears
if (!event._constructed) {// pc clicks because the shopping cart button is in bscroll, so you need to deal with the event type of bscroll
return;
}
// Parabolic Ball Animation
this.$emit('add', event.target); //Trigger the event add on the current instance food (the add method bound to the food component on the goods component)
Vue.set(this.selcurfood, 'count', 1);
},
addFood(target) { //The addFood method associated with add
this.$emit('add', event.target); // Trigger the event add on the current instance food (the add method bound to the food component on the goods component)
}
Reference: The event add that triggers the current instance twice is because both operations are the same action. This action is the add method bound to the food component, and the food component will be directed in the goods component < food: selcurfood ='selectedfoods'@add = "addFood" ref = "food"> </food>. In the goods component, the addFood method will point to the method _drop of the current goods component, and then use s. Hopcart's spherical parabola animation is this.$refs.shopcart.drop(target);, which implements the effect of calling methods across components.
IV. Commodity Evaluation
(1) Selection of evaluation types
<ratingselect
:selectType="usrseleType"
:onlyContent="isonlyContent"
:curdesc="foodDesc"
:ratings="selcurfood.ratings"
@usrselect='usrseleRating'
@toggleSwitch='toggleContent' >
</ratingselect>
data(){
return {
foodtoShow :false, // Define the initial state of the merchandise details page to start hiding
usrseleType: ALL, // Default type
isonlyContent: true, // Whether to only look at the content of the evaluation by default do not look
foodDesc: { // Type object
all: 'whole',
positive: 'Recommend',
negative: 'Make complaints'
}
}
}
usrseleRating (type){ // Events passed from subcomponents
this.usrseleType = type
this.$nextTick(() => { // dom refreshes asynchronously every time a type is changed
this.scroll.refresh();
});
},
toggleContent(){ // Evaluation of whether there is content in switching display
this.isonlyContent=!this.isonlyContent
this.$nextTick(() => { //bscroll needs to be refreshed when switching
this.scroll.refresh();
});
},
needShow(type,txt) {
if (this.isonlyContent && !txt) { // // Returns false if only content is displayed and there is no content
return false;
}
if (this.usrseleType === ALL) { //Display all types of evaluations
return true;
} else { // Display the corresponding type of evaluation
return type === this.usrseleType;
}
}
usrseleRating and toggleContent use asynchronous $nextTick because vue updates the DOM asynchronously. When vue attributes are changed, the current DOM is not updated immediately (which causes page height to change, but bscroll can't update, affecting the rolling experience), but will be placed in the asynchronous update queue to wait for updates, even if the queue's waiting time is not long, but not immediately. Update dom, so force the queue to refresh with $nextTick
In food.vue component, usrseleRating and toggleContent are used to update the properties of food.vue component, but not in ratingSelect, because Vue restricts the properties of the child component from changing the properties of the parent component, so the method of calling the parent component is changed by using something like this.$emit('select', type);
(2) Evaluation Time Conversion
Filters using vue
<div class="time">{{rating.time | formatDate}}</div>
filters: {
formatDate(time) {
let date = new Date(time);
//Call formatDate function of curTime module to parse time
return formatDate(date, 'yyyy-MM-dd hh:mm');
}
}
//Under es6, the import of export function needs to be written as follows
import { formatDate } from '../../common/js/date'; //Import custom date module
formatDate.js is a custom JS component, not a vue component. The directory is located at: src/common/js, which is written to practice modular programming of js.
Write a single function as a module
export Derived Functions
Import functions through import
export function formatDate(date, fmt) { //Derive a function under es6
//Matching one or more y to replace the year in which it is matched (four bits per year, so special processing is required)
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
}
let o = {
'M+': date.getMonth() + 1, //The month of js is calculated from zero, so add 1
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds()
};
//Match and replace the months, days, hours, minutes and seconds (these are two, can be processed together)
for (let k in o) {
if (new RegExp(`(${k})`).test(fmt)) { //Matching to key s such as MM
let str = o[k] + ''; //Then o['MM'] is date. getMonth ()+1
//If the matching time is 1 digit, such as M, then the value of date. getMonth ()+1 is used directly.
//If it's a two-digit number, fill in 0 just ahead, using the padLeftZero function
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str));
}
}
return fmt;
};
//Add two zeros first, and then intercept them according to length (because the longest is the length of two zeros).
function padLeftZero(str) {
return ('00' + str).substr(str.length);
}
That's about the end of the food component.