How to hide tooltip boxes in Vue - javascript

My goal is to get tooltip box popping up on each incorrect or empty input field when pressing 'Save' button.
I managed to display tooltip boxes by pressing 'Save' button, but when I input correct text (numbers), the tooltip box doesn't hide, but shows 'false' tooltip. How to hide tooltips completely on correct input?
full code: https://jsfiddle.net/a8bnp6m4/1/
var productForm = Vue.createApp ({})
productForm.component('custom-form', {
props: {
modelValue: {
type: String,
default: ''
},
},
components: ['status-bar', 'tooltips'],
template: `
<button v-on:click="submitProduct">Save</button>
<h1>Product Add</h1>
<div class="lines">
<label>SKU<input type="text" id="sku" v-model="this.product.sku" placeholder="ID"></label>
<tooltips v-if="this.tooltipText.show && showTooltip!=false" :tooltip="this.showTooltip(this.product.sku)" />
<label>Name<input type="text" id="name" v-model="this.product.name" placeholder="Please, provide name"></label>
<tooltips v-if="this.tooltipText.show && showTooltip!=false" :tooltip="this.showTooltip(this.product.name)" />
<label>Price<input type="text" id="price" v-model="this.product.price" placeholder="Please, provide price"></label>
<tooltips v-if="this.tooltipText.show && showTooltip!=false" :tooltip="this.showTooltip(this.product.price)" />
</div>
` ,
data: function() {
return {
product: {
sku: null,
name: null,
price: null,
},
options: ['DVD', 'Book', 'Furniture'],
selected: 'DVD',
tooltipText: {
onSubmit: 'Please, submit required data',
onType: 'Please, provide the data of indicated type',
show: false
}
}
},
computed:{
model:{
get(){ return this.modelValue },
set(v){ this.$emit('update:modelValue',v)}
}
},
methods:{
updateValue: function () {
return this.$emit('sendData')
},
submitProduct: function(){
for(var i in this.product){
this.showTooltip(this.product[i])
if(this.product[i]==null){
this.tooltipText.show = true
//this.product[i]=null
}
}
},
showData: function(){
//console.log(this.product.sku)
return JSON.stringify(this.product)
},
showTooltip: function(v){
if(v == null){ return this.tooltipText.onSubmit }
else if(!Number.isInteger(parseInt(v))){ return this.tooltipText.onType }
else { return false }
},
created() {
this.showData()
}
}
})
productForm.component ('tooltips', {
template: `
<div class="tooltip" :tooltip="tooltip" :showTooltip="showTooltip">
<span class="tooltiptext">{{tooltip}}</span>
</div>
`
})
const vm = productForm.mount('#product_form')

Today in about 10 minutes with clear head I solved my problem by replacing 'v-if' content with this.tooltipText.show && showTooltip(this.product.sku)!=false in my custom tooltip tag. :)
I just forgot to add an argument this.product.sku to showTooltip function.
full code: https://jsfiddle.net/amwfcv2o/
<label>SKU<input type="text" id="sku" v-model="this.product.sku" placeholder="ID"></label>
<tooltips v-if="this.tooltipText.show && showTooltip(this.product.sku)!=false" :tooltip="this.showTooltip(this.product.sku)" />
showTooltip: function(v){
if(v == null) { return this.tooltipText.onSubmit }
else if(!Number.isInteger(parseInt(v))) { return this.tooltipText.onType }
else { return false }
}
}
})

I suspect issue is with this.tooltipText.show && showTooltip!=false".
Can you try changing it to this.tooltipText.show && showTooltip"

Try without this in template and pass field in v-if showTooltip:
<label>SKU<input type="text" id="sku" v-model="product.sku" placeholder="ID"></label>
<tooltips v-if="tooltipText.show && showTooltip(product.sku)" :tooltip="showTooltip(product.sku)" />
...
Pls check following snippet:
var productForm = Vue.createApp ({})
productForm.component('custom-form', {
props: {
modelValue: {
type: String,
default: ''
},
},
components: ['status-bar', 'tooltips'],
template: `
<button v-on:click="submitProduct">Save</button>
<h1>Product Add</h1>
<div class="lines">
<label>SKU<input type="text" id="sku" v-model="product.sku" placeholder="ID"></label>
<tooltips v-if="tooltipText.show && showTooltip(product.sku)" :tooltip="showTooltip(product.sku)" />
<label>Name<input type="text" id="name" v-model="product.name" placeholder="Please, provide name"></label>
<tooltips v-if="tooltipText.show && showTooltip(product.name)" :tooltip="showTooltip(product.name)" />
<label>Price<input type="text" id="price" v-model="product.price" placeholder="Please, provide price"></label>
<tooltips v-if="tooltipText.show && showTooltip(product.price)" :tooltip="showTooltip(product.price)" />
</div>
` ,
data: function() {
return {
product: {
sku: null,
name: null,
price: null,
},
options: ['DVD', 'Book', 'Furniture'],
selected: 'DVD',
tooltipText: {
onSubmit: 'Please, submit required data',
onType: 'Please, provide the data of indicated type',
show: false
}
}
},
computed:{
model:{
get(){ return this.modelValue },
set(v){ this.$emit('update:modelValue',v)}
}
},
methods:{
showSelected: function(){
//return console.log(this.selected)
},
updateValue: function () {
return this.$emit('sendData')
},
submitProduct: function(){
for(var i in this.product){
this.showTooltip(this.product[i])
if(this.product[i]==null){
this.tooltipText.show = true
//this.product[i]=null
}
}
if (this.tooltipText.show == false){
//window.location.href = '../';
}
//console.log(this.product)
//return this.postData(this.product)
},
showData: function(){
//console.log(this.product.sku)
return JSON.stringify(this.product)
},
showTooltip: function(v){
if(v == null){ return this.tooltipText.onSubmit }
else if(!Number.isInteger(parseInt(v))){ return this.tooltipText.onType }
else { return false }
},
created() {
this.showData()
}
}
})
productForm.component ('tooltips', {
props: ['tooltip', 'showTooltip'],
//data: function(){
// return {
// tooltipText: this.tooltipText.onType
// }
//},
template: `
<div class="tooltip" :tooltip="tooltip" :showTooltip="showTooltip">
<span class="tooltiptext">{{tooltip}}</span>
</div>
`
})
const vm = productForm.mount('#product_form')
<!DOCTYPE html>
<html>
<head>
<title>scandiweb task</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
<link href="../styles.css" rel="stylesheet" type="text/css" media="all">
<!-- <script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script> -->
<script src="https://unpkg.com/vue#next"></script>
</head>
<body>
<style>
div input {
display:inline-block;
justify-content:space-between;
align-items:center;
border:3px solid black;
margin:10px;
padding:10px;
}
div label{
display:block;
}
.tooltip {
position: relative;
display: inline;
border-bottom: 1px dotted black;
}
.tooltip .tooltiptext {
position: absolute;
width: 400px;
background-color: black;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px;
margin-left:300px;
z-index: 1;
visibility: visible;
}
</style>
<div id="product_form" v-cloak>
<custom-form>
</custom-form>
</div>
</body>
</html>

Related

In Vue.js, How this fix, after splicing an array, following its class

First of all, I'm sorry to write in English not well.
I'm looking foward to find the answer to fix this problems.
I'm making a todolist, it had a problem that the class ('centerLine') keeps following next element
after deleting an array to use splice.
Please someone know, let me know how to fix it.
Thank you
https://github.com/seongjin2427/Public
* checked the check box
*after pushing x-box to get rid of checked todo
You can send id to method
#click="deleteTask(todo.id)"
and then filter array
deleteTask(id) {
this.todos = this.todos.filter(t => t.id !== id)
}
let app = new Vue({
el: '#app',
data: {
todos: [{
id: 1,
text: '밥 먹기',
checked: false
},
{
id: 2,
text: '잘 자기',
checked: false
},
{
id: 3,
text: '유튜브 보기',
checked: false
}
],
input_text: ""
},
methods: {
addTodo() {
// 배열 길이 변수 저장
let arrayLength = this.todos[this.todos.length-1].id;
// Add 버튼 눌렀을 때, input_text값 그대로 배열에 push 하기
if (this.input_text != "") {
this.todos.push({
id: arrayLength + 1,
text: this.input_text
});
}
// push후 input 값 초기화
this.input_text = "";
},
change1(e) {
// 할 일 클릭 후 input 창으로 변경
let index = e.target.id.substr(3, 3);
document.querySelector('#vsb' + index).classList.toggle('none');
document.querySelector('#invsb' + index).classList.toggle('none');
},
change2(e) {
// input 창에서 마우스가 out되면 실행할 것
let index = e.target.id.substr(5, 3);
document.querySelector('#vsb' + index).classList.toggle('none');
document.querySelector('#invsb' + index).classList.toggle('none');
},
deleteTask(id) {
this.todos = this.todos.filter(t => t.id !== id)
}
}
});
#app li {
list-style: none;
padding:0;
margin: 0;
}
span.centerLine {
text-decoration: line-through;
color: gray;
}
.x-box {
border-radius: 30%;
opacity: 0%;
}
.x-box:hover {
opacity: 100%;
transition: all 1s;
}
.none {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<body>
<div id="app">
<h1>To Do List</h1>
<hr>
<input type="text" v-model="input_text" #keyup.enter="addTodo">
<input type="button" value="Add" #click="addTodo">
<br>
<div class="todo-box">
<ul>
<li v-for="(todo, idx) in todos" :key="idx">
<input :id="'chk'+(idx+1)" type="checkbox" v-model="todo.checked">
<span :id="'vsb'+(idx+1)" #click="change1" :class="{'centerLine': todo.checked}">{{ todo.text }}</span>
<input :id="'invsb'+(idx+1)" #mouseout="change2" class="none" type="text" v-model="todo.text">
<input :id="'xbox'+(idx+1)" class="x-box" #click="deleteTask(todo.id)" type="button" value="x">
</li>
</ul>
</div>
</div>
</body>
you can send the task in the method
#click="deleteTask(task)"
then splice it from array
deleteTask(task) {
this.todos.splice(this.todos.indexOf(task),1)
}

Form is listening to enter key Vue

I have made a form component (CreateDocument) in Nuxt. Inside this component i made also an autocomplete (AutoCompleteFilters).
When I hit enter inside the autocomplete component, also the CreateDocument is listening to the enter key. But I only want that a specific input field is listing to the enter key event.
This is the CreateDocument component:
<template>
<div>
<Notification :message="notification" v-if="notification"/>
<form method="post" #submit.prevent="createDocument">
<div class="create__document-new-document">
<div class="create__document-new-document-title">
<label>Titel</label>
<input
type="text"
class="input"
name="title"
v-model="title"
required
>
</div>
<div class="create__document-new-document-textarea">
<editor
apiKey="nothing"
v-model="text"
initialValue=""
:init="{
height: 750,
width: 1400
}"
>
</editor>
</div>
<div class="create__document-new-document-extra-info">
<div class="create__document-new-document-tags">
<label>Tags</label>
<AutoCompleteFilters/>
</div>
<div class="create__document-new-document-clients">
<label>Klant</label>
<input
type="text"
class="input"
name="client"
v-model="client"
required
>
</div>
</div>
<Button buttonText="save" />
</div>
</form>
</div>
</template>
<script>
import Notification from '~/components/Notification'
import Editor from '#tinymce/tinymce-vue'
import Button from "../Button";
import { mapGetters, mapActions } from 'vuex'
import AutoCompleteFilters from "./filters/AutoCompleteFilters";
export default {
computed: {
...mapGetters({
loggedInUser: 'loggedInUser',
})
},
middleware: 'auth',
components: {
Notification,
Button,
editor: Editor,
AutoCompleteFilters
},
data() {
return {
title: '',
text: '',
tags: '',
client: '',
notification: null,
}
},
methods: {
...mapActions({
create: 'document/create'
}),
createDocument () {
const documentData = {
title: this.title,
text: this.text,
tags: this.tags,
client: this.client,
userId: this.loggedInUser.userId
};
this.create(documentData).then((response) => {
this.notification = response;
this.title = '';
this.text = '';
this.tags = '';
this.client= '';
})
}
}
}
</script>
And this is the AutoCompleteFilters component:
<template>
<div class="autocomplete">
<input
type="text"
id="my-input"
#input="onChange"
v-model="search"
#keydown.down="onArrowDown"
#keydown.up="onArrowUp"
#keydown.enter="onEnter"
/>
<ul
v-show="isOpen"
class="autocomplete-results"
>
<li
v-for="result in results"
:key="results.id"
class="autocomplete-result"
#click="setResult(result.name)"
:class="{ 'is-active': results.indexOf(result) === arrowCounter }"
>
{{ result.name }}
</li>
</ul>
</div>
</template>
<script>
import {mapActions} from 'vuex'
export default {
data() {
return {
isOpen: false,
results: false,
search: '',
arrowCounter: 0,
filter: null,
position: 0
};
},
methods: {
...mapActions({
getFilterByCharacter: 'tags/getTagsFromDb'
}),
onChange(e) {
this.isOpen = true;
this.position = e.target.selectionStart;
},
setResult(result) {
this.search = result;
this.isOpen = false;
},
getResults(){
this.getTagsByValue(this.search).then((response) => {
this.results = response;
});
},
async getTagsByValue(value){
const filters = {autocompleteCharacter : value};
return await this.getFilterByCharacter(filters);
},
onArrowDown() {
if (this.arrowCounter < this.results.length) {
this.arrowCounter = this.arrowCounter + 1;
}
},
onArrowUp() {
if (this.arrowCounter > 0) {
this.arrowCounter = this.arrowCounter - 1;
}
},
onEnter(evt) {
this.search = this.results[this.arrowCounter].name;
this.isOpen = false;
this.arrowCounter = -1;
}
},
watch: {
search: function() {
this.getResults();
}
},
};
</script>
<style>
.autocomplete {
position: relative;
}
.autocomplete-results {
padding: 0;
margin: 0;
border: 1px solid #eeeeee;
height: 120px;
overflow: auto;
width: 100%;
}
.autocomplete-result {
list-style: none;
text-align: left;
padding: 4px 2px;
cursor: pointer;
}
.autocomplete-result.is-active,
.autocomplete-result:hover {
background-color: #4AAE9B;
color: white;
}
</style>
Just as you did in your form to avoid "natural" form submit and replace it with a custom action:
#submit.prevent="createDocument"
... you have to preventDefault the "natural" event that submits the form when you press Enter while focusing the form.
To do so, just add .prevent to your events in the template:
#keydown.down.prevent="onArrowDown"
#keydown.up.prevent="onArrowUp"
#keydown.enter.prevent="onEnter"

