Vue.js 转换从列表中更改选定元素
Posted
技术标签:
【中文标题】Vue.js 转换从列表中更改选定元素【英文标题】:Vue.js transition on changing selected element from a list 【发布时间】:2019-01-20 11:28:39 【问题描述】:我有一个打开“编辑个人资料”屏幕的个人资料列表。该屏幕从左侧滑入。当我选择一个配置文件时,如果已经选择了一个屏幕,我希望它先滑出,更改选定的配置文件数据,然后再滑入。
现在发生的情况是:当我第一次选择一个元素时,屏幕会滑入。当我更改所选元素时,屏幕会停留并且不会滑出和滑入。
这是一个 gif,显示它现在的行为:
我的代码是:
Vue方法:
editProfile: function (index)
// this.editingProfile = false;
this.setProfile(index);
this.editingProfile = true;
html 视图:
<transition name="fade" mode="out-in">
<div v-if="editingProfile" id="edit-profile">
<input placeholder="Profile Name" v-model="synced.profiles[synced.selectedProfile].name">
</div>
</transition>
CSS:
.fade-enter-active, .fade-leave-active
transition: all .2s;
/* transform:translateX(0); */
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */
opacity: 0;
transform:translateX(-100%);
如何在更改配置文件时正确滑出然后重新滑入?
【问题讨论】:
你是否正确切换this.editingProfile
true falsey
如果我将其切换为 false 然后再次为 true,则不会发生任何事情。
我认为您需要一组面板,而不是您调整其内容的单个面板。否则 Vue 将尝试回收该面板并忽略转换。您想要更多带有项目进出动画的列表过渡。
在editProfile
里面,你可以先设置为false,然后在this.$nextTick
里面,在setTimeout(()=>, 200)
里面再设置为true,请查看fiddle。正如@zero298 提到的,组转换应该是滑入/滑出的解决方案。
如果真假切换滑入和滑出是否正常工作,您可以尝试使用单个元素,即 profile1。单击垂直列表元素之一时,您还会调用 editProfile 吗
【参考方案1】:
根本原因是v-if="editingProfile"
在您的代码中显示一个个人资料后始终为真。
一种解决方案是先将其设置为 false,然后在 this.$nextTick
中再次将其设置为 true。但是您必须将this.editingProfile = true
放在一个setTimeout
中,并且延迟时间= 转换时间。否则slide out
效果会被覆盖。
如下演示:
new Vue(
el: '#emit-example-simple',
data()
return
editingProfile: false,
synced :
profiles: ['name':'A', 'name':'B', 'name':'C'],
selectedProfile: 0
,
,
methods:
editProfile: function (index)
this.editingProfile = !this.editingProfile
this.$nextTick(() =>
setTimeout(()=>
this.synced.selectedProfile = index
this.editingProfile = true
, 1200)
)
)
.fade-enter-active, .fade-leave-active
transition: all 1.2s;
/* transform:translateX(0); */
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */
opacity: 0;
transform:translateX(-100%);
border: 1px solid white;
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="emit-example-simple">
<button @click="editProfile(0)">Profile 1</button>
<button @click="editProfile(1)">Profile 2</button>
<button @click="editProfile(2)">Profile 3</button>
<transition name="fade" mode="out-in">
<div v-if="editingProfile" id="edit-profile">
<input style="border: 5px solid red;" placeholder="Profile Name" v-model="synced.profiles[synced.selectedProfile].name">
</div>
</transition>
</div>
或者您可以考虑使用Group transition
,如下简单演示:
new Vue(
el: '#emit-example-simple',
data()
return
editingProfile: false,
profileContainers: [true, false],
synced :
profiles: ['name':'A', 'name':'B', 'name':'C'],
selectedProfile: 0
,
,
methods:
editProfile: function (index)
this.synced.selectedProfile = index
this.profileContainers = this.profileContainers.map((x)=>!x)
)
.list-items-enter-active
transition: all 1.2s;
.list-items-leave-active
transition: all 1.2s;
.list-items-enter, .list-items-leave-to /* .fade-leave-active below version 2.1.8 */
opacity: 0;
transform:translateX(-100%);
border: 1px solid white;
.list-item
display: inline-block;
border: 6px solid red;
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="emit-example-simple">
<button @click="editProfile(0)">Profile 1</button>
<button @click="editProfile(1)">Profile 2</button>
<button @click="editProfile(2)">Profile 3</button>
<transition-group name="list-items" tag="p">
<div v-for="(item, index) in profileContainers" :key="index" v-if="item">
<input style="border: 5px solid red;" placeholder="Profile Name" v-model="synced.profiles[synced.selectedProfile].name">
</div>
</transition-group>
</div>
【讨论】:
我认为如果没有nextTick
使用:key
,您也许可以逃脱。看我的回答。不过答案很好。【参考方案2】:
我认为我的评论不正确。您可以做到这一点的一种方法是利用:key
和v-if
,这样如果您选择了一个面板,您就可以告诉Vue 渲染一个面板,然后以这种方式在面板之间进行转换。那时你不需要transition-group
。
:key
告诉 Vue 一切都变了。如果您不使用它,Vue 会尝试尽可能多地回收。查看文档:Transitioning Between Elements
在具有相同标签名称的元素之间切换时,您 必须通过赋予它们独特性来告诉 Vue 它们是不同的元素
key
属性。否则,Vue 的编译器只会替换 元素的内容以提高效率。即使在技术上 虽然没有必要,始终键入键被认为是一种很好的做法<transition>
组件中的多个项目。
考虑下面的最小示例:
const panels = [
title: "Hello"
,
title: "World"
,
title: "Foo"
,
title: "Bar"
];
const app = new Vue(
el: "#app",
data()
return
panels,
activePanel: null
;
,
computed:
titles()
return this.panels.map(panel => panel.title);
,
methods:
handleTitleClick(idx)
if (this.activePanel === idx)
this.activePanel = null;
return;
this.activePanel = idx;
);
body
margin: 0;
padding: 0;
#app
display: flex;
align-items: stretch;
height: 100vh;
#panel-set
flex: 1 0 70%;
display: flex;
#side-panel
flex: 1 0 30%;
.panel
padding: 1em;
flex: 1 0;
background-color: rgba(0, 0, 0, 0.2);
.slide-fade-enter-active,
.slide-fade-leave-active
transition: transform 500ms ease-in-out;
.slide-fade-enter,
.slide-fade-leave-to
transform: translateX(-100%);
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<div id="app">
<div id="panel-set">
<transition name="slide-fade" mode="out-in">
<div class="panel" v-if="activePanel !== null" :key="activePanel">
<h2>panels[activePanel].title</h2>
<p>Lorem ipsum</p>
</div>
</transition>
</div>
<div id="side-panel">
<ul>
<li v-for="(title, idx) in titles" @click="handleTitleClick(idx)">title</li>
</ul>
</div>
</div>
【讨论】:
哦,你说得对,这里用:key
会更好。
我考虑过改变,但我仍然更喜欢使用 nexttick,我宁愿控制帧而不是让 vue 决定让它变得神奇。此外,nexttick 会产生更少的代码,并且似乎更容易理解正在发生的事情,尽管由于超时而没有那么优雅。也不太热衷于跟踪数据属性的面板。性能似乎差不多。
我一直在寻找这个!非常感谢以上是关于Vue.js 转换从列表中更改选定元素的主要内容,如果未能解决你的问题,请参考以下文章