Trying to become friends with VUEJS. Got stuck on some simple stuff, couldn't google it. So I'm using MuseUi framework in my App. Well I want to do a simple thing, change the value of the property for each specific element on hover
<mu-paper :zDepth="1" v-on:mouseover=" ???? " class="searchBox">
I need to change:zDepth to 3 for example, on hover of the element. How could I achieve that?
full code snippet
<template>
<div>
<div class="column is-one-quarter">
<mu-paper :zDepth="paperHover" v-on:mouseover="changePaper" class="searchBox">
<mu-text-field fullWidth="true" :hintText="searchHint" v-model="query" class="demo-radio"/><br/>
<mu-radio label="Name" name="group" nativeValue="1" v-model="selected" class="demo-radio"/>
<mu-radio label="Manager" name="group" nativeValue="2" v-model="selected" class="demo-radio"/>
<mu-radio label="Department" name="group" nativeValue="3" v-model="selected" class="demo-radio"/>
<mu-radio label="Stage" name="group" nativeValue="4" v-model="selected" class="demo-radio"/>
<mu-radio label="Deadline" name="group" nativeValue="5" v-model="selected" class="demo-radio"/>
<mu-radio label="Start Date" name="group" nativeValue="6" v-model="selected" class="demo-radio"/>
</mu-paper>
</div>
<div class="column is-one-column">
<mu-float-button icon="add" v-on:click="openModal"/>
</div>
<div class="column">
<draggable v-model="projects" #start="drag=true" #end="drag=false">
<transition-group name="list-complete staggered-fade" tad="ul" :css="false" :before-enter="beforeEnter" :enter="enter" :leave="leave">
<li v-for="(project, index) in projectsComputed" :key="project.name" :data-index="index" class="column is-one-third list-complete-item">
<mu-paper :zDepth="3">
<mu-icon-button icon="delete" class="delete-button"></mu-icon-button>
<article class="media">
<div class="media-content">
<div class="content">
<div class="project-name has-text-centered">
<span>{{project.name}}</span>
</div>
<mu-badge :content="project.stage.name" primary slot="right"/>
<!--<div class="project-status has-text-centered">-->
<!--<span>{{project.stage.name}}</span>-->
<!--</div>-->
<div class="project-desc-list has-text left">
<span>Manager: </span> <span>{{project.manager.name}}</span>
</div>
<div class="project-desc-list has-text left">
<span>Department: </span> <span>{{project.department.name}}</span>
</div>
<div class="project-desc-list has-text left">
<span>Start Date: </span> <span>{{project.started_from}}</span>
</div>
<div class="project-desc-list has-text left">
<span>Dealine: </span> <span>{{project.deadline}}</span>
</div>
<p class="project-description">
{{project.description}}
</p>
</div>
</div>
</article>
</mu-paper>
</li>
</transition-group>
</draggable>
</div>
<div id="modal-ter" :class="[isActive ? activeClass : '', modal]">
<div class="modal-background" v-on:click="openModal"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">Add New Project</p>
<button v-on:click="openModal" class="delete"></button>
</header>
<section class="modal-card-body">
<div class="column is-one-third">
<div class="field">
<label class="label">Project Name</label>
<p class="control">
<input class="input" v-model="newProject.name" type="text" placeholder="Text input">
</p>
</div>
</div>
<div class="column is-one-third">
<div class="field">
<label class="label">Project Description</label>
<p class="control">
<input class="input" v-model="newProject.description" type="text" placeholder="Text input">
</p>
</div>
</div>
<div class="column is-one-third">
<div class="field">
<label class="label">Start Date</label>
<p class="control">
<input class="input" v-model="newProject.started_from" type="text" placeholder="Text input">
</p>
</div>
</div>
<div class="column is-one-third">
<div class="field">
<label class="label">Deadline</label>
<p class="control">
<input class="input" v-model="newProject.deadline" type="text" placeholder="Text input">
</p>
</div>
</div>
<div class="column is-one-third">
<div class="field">
<label class="label">Manager</label>
<p class="control">
<input class="input" v-model="newProject.manager.name" type="text" placeholder="Text input">
</p>
</div>
</div>
<div class="column is-one-third">
<div class="field">
<label class="label">Department</label>
<p class="control">
<input class="input" v-model="newProject.department.name" type="text" placeholder="Text input">
</p>
</div>
</div>
<div class="column is-one-third">
<div class="field">
<label class="label">Stage</label>
<p class="control">
<input class="input" type="text" v-model="newProject.stage.name" placeholder="Text input">
</p>
</div>
</div>
</section>
<footer class="modal-card-foot">
<a v-on:click="createProject" class="button is-success">Save changes</a>
<a class="button">Cancel</a>
</footer>
</div>
</div>
</div>
</template>
<style scoped>
.demo-radio {
min-width: 8em;
}
.searchBox {
padding: 2em;
}
.project-status {
position: absolute;
top: 0.3em;
left: 0.3em;
background-color: #ffdb57;
padding: 0.2em;
padding-left: 0.5em;
padding-right: 0.5em;
border-radius: 0.3em;
}
.project-name {
font-weight: 900;
}
.project-description {
margin-top: 0.5em;
padding-top: 1em;
border-top: 1px solid lightgrey;
}
.project-desc-list span:first-of-type{
font-weight: 900;
}
.box {
position: relative;
}
.project-name {
text-align: center;
}
.delete-button {
background-color: rgba(255, 8, 8, 0.4);
transition: all .25s ease-in;
float: right;
position: absolute;
right: 0.3em;
top: 0.3em;
}
.delete-button:hover, .delete-button:focus{
background-color: rgba(255, 0, 0, 0.68);
}
.column{
display: inline-block;
padding: 1em;
}
.list-complete-item {
transition: all 1s;
}
.list-complete-enter, .list-complete-leave-active {
opacity: 0;
}
</style>
<script >
export default {
data(){
return {
loading: false,
isActive: false,
paperHover: 3,
modal: 'modal',
searchHint: 'Search by ...',
activeClass: 'is-active',
query: "",
selected: "",
projects: [],
newProject: {
name: '',
description: '',
started_from: '',
deadline: '',
manager: {
name: ''
},
department: {
name: ''
},
stage: {
name: ''
}
},
}
},
options: {
segmentShowStroke: false
},
mounted() {
this.getData();
},
computed: {
projectsComputed: function () {
var vm = this;
if(this.selected == '1' || this.selected == ""){
this.searchHint = "Search by Name";
return this.projects.filter(function (project) {
return project.name.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1;
})
}else if(this.selected == '2'){
this.searchHint = "Search by Manager";
return this.projects.filter(function (project) {
return project.manager.name.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1;
})
}else if(this.selected == '3'){
this.searchHint = "Search by Department";
return this.projects.filter(function (project) {
return project.department.name.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1;
})
}else if(this.selected == '4'){
this.searchHint = "Search by Stage";
return this.projects.filter(function (project) {
return project.stage.name.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1;
})
}else if(this.selected == '5'){
this.searchHint = "Search by Deadline";
return this.projects.filter(function (project) {
return project.deadline.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1;
})
}else if(this.selected == '6'){
this.searchHint = "Search by Start Date";
return this.projects.filter(function (project) {
return project.started_from.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1;
})
}
}
},
methods: {
changePaper: function () {
this.paperHover = 6 ;
},
beforeEnter: function (el) {
el.style.opacity = 0
el.style.height = 0
},
enter: function (el, done) {
var delay = el.dataset.index * 150
setTimeout(function () {
Velocity(
el,
{ opacity: 1, height: '1.6em' },
{ complete: done }
)
}, delay)
},
leave: function (el, done) {
var delay = el.dataset.index * 150
setTimeout(function () {
Velocity(
el,
{ opacity: 0, height: 0 },
{ complete: done }
)
}, delay)
},
getData() {
this.loading = true;
axios.get('/project/').then(({data}) => this.projects = data).then(() => this.loading = false)
},
openModal: function(){
if(this.isActive){
this.isActive = false;
}else {
this.isActive = true;
}
},
createProject() {
axios.post('project/store', this.newProject)
}
}
}
</script>
enter code here
The easiest way is probably to wrap the mu-paper component in another component where you can encapsulate the hover behavior.
// MuPaperHover.vue
<template>
<mu-paper :z-depth="zDepth" #mouseenter.native="zDepth = 3" #mouseleave.native="zDepth = 1">
<slot/>
</mu-paper>
</template>
<script>
export default {
data() {
return {
zDepth: 1,
};
},
};
</script>
You can then use mu-paper-hover in place of mu-paper.
I'm not familiar with MuseUI. However, I've written a Vue.js training course. In Vue, the area where your "????" are, should be a JavaScript expression. A typical practice is to call a method on your Vue instance that would then update the value of the property for the specific element. Without more details, it's hard to provide specifics.
The bottom line is, the "????" can be a JavaScript expression.
Related
I am trying to create dynamic columns, whenever I have 2,3,4 columns then it's not a problem because these are cards with same styles. I want to be able to have 1 item per row with completely different css than the cards. What's the best way to achieve that?
This is what I've tried so far
<script setup>
import { ref } from 'vue'
const list = ref([
{ title: 'hello', content: 'world'},
{ title: 'hello', content: 'world'},
{ title: 'hello', content: 'world'},
{ title: 'hello', content: 'world'},
{ title: 'hello', content: 'world'},
])
const itemsPerRow = ref('25%')
function toggle(e) {
itemsPerRow.value = `${e.target.value}%`
}
</script>
<template>
<select #change="toggle($event)">
<option value="25">4</option>
<option value="33">3</option>
<option value="50">2</option>
<option value="100">1</option>
</select>
<div class="container">
<div class="items">
<div v-for="item in list" class="item">
{{ item.title }}
</div>
</div>
</div>
</template>
<style>
.items {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
}
.item {
flex-basis: v-bind(itemsPerRow);
border: 1px solid black;
box-sizing: border-box;
}
</style>
Working Example
This is just one of many ways you could conditionally render the data based on the number of columns.
Per Vue documentation, you could place the v-for on a template element. And inside use v-if and v-else to render different markup. In this example we check if itemsPerRow == '100%', which indicates that one column is being displayed, and apply different Bootstrap classes.
<template v-for="item in list">
<div v-if="itemsPerRow == '100%'" class="item card mb-2">
<img class="card-image-top" src="https://loremflickr.com/320/50/dog">
<div class="card-body">
<h5 class="card-title">{{item.title}}</h5>
{{item.content}}
</div>
</div>
<div v-else class="item alert alert-primary">
{{ item.title }}
</div>
</template>
Snippet
The snippet differs from the original code since it's an app rather than a component. Selecting 1 column from the dropdown will display a different layout from multiple columns.
const {
createApp
} = Vue
createApp({
methods: {},
watch: {
itemsPerRow: function(value) {
document.querySelector(":root").style
.setProperty("--itemsPerRow", value);
}
},
data() {
return {
itemsPerRow: "25%",
list: []
}
},
mounted() {
for (let i = 1; i <= 100; i++) {
this.list.push({
title: "Title " + i,
content: "The quick brown fox jumped over the lazy dog"
});
}
}
}).mount('#app')
:root {
--itemPerRow: 25%;
}
.container {
margin: 2rem;
}
.items {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
}
.item {
flex-basis: var(--itemsPerRow);
border: 1px solid black;
box-sizing: border-box;
padding: 0.25rem;
}
<div id="app">
<div class="container">
<select v-model="itemsPerRow" class="form-control mb-2">
<option value="25%">4 columns</option>
<option value="33%">3 columns</option>
<option value="50%">2 columns</option>
<option value="100%">1 column</option>
</select>
<div class="items">
<template v-for="item in list">
<div v-if="itemsPerRow == '100%'" class="item card mb-2">
<img class="card-image-top" src="https://loremflickr.com/320/50/dog">
<div class="card-body">
<h5 class="card-title">{{item.title}}</h5>
{{item.content}}
</div>
</div>
<div v-else class="item alert alert-primary">
{{ item.title }}
</div>
</template>
</div>
</div>
</div>
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.2/dist/css/bootstrap.min.css" rel="stylesheet">
This is the logic algorithm I'm converting and it's not working.
Popup.vue
<script>
import '../../assets/style.css'
export default {
data(){
return{
ceos: [{name_ceo:"Mr. A",
position_ceo:"CEO-GOSTREAM",
content:"Lorem."},
{name_ceo:"Mr. B",
position_ceo:"CTO-GOSTREAM",
content:"Lorem2."},
{name_ceo:"Mr. C",
position_ceo:"CGO-GOSTREAM",
content:"Lorem3."},
]
}
},
methods:{
modalCeo(i){
var modal = this.$refs.modal;
modal[0].style.display="flex";
ceos.forEach(element => {
name_ceo1.innerHTML=ceos[i].name_ceo;
position.innerHTML=ceos[i].position_ceo;
content.innerHTML=ceos[i].content;
});
},
closemodal(){
var modal = this.$refs.modal;
modal[0].style.display="none";
}
}
}
</script>
<style>
</style>
My theme:
<template>
<div class="ceo">
<div ref="modal" class="ceo-popup">
<div class="modal-content-ceo">
<div class="btn-close-popup">
<img src="../../assets/close.svg" id="btn-close-modal" type="button" #click="closemodal()">
</div>
<div class="content-main" v-for="(detail, index) in ceos" :key="index">
<h3 id="name"></h3>
<h5 id="position"></h5>
<h6 id="intro-ceo"></h6>
</div>
<img class="buiding-popup" src="../../assets/Group 226.png" alt="">
</div>
</div>
<div class="title">
<h5>Đội ngũ GoStream</h5>
<h2>Co-Founders</h2>
</div>
<div class="avatar-buiding">
<img class="buiding" src="../../assets/building-0122.png" alt="">
<div class="avatar ">
<img src="../../assets/Profile1.png" #click="modalCeo(0)">
<img src="../../assets//Profile2.png" alt="" #click="modalCeo(1)">
<img src="../../assets/Profile3.png" alt="" #click="modalCeo(2)">
</div>
</div>
</div>
</template>
I want when I click it, a pop up will appear showing the values I have given.
Can you guys give me a solution to this problem?
<div class="avatar" v-for="(item, index) in images" :key="index" >
<img :src="item.url" #click="modalCeo(index)">
</div>
Script:
images:[
{url: '../../assets/Profile1.png'},
{url: '../../assets/Profile2.png'},
{url: '../../assets/Profile3.png'},
]
Update on the code I worked on but it still can't render the image.
You can simply achieve it by using Vue Modal component.
Demo :
new Vue({
el: '#app',
data: {
showModal: false,
selectedCeo: [],
ceos: [
{name_ceo:"Mr. A",
position_ceo:"CEO-GOSTREAM",
content:"Lorem."},
{name_ceo:"Mr. B",
position_ceo:"CTO-GOSTREAM",
content:"Lorem2."},
{name_ceo:"Mr. C",
position_ceo:"CGO-GOSTREAM",
content:"Lorem3."}
]
},
methods: {
modalCeo(index) {
this.showModal = true;
this.selectedCeo = this.ceos[index];
}
}
});
.modal-vue .overlay {
position: fixed;
z-index: 9998;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .5);
}
.modal-vue .modal {
position: relative;
width: 300px;
z-index: 9999;
margin: 0 auto;
padding: 20px 30px;
background-color: #fff;
}
.modal-vue .close{
position: absolute;
top: 10px;
right: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<!-- app -->
<div id="app" class="modal-vue">
<!-- button show -->
<button v-for="(item, index) in ceos" :key="index" #click="modalCeo(index)">{{ item.name_ceo }}</button>
<!-- overlay -->
<div class="overlay" v-if="showModal" #click="showModal = false"></div>
<!-- modal -->
<div class="modal" v-if="showModal">
<button class="close" #click="showModal = false">x</button>
<p>Name : {{ selectedCeo.name_ceo }}</p>
<p>Position : {{ selectedCeo.position_ceo }}</p>
<p>Content : {{ selectedCeo.content }}</p>
</div>
</div>
Note : I updated code snippet as per your comment. Instead of the profile pictures I used buttons just for the demo.
I have this app.
I'm tryna make when progress %20 bottom section text number 1 (Your answers are being analyzed)
opacity = 0.4; --> opacity = 1; I mean I guess active class but how to bind with progress bar ı dont know.
Progress percent %40 text number 2
Progress percent %60 text number 3
Progress percent stops %75 (and there will be popup, this section my part)
When pop-up closed Progress bar keep going from 75 to 100
I'm using this plugin for progress circle: https://github.com/setaman/vue-ellipse-progress
<template>
<div>
<section class="AboutSection d-flex flex-column justify-content-center align-items-start text-center pt-5">
<div>
<router-link to="/eighthPage" class="BackBtn"> <img src="https://img.icons8.com/ios-glyphs/30/000000/back.png"/></router-link>
<router-view></router-view>
</div>
<div class="container-fluid logoHeader">
<h1>Vue-VPN</h1>
</div>
<div class="AboutText container-fluid ">
<div class="row justify-content-center align-items-center text-center ">
<div class="col-md-10">
<h2 class="fw-bolder my-5">Thanks! Your plan is being prepared</h2>
<p class="py-3" style="color: #1488CC;">Please wait a moment...</p>
</div>
<div class="col-md-5 d-flex flex-column justify-content-center align-items-center text-center">
<!-- PROGRESS CIRCLE -->
<ve-progress class="progressCircle position-relative" :progress="75" />
<!-- END PROGRESS CIRCLE -->
<!-- TEXT SECTION-->
<ul class="p-0 m-0 mt-3">
<li class="my-3 d-flex justify-content-start progressText">
<i class="fas fa-check me-2 pt-1" style="color: limegreen;"></i>
<p>Your answers are being analyzed. </p>
</li>
<li class="my-3 d-flex justify-content-start progressText">
<i class="fas fa-check me-2 pt-1" style="color: limegreen;"></i>
<p>Account setup in progress. </p>
</li>
<li class="my-3 d-flex justify-content-start progressText">
<i class="fas fa-check me-2 pt-1" style="color: limegreen;"></i>
<p>Server location setup is in progress. </p>
</li>
<li class="my-3 d-flex justify-content-start progressText">
<i class="fas fa-check me-2 pt-1" style="color: limegreen;"></i>
<p>Your account is being created. </p>
</li>
</ul>
<!-- END TEXT SECTION-->
</div>
<h1>IN PROGRESS</h1>
</div>
</div>
</section>
</div>
</template>
<script>
// import percentCounter from '../components/percentCounter.vue'
// import Progress from '../components/progress2.vue'
export default {
el: '#app',
components: {
// percentCounter ,
// Progress
},
methods:{
}
}
</script>
<style scoped>
.progressCircle{
font-size: 1.5rem;
}
.progressCircle::before{
content: '%';
position: absolute;
font-size: 1.5rem;
display: flex;
justify-content: center;
align-items: center;
top: 50%;
left: 35%;
transform: translate(-50%, -50%);
}
.progressText{
opacity: .4;
color: #111;
}
</style>
if you have an idea it could be anything please comment.
Track your percent in a variable. Then compare if the percent passes text line's percent.
Checkout code snippet below.
<template>
<div id="app">
<ol>
<li v-for="(todo, index) in todos" :key="index">
<label :class="{ passed: percent >= todo.percent }">
{{ todo.text }}
</label>
</li>
</ol>
</div>
</template>
<script>
export default {
data() {
return {
todos: [
{ text: "Learn JavaScript", percent: 20 },
{ text: "Learn Vue", percent: 40 },
{ text: "Play around in JSFiddle", percent: 60 },
],
percent: 10,
interval: null,
}
},
methods: {
increasePercent: function() {
this.percent += 10
},
},
created() {
this.interval = setInterval(this.increasePercent, 500)
},
}
</script>
<style lang="scss">
li {
margin: 8px 0;
font-weight: bolder;
label {
opacity: 0.4;
&.passed {
opacity: 1;
text-decoration: line-through;
}
}
}
</style>
JSFiddle
I really hit a wall with this issue. For an unknown reason (to me) Vuelidate doesn't validate the password field. No matter what you enter it is always invalid and I am not sure why. I tried different solutions but I always return that password is invalid. Does someone know where is the issue?
Here is the code that I am using:
<template>
<form
#submit="checkForm"
:action="this.action"
method="post"
novalidate="true"
>
<div class="app-form-wrapper mb-3 mb-md-0 pt-3 pb-1 py-md-0 rounded">
<!-- Email address -->
<div
class="form-group mb-3"
:class="{ 'form-error': $v.user.email.$error }"
>
<label for="email_login">Email address</label>
<input
type="email"
name="email"
class="form-control py-2 h-auto"
id="email_login"
ref="email"
placeholder="Your email"
aria-describedby="email_error"
v-model.trim.lazy="$v.user.email.$model"
autocomplete="off"
autofocus
/>
<div v-if="$v.user.email.$error">
<div
class="error mt-1"
id="email_error"
aria-live="assertive"
v-if="!$v.user.email.required"
>
Email address is required
</div>
</div>
</div>
<!-- Password -->
<div
class="form-group position-relative mb-0 mb-md-3"
:class="{ 'form-error': $v.user.password.$error }"
>
<div class="d-flex justify-content-between">
<label for="password_login">Password</label>
<a :href="this.forgotUrl" class=" mr-3 mr-md-0 text-muted"
><u>Forgot your password?</u></a
>
</div>
<input
:type="passwordFieldType"
name="password"
class="form-control py-2 h-auto"
id="password_login"
ref="password"
placeholder="Password"
aria-describedby="password_error"
v-model.trim.lazy="$v.user.password.$model"
autocomplete="off"
/>
<button
type="button"
id="switchVisibility"
class="position-absolute"
#click="switchVisibility"
></button>
<div v-if="$v.user.password.$error">
<div
class="error mt-1"
id="password_error"
aria-live="assertive"
v-if="$v.user.password.$dirty && !$v.user.password.required"
>
Password is required
</div>
</div>
</div>
</div>
<!-- CSRF -->
<input type="hidden" name="_token" :value="csrf" />
<!-- Submit -->
<button
class="btn btn-primary btn-lg btn-block"
type="submit"
id="loginBtn"
:disabled="submitStatus === 'PENDING'"
>
Login
</button>
<div class="text-center mt-2">
Don't have an account yet? Sign up here!
</div>
<div
class="bottom-validation--ok mt-2 text-center"
v-if="submitStatus === 'OK'"
>
Thanks for logging in!
</div>
<div
class="bottom-validation--error mt-2 text-center"
v-if="submitStatus === 'ERROR'"
>
Please fill the form correctly.
</div>
<div
class="bottom-validation--sending mt-2 text-center"
v-if="submitStatus === 'PENDING'"
>
Sending...
</div>
<div class="bottom-validation--error mt-2 text-center" v-if="errors">
<div v-for="value in errors">
{{ value }}
</div>
</div>
<hr />
<a
:href="googleUrl"
role="button"
id="btn-google"
class="btn btn-social btn-white btn-block border"
>
<svg
version="1.1"
id="google"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 533.5 544.3"
style="enable-background: new 0 0 533.5 544.3"
xml:space="preserve"
class="mr-2"
>
<path
class="st0"
d="M533.5,278.4c0-18.5-1.5-37.1-4.7-55.3H272.1v104.8h147c-6.1,33.8-25.7,63.7-54.4,82.7v68h87.7
C503.9,431.2,533.5,361.2,533.5,278.4L533.5,278.4z"
/>
<path
class="st1"
d="M272.1,544.3c73.4,0,135.3-24.1,180.4-65.7l-87.7-68c-24.4,16.6-55.9,26-92.6,26c-71,0-131.2-47.9-152.8-112.3
H28.9v70.1C75.1,486.3,169.2,544.3,272.1,544.3z"
/>
<path
class="st2"
d="M119.3,324.3c-11.4-33.8-11.4-70.4,0-104.2V150H28.9c-38.6,76.9-38.6,167.5,0,244.4L119.3,324.3z"
/>
<path
class="st3"
d="M272.1,107.7c38.8-0.6,76.3,14,104.4,40.8l77.7-77.7C405,24.6,339.7-0.8,272.1,0C169.2,0,75.1,58,28.9,150
l90.4,70.1C140.8,155.6,201.1,107.7,272.1,107.7z"
/>
</svg>
<span>Log in with Google</span>
</a>
<a
:href="facebookUrl"
role="button"
id="btn-facebook"
class="btn btn-social btn-facebook btn-block shadow-sm border"
>
<svg
id="facebook"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
class="mr-2"
>
<path
class="st0"
d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z"
/>
</svg>
<span>Log in with Facebook</span>
</a>
</form>
</template>
<script>
import Vuelidate from "vuelidate";
import { required } from "vuelidate/lib/validators";
//import './useragent.js';
Vue.use(Vuelidate);
export default {
props: {
action: String,
old: Object,
forgotUrl: String,
errors: Array,
facebookUrl: String,
googleUrl: String
},
data() {
return {
user: {
email: "",
password: ""
},
passwordFieldType: "password",
csrf: document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content"),
submitStatus: null
};
},
validations: {
user: {
email: {
required
},
password: {
required
}
}
},
methods: {
checkForm(e) {
this.$v.user.$touch();
// For accessibility: focusing on the field with an error
if (this.$v.user.$invalid) {
e.preventDefault();
for (let key in Object.keys(this.$v.user)) {
const input = Object.keys(this.$v.user)[key];
if (input.includes("$")) return false;
if (this.$v.user[input].$error) {
this.$refs[input].focus();
break;
}
}
this.submitStatus = "ERROR";
} else {
return true;
}
},
switchVisibility() {
this.passwordFieldType =
this.passwordFieldType === "password" ? "text" : "password";
}
},
created() {
this.user = this.old;
}
};
</script>
<style scoped>
.form-error input {
border-color: #c91547;
}
.error,
.bottom-validation--error {
color: #c91547;
}
#switchVisibility {
bottom: 10px;
right: 10px;
background-color: transparent;
border: none;
opacity: 0.4;
font-size: 14px;
font-weight: 600;
}
#switchVisibility:focus,
#switchVisibility:active,
#switchVisibility:hover {
box-shadow: none;
outline: none;
}
input[type="password"] + button:before {
content: "Show";
}
input[type="text"] + button:before {
content: "Hide";
}
</style>
Try below steps it will help you to fix the issue.
Step 1: You forgot to import email from vuelidate/lib/validators
import { required, email } from 'vuelidate/lib/validators'
Step 2:
validations: {
user: {
email: { required, email },
password: { required }
}
},
Step 3: Do the validation on button click
checkForm(e) {
this.$v.$touch();
if (this.$v.$invalid) {
return // stop here if form is invalid
}
},
Step 4: Display error message in your html template
<div class="form-group mb-3" :class="{ 'form-error': $v.user.email.$error }">
<label for="email_login">Email address</label>
<input
type="email"
name="email"
class="form-control py-2 h-auto"
id="email_login"
ref="email"
placeholder="Your email"
aria-describedby="email_error"
v-model.trim.lazy="$v.user.email.$model"
autocomplete="off"
autofocus />
<div v-if="!$v.user.email.required" class="error mt-1" id="email_error" aria-live="assertive">
Email address is required
</div>
<div v-if="!$v.user.email.email" class="error mt-1" id="email_error" aria-live="assertive">
Invalid Email address
</div>
</div>
You can checkout for Vue,Vuex,Vuelidate,translation on Vue-vuex-vuelidate-i18n-registration-login-todo
All I want to do is apply a conditional class to a list item as it is pushed to my messages array and then shown on screen. However, I do not want the all of the previous list items to have the same styling applied. Every time a new item is added, it checks what class should be applied, depending on whether sender or receiver of message, and then it applies whichever class necessary to all list items, but I only want it to be applied that new item. How can I achieve this?
app.js:
const chatWindow = new Vue({
el: '#chatWindow',
data: {
messages: [],
currentUser: document.getElementById('sender').value,
sender: true
},
methods: {
sendMessage: function () {
this.$http.post('/chat', {message: this.message, sender: this.currentUser}).then((response) => {
console.log('request sent successfully');
}, (response) => {
console.log('request not sent');
});
this.message = '';
}
},
props: ['message']
});
window.Echo.channel('test-chat-channel')
.listen('MessageSent', (event) => {
chatWindow.$data.sender = event.sender == chatWindow.$data.currentUser;
chatWindow.$data.messages.push(event);
});
chat.blade.php (view):
#extends('layouts.app')
#section('content')
<div class="container text-center">
<div class="row">
<div class="col-sm-3 hidden-xs" style="border: 2px solid black; height: 500px;">
<h2 class="text-center no-top">Sidebar</h2>
</div>
<div id="chatWindow" class="col-sm-7">
<h1 class="text-center no-top">Chat</h1>
<div class="chat-window">
<input type="hidden" id="sender" value="{{ Auth::user()->id }}">
<ul id="chat-messages">
<li v-for="message in messages" :class="sender ? 'left' : 'right'">
#{{ message.message }}
<span class="pull-right">#{{ message.timestamp }}</span>
</li>
</ul>
<div class="form-group">
<input title="Send Message" v-model="message" #keyup.enter="sendMessage" class="form-control" placeholder="Send message...">
</div>
</div>
</div>
<div class="col-sm-2 hidden-xs" style="border: 2px solid black; height: 500px;">
<h2 class="text-center no-top">Infobar</h2>
</div>
</div>
</div>
#endsection
#section('scripts')
#endsection