问题类型:父子组件传值问题
需求:
1.导航栏右侧,未登录时显示登录注册按钮,已登录状态显示头像
2.点击按钮,弹出dialog进行登录,登录成功dialog自动关闭,且点击空白处dialog也会关闭
报错信息:
原始代码:
为了精简一下,我只留下了跟问题有关的代码
子组件 Login.vue
<template>
<el-dialog :visible.sync="showLogin" title="Login Form" @close="closeLoginDialog" center>
// 登录
<div class="login-container">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on" label-position="left">
// 用户名
<el-form-item prop="username">
...
</el-form-item>
// 密码
<el-form-item prop="password">
...
</el-form-item>
// 登录按钮
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">Login</el-button>
</el-form>
</div>
</el-dialog>
</template>
<script>
import axios from 'axios'
import { Message } from 'element-ui'
export default {
name: 'Login',
props: { // 用户父组件传值
showLogin: {
type: Boolean
}
},
data () {
return {
loginForm: {
username: '',
password: ''
},
loginRules: {
username: [{ required: true, trigger: 'blur', message: '请输入用户名' }],
password: [{ required: true, trigger: 'blur', message: '请输入密码' }]
},
loading: false,
passwordType: 'password',
islogin: false // 登录状态
}
},
methods: {
handleLogin () {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
axios.post('/api/user/login', this.loginForm)
.then((response) => {
window.console.log(response)
if (response.data === 'login success') {
localStorage.token = this.loginForm.username
alert('登录成功')
this.loading = false
this.showLogin = false // 错误:修改了props
this.islogin = true
this.$emit('getIsLogin', this.islogin)
} else {
alert(response.data)
this.loading = false
}
}).catch(() => {
this.loading = false
})
} else {
console.log('error submit!!')
return false
}
})
},
closeLoginDialog () {
this.$emit('closeLoginDialog', false)
}
}
}
</script>
父组件 Navigator.vue
<template>
<div>
<el-menu
:default-active="activeIndex2"
class="el-menu-demo"
mode="horizontal"
@select="handleSelect"
background-color="#545c64"
text-color="#fff"
active-text-color="#ffd04b"
router>
<div class="right-menu" v-if="islogin"> // 头像
...
</div>
<div v-else> // 登录按钮
<el-button type="text" class="right-nav-button" @click="showLoginDialog">登录</el-button>
</div>
</el-menu>
// Login
<Login :showLogin='loginDialogVisible' @getIsLogin="getValueFromLogin" @closeLoginDialog="getIsCloseFromLogin"></Login>
</div>
</template>
<script>
import axios from 'axios'
import Login from './Login'
export default {
name: 'Navigator',
components: {
Login
},
data () {
return {
// navgator
activeIndex: '/index',
activeIndex2: '/index',
islogin: false,
loginDialogVisible: false,
registerDialogVisible: false
}
},
mounted () {
if (localStorage.getItem('token')) {
this.islogin = true
}
},
methods: {
// 导航栏的处理函数
handleSelect (key, keyPath) {
console.log(key, keyPath)
},
// loginDialog是否显示
showLoginDialog () {
this.loginDialogVisible = !this.loginDialogVisible
},
// 登出
logout () {
if (localStorage.getItem('token')) {
axios({
method: 'POST',
url: '/api/user/logout'
})
.then(Response => {
window.console.log(Response)
if (Response.data === 'logout success.') {
alert('成功退出!')
localStorage.removeItem('token')
this.$router.push('/index')
this.islogin = false
} else {
alert('登出失败,请再次尝试!')
}
})
.catch(error => console.log(error))
} else {
alert('尚未登录,请登录!')
this.$router.push('/login')
}
},
// 从子组件获得登录状态
getValueFromLogin (input) {
this.islogin = input
this.loginDialogVisible = false
},
// 从子组件获得dialog打开关闭状态
getIsCloseFromLogin (input) {
this.loginDialogVisible = input
}
}
}
</script>
涉及的知识点:
- VUE中,prop的传递是单向下行绑定的,也就是说只能父传给子,不能反过来
- :visible.sync="showLogin"实现父子同步,所以关闭dialog时,子组件会更改showLogin
- 在VUE中,子组件向父组件通信是通过事件完成的,this.$emit(),有若干个参数,第一个参数是自定义事件的标签,接着是一个或者若干个传给父组件的参数
修改:
- :visible.sync="showLogin"改为:visible=“showLogin”
- 子组件的handleLogin方法中去掉this.showLogin = false
- 为了实现子组件关闭dialog而且不产生冲突,所以我们将子组件的变化告诉父组件,让父组件帮我们实现,也就是说我们操作子组件后通知父组件去关闭窗口。子组件监听dialog的close事件,在colse事件中告诉父组件。
参考资料: