I want to change the font size of the element using a custom directive
Thus I have tried below code for that
<template>
<div class="hello"><label v-onhover>CLICK ME TO CHANGE FONT</label></div>
</template>
<script>
export default {
name: "CustomDirective",
props: {
msg: String
},
directives: {
onhover: {
bind(el, binding) {
el.onmouseover = function() {
el.fontSize = "100px";
};
}
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped></style>
Inside the bind, I'm getting the complete label element but don't know how to get it worked for changing the user-defined font size on mouse hover
You want el.style.fontSize instead of el.fontSize.
Vue.directive('onhover', {
bind(el, binding) {
el.onmouseover = function() {
el.style.fontSize = "100px";
};
}
});
new Vue().$mount('#app');
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="hello">
<label v-onhover>Hover on me to change font</label>
</div>
</div>
After trying a few different ways, I have solved it out the situation
Below is my code for the solution
<template>
<div class="hello">
<label v-onhover>{{ msg }}</label>
</div>
</template>
<script>
export default {
name: "CustomDirective",
data() {
return {
str: "",
msg: "Welcome to Your Vue.js App"
};
},
directives: {
onhover: {
bind(el, binding) {
el.onmouseover = function() {
el.style.fontSize = "100px";
};
el.onmouseout = function() {
el.style.fontSize = "15px";
};
}
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped></style>
Here in the above example, I have used two different event mouseover and mouseout event and based on the event state, we can change the property of an element
Related
I am building a Vue app. I have two components that are related to each other. One of them is called loginRegister.vue and the code of that is here:
loginRegister.vue:
<template>
<BaseModal idBtn = "regis" btnOneText="Cancel" :btnTwoText="button2Text['register']" v-show="showModal1" #close="showModal1 = false" #submitForm="registerUser(regLogPara['register'])" :popoverVue = "showToolTip.register">
<!-- the material that must be shown in modal comes here. it is better to use bootstrap "card" classes for compatible design -->
<!-- ################ -->
<!-- transition part -->
<!-- ################ -->
<transition name="fade">
<div v-if="modalAlert==='form'" key="item1">
<h4 class="card-title text-center my-3">Create Your Account</h4>
</div>
<!-- showing success message -->
<div v-else-if="modalAlert==='success'" key="item2">
<p>you succeed</p>
</div>
<!-- showing error message -->
<div v-else key="item3">
<p>there is an error!</p>
</div>
</transition>
</BaseModal>
</template>
<script>
import BaseButton from './BaseButton.vue';
import BaseInput from './BaseInput.vue'
import BaseModal from './BaseModal.vue';
import { onMounted, reactive, ref } from 'vue';
export default {
name: "loginRegister",
components: {
BaseModal,
BaseButton,
BaseInput
},
setup(props) {
const showModal = ref(false);
const showModal1 = ref(false);
const modalAlert = ref("form");
const registerUser = async (checkPara) => {
/* This function is responsible for sending form data to backend and getting the result from backend */
if (checkPara == "reset") {
/* for showing form again if there is a back-end error */
console.log("reset form");
modalAlert.value = "form";
button2Text["login"] = "Submit";
button2Text["register"] = "Submit";
regLogPara["login"] = "login";
regLogPara["register"] = "register";
} else {
/* for submiting form */
modalAlert.value = "error";
button2Text[checkPara] = "Try again";
regLogPara[checkPara] = "reset";
console.log("register function");
}
}
return {
registerUser,
showModal,
showModal1,
validation,
blurInput,
loginValid,
registerValid,
finalCheck,
showToolTip,
store,
modalAlert,
button2Text,
regLogPara
}
}
};
</script>
The other one called BaseModal.vue and the code of that is here:
BaseModal.vue:
<template>
<div class="modal-overlay container-fluid p-0">
<div class="row align-items-center justify-content-center" #click.self="$emit('close')">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<!-- text and other html comes here in the slot -->
<!-- ############## -->
<!-- this is the slot part -->
<!-- ############## -->
<slot></slot>
<div class="card-footer d-flex justify-content-around align-items-center">
<base-button
large
v-if="btnOneText"
kind = "btn-secondary"
:textBtn = "btnOneText"
#click="$emit('close')"
>
</base-button>
<base-button
large
v-if="btnTwoText"
kind = "btn-secondary"
:textBtn = "btnTwoText"
#click="popoverFunc"
#blur="popoverDisable"
:id="idBtn"
>
</base-button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import BaseButton from './BaseButton.vue';
import bootstrap from "bootstrap/dist/js/bootstrap.bundle.min.js";
export default {
// $emit('submitForm'),
name: "BaseModal",
props: {
btnOneText: {
type: String
},
btnTwoText: {
type: String
},
popoverVue: {
type: Boolean
},
idBtn: {
type: String
}
},
computed: {
popOverData: function () {
return this.popoverVue;
},
popVar: function() {
return new bootstrap.Popover(document.getElementById(this.idBtn), {
trigger: "manual",
title: "Notice:",
content: "Please fill all fields in the correct way to submit your information!",
customClass: "myPopover",
placement: "top"
})
}
},
data() {
return {
showPopOver: false
}
},
emits: ["close", "submitForm"],
components: {
BaseButton
},
watch: {
popOverData: function (newState, oldState) {
if(newState === true) {
this.showPopOver = true;
this.popoverFinal();
} else {
this.showPopOver = false;
this.popoverFinal();
}
}
},
methods: {
popoverFunc: function() {
this.$emit('submitForm');
if (this.popOverData) {
this.showPopOver = true;
this.popoverFinal();
}
},
popoverDisable: function() {
console.log("popoverDisable");
this.showPopOver = false;
this.popoverFinal();
},
popoverFinal: function() {
console.log(this.showPopOver);
/* this function is responsible for showing and hiding the popover according to "showPopOver" data */
if (this.showPopOver) {
this.popVar.show();
} else {
this.popVar.hide();
}
}
}
};
</script>
<style scoped src="../assets/css/compoStyles/baseModal.css"></style>
There are some codes that are not related to this question and also I tried to simplify the codes and clarify the parts that are related to transition and slot in my components code.
Although the codes may seem long or complicated, the goal that I want to reach is simple. I want to submit a register form in a modal component. Actually the user in my app clicks on register button and then the BaseModal.vue component is shown. In that case the register form (that for simplicity I substitute that with a h4 tag) is the default thing that user could see. After submitting the form according that the process is successful or there is an error, I want to show a message to the user and change the text of button from submit to try again if there is an error. After that when the user clicks try again button the form (h4 tag) must be fade in again. So I tried the v-if/v-else-if/v-else structure of Vue in my loginRegister.vue component. The code that I used in transition part is similar to the code that Vue documentation is used, But the transition does not work correctly. In my local development environment, the h4 tag disappears smoothly and then no message is shown, after clicking try again the h4 tag fade in again. Also in the console I could see this warning:
[Vue warn]: <transition> can only be used on a single element or component. Use <transition-group> for lists.
at <BaseTransition mode=undefined appear=false persisted=false ... >
at <Transition name="fade" >
at <BaseModal idBtn="regis" btnOneText="Cancel" btnTwoText="Try again" ... >
at <LoginRegister>
at <App>
But I don't think that is related to my issue, because I did not use transition on multi elements in my app. So could anyone please help me that what is wrong in my codes?
It seems like you want baseModal.vue to be inside loginRegister.vue and the baseModal to have some kind of transition whenever some state changes? Try changing <transition></transition> into <transition-group></transition-group>
Here are the simplified html and javascript files of the page. It has a button and component which is a text displays the data of the component. I want the component's data to be changed when I click the button. But how to access the component's data from a script?
index.html
<body>
<div id="app">
<my-component></my-component>
<button id="btn"> change data </button>
</div>
<script src="https://unpkg.com/vue#next"></script>
<script src="./main.js"></script>
</body>
main.js
let app = Vue.createApp({});
app.component('my-component', {
data: function() {
return {
component_data : "foo"
}
},
template: '<p> data = {{ component_data }} </p>'
}
);
app.mount("#app");
document.querySelector("btn").onclick = function() {
// HOW TO CHANGE component_data TO "bar"
}
One possibility is to incorporate the button into the HTML within the component's template. If that's feasible for your app then you can add a function to the component and bind the function to the button's click event.
E.g. (Note this is untested so may have typos)
app.component('my-component', {
data: function() {
return {
component_data : "foo"
}
},
methods: {
changeData() {
this.component_data = "The data changed";
}
},
template: `<p> data = {{ component_data }} </p>
<button #click="changeData">Change data</button>`
}
);
If the button can't be incorporated into my-component then I'd recommend using the Vuex datastore. Vuex is a reactive datastore that can be accessed across the entire application.
You can use component props change data between components.
index.html
<body>
<div id="app">
<my-component :component-data="text"></my-component>
<button #click="handleBtnClick"> change data </button>
</div>
<script src="https://unpkg.com/vue#next"></script>
<script src="./main.js"></script>
</body>
main.js file
let app = Vue.createApp({
data() {
return { text: 'foo' }
},
methods: {
handleBtnClick() {
this.text = 'bar';
}
}
});
app.component('my-component', {
props: {
componentData: {
type: String,
default: 'foo'
}
}
template: '<p> data = {{ componentData }} </p>'
}
);
app.mount("#app");
I think you new in Vuejs. You have to first read Vue documentation
To get the reference of a component outside of it, you can use the template refs
Here is the refactor of the code provided in the above question to access the components data from the script.
<div id="app">
<my-component ref="my_component"></my-component>
<button #click="onBtnClick()"> change data </button>
</div>
let app = Vue.createApp({
methods: {
onBtnClick() {
this.$refs.my_component.component_data = "bar";
}
}
});
How can I i update the counters separatly inside v-for? Now it updates both when incrementing, since the list can be dynamic i cant set variables for all the types.
The code i run inside the v-for is:
<a #click.prevent="decrease(quote, min)">
Decrease
</a>
<input name="types" type="text" v-model="quote" >
<a #click.prevent="increase(quote, max)">
Increase
</a>
It looks like:
And here is the vue script:
<script>
var app = new Vue({
el: "#contents",
data: {
quote: 1,
},
computed: {
},
methods: {
decrease: function(quote, min){
app.quote--;
if(app.quote < min){
app.quote = min;
}
},
increase: function(quote, max){
if(app.quote > max){
app.quote = max;
}
},
},
});
</script>
You should do something like this:
<template>
<div>
<div v-for="item in d" :key="item">
<Counter></Counter>
</div>
</div>
</template>
<script>
import Counter from "./Counter.vue";
export default {
name: "HelloWorld",
components: { Counter },
props: {
msg: String
},
data: () => {
return {
d: [1, 2]
};
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
/// counter component with its own state
<template>
<div>
<div>
<span>{{count}}</span>
<button v-on:click="increment">increment</button>
<button v-on:click="decrement">decrement</button>
</div>
</div>
</template>
<script>
export default {
name: "Counter",
data: () => {
return {
count: 0
};
},
methods: {
increment() {
this.count++;
},
decrement() {
this.count--;
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
demo: https://codesandbox.io/s/silly-solomon-0hk16?file=/src/components/Counter.vue:0-516
The idea is that you should put the logic inside each component, but in your case you set just one state ant try to share it for many instance, instead you should create a reusable component with its own state, like in my case : count
I have am building a Vue app that includes a QuillJS editor in a tab. I have a simple setTab(tabName) Vue method that shows/hides tabs with the v-if directive.
methods: {
setTab: function (tabName) {
this.view = tabName;
if(tabName === 'compose') {
var editor = new Quill('#editor', {
modules: { toolbar: '#toolbar' },
theme: 'snow'
});
}
}
}
My tab is basically like this:
<div id="composer" v-if="tabName === 'compose'">
<!-- toolbar container -->
<div id="toolbar">
<button class="ql-bold">Bold</button>
<button class="ql-italic">Italic</button>
</div>
<!-- editor container -->
<div id="editor">
<p>Hello World!</p>
</div>
</div>
Currently, I'm getting an error because the #editor element does not yet exist when I am calling new Quill(...). How do I delay that QuillJS initialization on the page so that it doesn't happen until after the #editor is already there?
Use mounted hook.
mounted: function () {
// Code that will run only after the
// entire view has been rendered
}
Use this.$nextTick() to defer a callback to be executed after the next DOM update cycle (e.g., after changing a data property that causes a render-update).
For example, you could do this:
methods: {
setTab: function (tabName) {
this.view = tabName;
if(tabName === 'compose') {
this.$nextTick(() => {
var editor = new Quill('#editor', {
modules: { toolbar: '#toolbar' },
theme: 'snow'
});
})
}
}
}
A clean way to do this is not to rely on selectors but make Quill editor a self-contained component:
<template>
<div class="quill-editor">
<!-- toolbar container -->
<div ref="toolbar">
<button class="ql-bold">Bold</button>
<button class="ql-italic">Italic</button>
</div>
<!-- editor container -->
<div ref="editor">
<p>Hello World!</p>
</div>
</div>
</template>
<script>
...
name: "QuillEditor",
mounted() {
this.quill = new Quill(this.$refs.editor, {
modules: { toolbar: this.$refs.toolbar },
theme: 'snow'
});
}
...
</script>
I am very new to Vuejs so although I can probably devise a solution myself by using a watcher or perhaps a lifecycle hook I would like to understand why the following does not work and what should be done instead.
The problem is that the mutated local data doesn't update whenever the component consumer changes the property cellContent. The parent owns cellContent so using the property directly is a no-no (Vue seems to agree).
<template>
<textarea
v-model="mutableCellContent"
#keyup.ctrl.enter="$emit('value-submit', mutableCellContent)"
#keyup.esc="$emit('cancel')">
</textarea>
</template>
<script>
export default {
name: 'CellEditor',
props: ['cellContent', 'cellId'],
data () {
return {
mutableCellContent: this.cellContent
}
}
}
</script>
<style>
...
</style>
In data (mutableCellContent: this.cellContent) you are creating a copy of the prop, that's why when the parent changes, the local copy (mutableCellContent) is not updated. (If you must have a local copy, you'd have to watch the parent to update it.)
Instead, you should not keep a copy in the child component, just let the state be in the parent (and change it through events emitted in the child). This is a well known the best practice (and not only in Vue, but in other frameworks too, if I may say it).
Example:
Vue.component('cell-editor', {
template: '#celleditor',
name: 'CellEditor',
props: ['cellContent', 'cellId'],
data () {
return {}
}
});
new Vue({
el: '#app',
data: {
message: "Hello, Vue.js!"
}
});
textarea { height: 50px; width: 300px; }
<script src="https://unpkg.com/vue"></script>
<template id="celleditor">
<textarea
:value="cellContent"
#keyup.ctrl.enter="$emit('value-submit', $event.currentTarget.value)"
#keyup.esc="$event.currentTarget.value = cellContent">
</textarea>
</template>
<div id="app">
{{ message }}
<br>
<cell-editor :cell-content="message" #value-submit="message = $event"></cell-editor>
<br>
<button #click="message += 'parent!'">Change message in parent</button>
</div>
You have to create a watcher to the prop cellContent.
Vue.config.productionTip = false
Vue.config.devtools = false
Vue.config.debug = false
Vue.config.silent = true
Vue.component('component-1', {
name: 'CellEditor',
props: ['cellContent', 'cellId'],
data() {
return {
mutableCellContent: this.cellContent
}
},
template: `
<textarea
v-model="mutableCellContent"
#keyup.ctrl.enter="$emit('value-submit', mutableCellContent)"
#keyup.esc="$emit('cancel')">
</textarea>
`,
watch: {
cellContent(value) {
this.mutableCellContent = value;
}
}
});
var vm = new Vue({
el: '#app',
data() {
return {
out: "",
cellContent: ""
}
},
methods: {
toOut(...args) {
this.out = JSON.stringify(args);
},
changeCellContent() {
this.cellContent = "changed at " + Date.now();
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<component-1 :cell-content="cellContent" #value-submit="toOut" #cancel="toOut"></component-1>
<p>{{out}}</p>
<button #click="changeCellContent">change prop</button>
</div>