Style Binding makes app no show up anymore in Vue

What I'm trying to do is to make small 20px x 20px boxes of different colors in Vue. But, whenever I try to do :style='{ background-color: color.text }' (color is an object in the data with property text), it just breaks the app and it shows up with nothing.
Sceenshot without :style='{ background-color: color.text }'
Screenshot of inspector with :style='{ background-color: color.text }'
(No divs with id=app!)
Code:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue Learning</title>
</head>
<body>
<style>
[v-cloak] {
display: none;
}
.color-box {
height: 20px;
width: 20px;
}
</style>
<div v-cloak id='app'>
<div id='product'>
<h1>{{ product }}</h1>
<div class='product-image'>
<img :src='image'>
</div>
<p v-if='inventory >= 50'>In stock</p>
<p v-else-if='10 <= inventory && inventory < 50'>Almost in stock</p>
<p v-else-if='inventory < 10'>Almost out of stock</p>
<p v-else-if='inventory == 0'>Out of stock</p>
<h3>Comes in the colors:</h3>
<div v-for='color in colors'
:key='color.id'
#mouseover='changeColor(color.image)' class='color-box'
:style='{ background-color: color.text }'>
</div>
<button #click='addToCart'>Add to cart</button>
<button #click='removeFromCart'>Remove item from cart</button>
<p># of items in cart: {{ cart }}</p>
</div>
</div>
<script src='https://cdn.jsdelivr.net/npm/vue'></script>
<script src='main.js'></script>
</body>
</html>
main.js
let app = new Vue({
el: '#app',
data: {
product: 'Socks',
image: '',
inventory: 9,
colors: idify([
{
text: "green",
image: 'socks.jpg'
},
{
text: "blue",
image: 'blue-socks.jpg'
}
]),
cart: 0,
},
methods: {
addToCart() { // ES6 shorthand for "addToCart: function() {"
this.cart += 1
},
removeFromCart() {
if (!this.cart) {
return;
}
this.cart -= 1
},
changeColor(image) {
this.image = image
},
}
})
app.image = app.colors[0].image
function idify(array) {
let idcount = 0
let array2 = []
for (let value of array) {
let obj = { id: idcount, ...value }
array2.push(obj);
idcount++;
}
return array2
}
function toTitleCase(str) {
return str.replace(
/\w\S*/g,
function(txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
}
);
}
background-color is not a valid property name in the object literal syntax because of the -.
You can fix it in these ways:
:style="{ backgroundColor: color.text }" (Vue-specific)
:style="{ 'background-color': color.text }"

