今天来看看一个常见问题。
{
first_name: 'chris',
last_name: 'wang',
email: '[email protected]',
camp: {
name: 'web camp',
member_count: 10
},
skills: ['javascript', 'html', 'css']
}
先依昨天讲的 UserForm 的 component 可以这样写。
<form @submit.prevent="$emit('submit')">
<label>firstName<br/>
<input type="text"
:disabled="!$listeners['update:firstName']"
:value="data.firstName"
@input="$emit('update:firstName', {
...data,
firstName: $event.target.value
})">
</label><br/>
<label>lastName<br/>
<input type="text"
:disabled="!$listeners['update:lastName']"
:value="data.lastName"
@input="$emit('update:lastName', {
...data,
lastName: $event.target.value
})">
</label><br/>
<label>email<br/>
<input type="email"
:disabled="!$listeners['update:email']"
:value="data.email"
@input="$emit('update:email', {
...data,
email: $event.target.value
})">
</label><br/>
<CampForm
:data="data.camp"
@update:name="$emit('update:camp', {
...data,
camp: $event
})"
@update:member_count="$emit('update:camp', {
...data,
camp: $event
})"
></CampForm>
<pre>skills: {{data.skills}}</pre>
<input type="submit" value="送出">
</form>
画面
还有一个 skills
怎麽办呢?
{
// ....
skills: ['javascript', 'html', 'css']
}
除了使用 radio 这样有 options 的选择元件之外,如果是 tags 怎办呢?
其实,「就是 Array 的操作要怎麽做在 component 上面」。
以这个概念回溯回去。
也因为需求不同,需要用到的操作自然也不会不同。
今天,我们来试看看 tags 的做法
目标画面
分别就是 CRUD (create, read, update, delete)
<div class="tags">
<div class="tags">
skills: <button @click.prevent="$emit('create', [...data, ''])">+</button><br />
<ul>
<li :key="index" v-for="(tag, index) in data">
<input
:id="`input-tag-${index}`"
type="text"
:value="tag"
@input="$emit('update', [
...data.slice(0, index),
$event.target.value,
...data.slice(index+1),
])"
>
<button @click.prevent="$emit('delete', [
...data.slice(0, index),
...data.slice(index+1),
])">-</button>
</li>
</ul>
</div>
</div>
在 UserForm 这一层,要使用 Tabs 来 CRUD skills
<Tags
:data="data.skills"
@create="$emit('update:skills', {
...data,
skills: $event
})"
@update="$emit('update:skills', {
...data,
skills: $event
})"
@delete="$emit('update:skills', {
...data,
skills: $event
})"
></Tags>
使用 UserForm
加一个 @update:skills="$store.commit('user', $event)"
表示可以更新 skills
<UserForm
:data="data"
@update:first_name="$store.commit('user', $event)"
@update:last_name="$store.commit('user', $event)"
@update:email="$store.commit('user', $event)"
@update:camp="$store.commit('user', $event)"
@update:skills="$store.commit('user', $event)"
@submit="onSubmit"
></UserForm>
...data.slice()
是什麽巫术?对於更新资料,坚持使用 immutable 的方式更新。
并且在触发的位置就决定如何组资料,这是最适合的。
对於 Array 的 immutable ,要靠 Array#slice
取得新的 sub array。
并且将新的值 (''
),更新的值 ($event.target.value
),甚至是删除值都可以做到
下面我们把「组 Array」和「往外传」分成两个步骤写出来。
create
var new_data = [
...data,
''
]
$emit('create', new_data)"
update
var new_data = [
...data.slice(0, index),
new_item, // data[index] 的位置,要修改
...data.slice(index+1),
]
$emit('update', new_data)"
delete
var new_data = [
...data.slice(0, index),
// data[index] 的位置,要删掉
...data.slice(index+1),
]
$emit('delete', new_data)
将型别的的操作,延伸到 component
输入
:value
将值输入 component:data
将物件输入 component要这样分也是可以唷
物件型别,使用了:data
将物件输入 component
阵列型别,使用了:list
将物件输入 component
输出
@input
表示更新这个值,并将值输出 component@update:property
表示物件更新某个属性,并将物件输入 component@create
, @update
, @delete
表示阵列更新某个元素,并将阵列输入 component所以
在 input 的输入中,:value="data.firstName"
输出就是取得 firstName 的新值。
在 camp-form 的输入中 :data="data.camp"
输出就是取得 user.camp 的新值。
在 tags 的输入中 :data="data.skills"
输出就是取得 user.skills 的新值。
物件里的简单型别、物件里的物件型别、物件里的阵列型别,三件事情的更新层次一致之後,按照这样的观念实作,任何巢状式的物件,就真的不用害怕它的表单有多复杂了。
对应方式也许不是一对一,但是可以限缩在一个合理的有限范围之内,像是表单控制项与资料型别之间的关系,也有着一定的合理范围之内。
有了昨天的学习心得。今天做出这样的结果是不是就快速许多了呢?
之後是不是遇到什麽 JSON 都可以顺利的建立出它相对应的表单了呢?
连续三天的 component 的介绍,到今天如果都了解的话,我想说说这一切的开始「舍弃 v-model」这件事,认真说起来我不写 v-model 很久了,让我保持读写分离的写法,也让我可以思考设计 component 的写法有更多的灵活与弹性。
v-model 本身代表的是 Vue.js 的 directives 厉害之处的一种表现,这是不用怀疑的,只是让我发现这样写让我觉得问题可以拆成一致的视角,在 Vue.js 里进行管理复杂度,不会因为资料复杂而让画面处理更复杂,感到非常的开心,为了可以交待这一切的思想源头,特别强调了不要写 v-model 这件事,但并不代表它不好,只是它不处在这些写法演进的脉胳之中。
到目前,希望有让你对 Vue.js 的全新视野与感受,写起来可以尝试更多的可能性。
也希望也可以透过回馈,了解其它的人在 Vue.js 上面的造脂与领悟。
咦?30 天还没到!明天要写什麽?!
前言 昨天分享了远距工作的好处,今天紧接着来看它带来的挑战,以及我们有什麽方式可以去改善它。 远距工...
今天我们接续昨日的话题,继续来聊聊AI发展史上的第二次寒冬。 前面提到,AI在1956年达特茅斯会议...
useCallback 如果父元件所传递的 props 包含 Object, 则在元件因状态改变而 ...
创建多张画布 如果要有遮色效果或者两个图层不想互相被干预可以考虑增加内部画布去让自己操作比较能够好处...
前言 昨天的文章介绍了 Deployment 以及 ReplicaSet 的基本介绍後,接下来要介绍...