project from jirengu.com
收集四个组件的value
- 需求是把用户填的东西,包括新增标签、备注、支出或收入下的金额输入信息,收集起来,单击ok键的时候,变成一个大的对象。
1. 收集tags组件的value(新增标签内容的值)
tags组件,怎么知道发生变化了呢?需要增加获取选中tags的东西,监听他的xxx事件,触发xxx事件时,就得到要收集的内容,再把这个内容放在yyy的方法中。
//Money.vue
<template>
<Layout class-prefix="layout">
<NumberPad/>
<Types/>
<Notes/>
<Tags :data-source.sync="tags" @xxx="yyy"/> //监听xxx事件
</Layout>
</template>
<script lang="ts">
import Vue from 'vue';
import NumberPad from '@/components/Money/NumberPad.vue';
import Types from '@/components/Money/Types.vue';
import Notes from '@/components/Money/Notes.vue';
import Tags from '@/components/Money/Tags.vue';
import {Component} from 'vue-property-decorator';
@Component({
components: {Tags, Notes, Types, NumberPad}
})
export default class Money extends Vue {
tags = ['餐饮', '购物', '交通', '娱乐', '医疗'];
yyy() { //获得内容放在yyy方法中
}
};
</script>
- 何时触发
xxx事件呢?是在用户选中tag之后,
//Tags.vue
<script lang="ts">
import Vue from 'vue';
import {Component, Prop} from 'vue-property-decorator';
@Component
export default class Tags extends Vue {
@Prop() readonly dataSource: string[] | undefined;
selectedTags: string[] = [];
toggle(tag: string) {
const index = this.selectedTags.indexOf(tag);
if (index >= 0) {
this.selectedTags.splice(index, 1);
} else {
this.selectedTags.push(tag);
}
this.$emit('xxx',this.selectedTags) //监听xxx事件,就可得到选中标签内容
}
- 怎么把内容放在
yyy方法中呢?
//Money.vue
export default class Money extends Vue {
tags = ['餐饮', '购物', '交通', '娱乐', '医疗'];
yyy(zzz:string) {
}
};
- 这里的
xxx事件命名为@update:value,yyy方法命名为onUpdateTags,zzz对应value,以上代码替换为:
//Money.vue
<Tags :data-source.sync="tags" @update:value="onUpdateTags"/>
...
onUpdateTags(value:string) { //获得内容放在onUpdateTags方法中
}
//Tags.vue
this.$emit('update:selected',this.selectedTags)
2. 收集Notes组件的value(用户输入的值)、Types组件的value(用户选中的type值,也就是支出或收入)、Notes组件的value(用户输入的值)
- 先完成3个组件获取用户输入的
value值的事件绑定,及对应的方法:
//Money.vue
<NumberPad @update:value="onUpdateAmount"/> //触发update:value事件
<Types @update:value="onUpdateType"/> //触发update:value事件
<Notes @update:value="onUpdateNotes"/> //触发update:value事件
...
onUpdateAmount(value:string) { //获得内容放在onUpdateAmount方法中
}
onUpdateType(value:string) { //获得内容放在onUpdateType方法中
}
onUpdateNotes(value:string) { //获得内容放在onUpdateNotes方法中
}
- 再写各组件的触发事件响应:
//Tags.vue
this.$emit('update:value',this.selectedTags)
//Notes.vue
<script lang="ts">
import Vue from 'vue';
import {Component, Watch} from 'vue-property-decorator';
@Component
export default class Notes extends Vue {
value = '';
@Watch('value') //Watch适用于发生变化时触发事件
onValueChange(value: string, oldValue: string) {
this.$emit('update:value', value);
}
}
</script>
//Types.vue
<script lang="ts">
import Vue from 'vue';
import {Component, Prop,Watch} from 'vue-property-decorator';
@Component
export default class Types extends Vue {
type = '-';
@Prop(Number) xxx: number | undefined
selectType(type: string) {
if (type !== '-' && type !== '+') {
throw new Error('type is unknown');
}
this.type = type;
}
@Watch('type')
onTypeChange(value:string){
this.$emit('update:value',value)
}
}
</script>
3. 拿到的四个组件对应的value,并收集到一个对象
- 收集到的value命名为record,并做类型声明,并把对应的方法输出:
//Money.vue
<script lang="ts">
import Vue from 'vue';
import NumberPad from '@/components/Money/NumberPad.vue';
import Types from '@/components/Money/Types.vue';
import Notes from '@/components/Money/Notes.vue';
import Tags from '@/components/Money/Tags.vue';
import {Component} from 'vue-property-decorator';
type Record = { //TS类型声明只写类型,JS类型声明写具体内容
tags: string[]
notes: string
type: string
amount: number
}
@Component(
{
components: {Tags, Notes, Types, NumberPad}
})
export default class Money extends Vue {
tags = ['餐饮', '购物', '交通', '娱乐', '医疗'];
record: Record = {
tags: [], notes: '', type: '-', amount: 0
};
onUpdateAmount(value: string) {
this.record.amount = parseFloat(value);
}
onUpdateType(value: string) {
this.record.type = value;
}
onUpdateNotes(value: string) {
this.record.notes = value;
}
onUpdateTags(value: string[]) {
this.record.tags = value;
}
};
</script>
- 验证代码是否正确,在页面中加一个占位符,并点击对应的按钮,看是否实现了需求:
//Money.vue
<template>
<Layout class-prefix="layout">
{{record}}
<NumberPad @update:value="onUpdateAmount"/>
<Types @update:value="onUpdateType"/>
<Notes @update:value="onUpdateNotes"/>
<Tags :data-source.sync="tags" @update:value="onUpdateTags"/>
</Layout>
</template>
实现的效果:
- 把初始值传给有需要的四个组件,以
Type.vue为例,默认的type为'-'支出,不写在组件自身,而从外部(默认的record初始值)传给Type.vue组件。
//Money.vue
<Types :value="record.value" @update:value="onUpdateType"/>
- 上一行代码可以用修饰符简写,如果想给组件一个初始值,然后在其更新时又拿到最新的值,就用
.sync:
<Types :value.sync="record.value" />
- type支出和收入的类型初始值,由外部传给他,也就不需要在自身组件里,定义这个
type,所以删掉以下代码:
//Types.vue
export default class Types extends Vue {
type = '-'; // 此行删除
@Prop({default: '-'}) readonly value!: string; //重新声明原type
selectType(type: string) {
if (type !== '-' && type !== '+') {
throw new Error('type is unknown');
}
this.type = type; //此行删除,已经通过外部属性传默认值,也就不需要改data
this.$emit('update:value', type); //新增
}
//已经通过外部属性传默认值,直接通过外部改,也就不需要监听变化了
@Watch('type')
onTypeChange(value:string){
this.$emit('update:value',value)
}
4. 使用LocalStorage
- 在用户点击ok时,把数据放到
LocalStorage,绑定一个@submit事件,并监听,提交时触发saveRecord方法,在NumberPad.vue组件中添加ok的saveRecord方法,此步骤为初步实现记一笔账,点击ok提交后,就把这条账目推到数组中,每次都是保存相同的内存地址,将上一次的覆盖掉,而没有接续排列为一个数组:
//NumberPad.vue
ok() {
this.$emit('update:value', this.output);
this.$emit('submit', this.output);
this.output = '0';
}
//Money.vue
export default class Money extends Vue {
tags = ['餐饮', '购物', '交通', '娱乐', '医疗'];
recordList: Record[] = []; //创建一个recordList,类型是Record数组,默认为空数组
record: Record = {
tags: [], notes: '', type: '-', amount: 0
};
saveRecord() {
this.recordList.push(this.record); //触发saveRecord方法时,把this.record推到recordList数组里
}
@Watch('recordlist')
onRecordListChange(){
window.LocalStorage.setItem('recordList',JSON.stringfy(this.recordList));
// JSON.stringfy() 用于从一个对象解析出字符串
}
- 用深拷贝的方法实现接续列出数组的每次
push数据,把record的复制保存到recordlist:
saveRecord(){
const record2 = JSON.parse(JSON.stringify(this.record));
// JSON.parse() 用于从一个字符串中解析出json对象
this.recordList.push(record2);
}
- List初始化时,不能赋值为空数组,要赋值,然后用插入语法在html页面中引入
{{recordList}}:
recordList: Record[] = JSON.parse(window.localStorage.getItem('recordList') || '[]');
- 打时间戳:
//Money.vue
type Record = { //TS类型声明只写类型,JS类型声明写具体内容
tags: string[]
notes: string
type: string
amount: number //TS写数据类型
createdAt: Date //TS写类或构造函数
// createdAt: Date | undefined 可写为 createdAt?: Date
}
saveRecord() {
const record2: Record = JSON.parse(JSON.stringify(this.record)); //写record2类型
record2.createdAt = new Date(); //新增
this.recordList.push(record2);
console.log(this.recordList);
}