How to pass v-model value between components

I have a parent form component and a child. How do I pass data from one to another using v-model? They are in different files. I'm using the components of the Quasar Framework.
Parent Component
<template>
<q-input
label="Nome *"
lazy-rules
:rules="[val => (val && val.length > 0) || 'Por favor, digite o seu nome']"
v-model="nome"
/>
</template>
<script>
export default {
name: "Nome",
data() {
return {
nome: "Max"
};
}
};
</script>
Child Component
<template>
<div class="q-pa-md" style="max-width: 500px">
<q-form #reset="onReset" class="q-gutter-md">
<Nome> </Nome>
<div>
<q-btn label="Reset" type="reset" color="red" flat class="q-ml-sm" />
</div>
</q-form>
</div>
</template>
<script>
import Nome from "components/Nome.vue";
export default {
components: { Nome },
onReset() {
this.name = null;
}
};
</script>
How do I onReset() work?
Automatically translated.
I think you have some confusion about your child component and parent component. On your code Nome is the child component and the form that using Nome is the parent component.
You can use ref to call the reset method on Nome from the parent form component.
Here is a Working example -
Vue.component("nome-input", {
data(){
return {
input: ""
}
},
template: `
<input #input="onInput" type="text" v-model="input">
`,
methods: {
reset(){
this.input = ""
},
onInput(){
this.$emit('onInput', this.input);
}
}
});
Vue.component("user-form", {
data(){
return {
name: '',
}
},
components: {
},
template: `
<div>
{{name}}
<nome-input ref="nome" #onInput="updateName"></nome-input>
<button #click.prevent="save">Save</button>
<button #click.prevent="reset">reset</button>
</div>
`,
methods: {
save(){
console.log(this.name);
},
reset(){
this.name = "";
this.$refs.nome.reset();
},
updateName(value){
this.name = value;
}
}
});
new Vue({
el: "#app",
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
</head>
<body>
<div id="app">
<user-form></user-form>
</div>
</body>
</html>
Here is a jsfiddle link for the above codes https://jsfiddle.net/azs06/u4x9jw62/34/

Changing color in angular todo list

hi this my first time here so be patience with me plz i stock in a problem
that i need to change a color in a span class this is my code
the color need to change in a click event when i click the checkbox from somecolor to grey
also if someone want to help i try to do select with selected option but in angular
tnx for the helper.
function TodoCtrl($scope) {
$scope.priority = ['Urgent', 'Critical', 'Normal', 'IfYouCan']
$scope.todos = [{
text: 'Attend Selection Day',
done: false,
lvl: 'Critical'
}, {
text: 'Register to Full Stack Web Course',
done: false,
lvl: 'Normal'
}, {
text: 'Go see X-Man apocalypse movie',
done: false,
lvl: 'IfYouCan'
}];
$scope.addTodo = function() {
$scope.todos.push({
text: $scope.todoText,
done: false,
lvl: $scope.todoLvl
});
$scope.todoText = '';
};
$scope.remaining = function() {
var count = 0;
angular.forEach($scope.todos, function(todo) {
count += todo.done ? 0 : 1;
});
return count;
};
$scope.archive = function() {
var oldTodos = $scope.todos;
$scope.todos = [];
angular.forEach(oldTodos, function(todo) {
if (!todo.done) $scope.todos.push(todo);
});
};
}
.todo-true {
text-decoration: line-through;
color: grey;
}
.todo-Urgent {
color: red;
}
.todo-Critical {
color: orange;
}
.todo-Normal {
color: green;
}
.todo-IfYouCan {
color: RoyalBlue;
}
div.frame {
position: absolute;
margin: 10px 0px 0px 50px;
}
div.todo {
height: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.11/angular.min.js"></script>
<div ng-app>
<div class="frame">
<h2>Todo</h2>
<div ng-controller="TodoCtrl">
<div>
<input type="text" ng-model="todoText" size="30" placeholder="add new todo here">
<select ng-init="todoLvl = priority[2]" ng-model="todoLvl">
<option ng-repeat="item in priority" value="{{item}}">{{item}}</option>
</select>
<button ng-click="addTodo()" type="button">add</button>
</div>
<hr />
<span>{{remaining()}} of {{todos.length}} remaining</span>
<br />
<div ng-repeat="todo in todos">
<input type="checkbox" ng-model="todo.done">
<span class="done-{{todo.done}}"><span class="todo-{{todo.lvl}}">{{todo.text}} {{todo.lvl}}</span></span>
</div>
<br />
Completed
</div>
</div>
</div>
You can add "ng-class" for based on your todo.done (ng-model).
Also add more specific selector for color.
function TodoCtrl($scope) {
$scope.priority = ['Urgent', 'Critical', 'Normal', 'IfYouCan']
$scope.todos = [{
text: 'Attend Selection Day',
done: false,
lvl: 'Critical'
}, {
text: 'Register to Full Stack Web Course',
done: false,
lvl: 'Normal'
}, {
text: 'Go see X-Man apocalypse movie',
done: false,
lvl: 'IfYouCan'
}];
$scope.addTodo = function() {
$scope.todos.push({
text: $scope.todoText,
done: false,
lvl: $scope.todoLvl
});
$scope.todoText = '';
};
$scope.remaining = function() {
var count = 0;
angular.forEach($scope.todos, function(todo) {
count += todo.done ? 0 : 1;
});
return count;
};
$scope.archive = function() {
var oldTodos = $scope.todos;
$scope.todos = [];
angular.forEach(oldTodos, function(todo) {
if (!todo.done) $scope.todos.push(todo);
});
};
}
span.todo-true {
text-decoration: line-through;
color: grey;
}
.todo-Urgent {
color: red;
}
.todo-Critical {
color: orange;
}
.todo-Normal {
color: green;
}
.todo-IfYouCan {
color: RoyalBlue;
}
div.frame {
position: absolute;
margin: 10px 0px 0px 50px;
}
div.todo {
height: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.11/angular.min.js"></script>
<div ng-app>
<div class="frame">
<h2>Todo</h2>
<div ng-controller="TodoCtrl">
<div>
<input type="text" ng-model="todoText" size="30" placeholder="add new todo here">
<select ng-init="todoLvl = priority[2]" ng-model="todoLvl">
<option ng-repeat="item in priority" value="{{item}}">{{item}}</option>
</select>
<button ng-click="addTodo()" type="button">add</button>
</div>
<hr />
<span>{{remaining()}} of {{todos.length}} remaining</span>
<br />
<div ng-repeat="todo in todos">
<input type="checkbox" ng-model="todo.done">
<span class="done-{{todo.done}}"><span class="todo-{{todo.lvl}}" ng-class="{'todo-true': todo.done}">{{todo.text}} {{todo.lvl}}</span></span>
</div>
<br />
Completed
</div>
</div>
</div>

Categories