Conditional CSS proprety set in computed object doesn't evaluate on the browser - vue.js

I am trying to make the header element's height size to change depending on a criteria. I set the desired heights in a headerHeightClass inside a computed object but it doesn't seem to work.
<template>
<header :class="['w-full', 'text-sm', 'headerHeightClass']">
<div class="fixed top-0 left-0 w-full h-16 bg-white">
<!--Some irrelevant html code -->
</div>
</header>
</template>
<script>
export default {
name: "MainNav",
components: {},
data() {
return {
isLoggedIn: false,
};
},
computed: {
headerHeightClass() {
return {
"h-16": !this.isLoggedIn,
"h-32": this.isLoggedIn,
};
},
},
methods: {
loginUser() {
this.isLoggedIn = true;
},
},
};
</script>
I also tried:
computed: {
headerHeightClass() {
return this.isLoggedIn? "h-32": "h-16",
},
},
The headerHeightClass appears as a simple string in the browser.

You need to write it like this instead of using headerHeightClass as a string (remove ' ')
<template>
<header :class="['w-full', 'text-sm', headerHeightClass]">
<div class="fixed top-0 left-0 w-full h-16 bg-white">
<!--Some irrelevant html code -->
</div>
</header>
</template>
This can also be written without using computed
<template>
<header :class="[
'w-full text-sm',
{
'h-16':!isLoggedIn,
'h-32':isLoggedIn,
}
]">
<div class="fixed top-0 left-0 w-full h-16 bg-white">
<!--Some irrelevant html code -->
</div>
</header>
</template>

You can give a try like this (Just for a demo purpose I used Vue 2 version) :
Vue.component('headerComponent', {
props: ['msg'],
template: '<p>{{ msg }}</p>'
});
var app = new Vue({
el: '#app',
data: {
isLoggedIn: true
}
});
.w-full {
background-color: gray;
width: 100%;
}
.text-sm {
font-size: 12px;
}
.h-16 {
height: 16px;
}
.h-32 {
height: 32px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<header-component
msg="This is a header component"
:class="['w-full text-sm', { 'h-16':!isLoggedIn, 'h-32':isLoggedIn }]">
</header-component>
</div>

Related

I am converting my code snippet from pure html to Vue2 but facing some difficulty

This is pure html code with javascript.
filter.js
var group_filter=document.getElementById("group-filter");
var btn_show_filter=document.getElementById("icon-filter");
var btn_close_filter=document.getElementById("icon-close-filter");
function showfilter(){
group_filter.style.display='block';
btn_show_filter.style.display='none';
btn_close_filter.style.display='inline';
}
function closefilter(){
group_filter.style.display='none';
btn_show_filter.style.display='inline';
btn_close_filter.style.display='none';
}
And here is the code that I'm trying to process but it doesn't look right.
<div class="job-filter">
<h3>Filter <img id="icon-filter" #click="showfilter" :class="{ 'display-none': this.display }" src="../assets/recruit/angle-down-svgrepo-com.svg" alt=""> <img id="icon-close-filter" :class="{ 'display-inline': this.display }" #click="showfilter" src="../assets/recruit/close-svgrepo-com.svg" alt=""></h3>
<div class="radio-group" id="group-filter" :class="{ 'display-block': this.display}" >
</div>
</div>
Thank you everyone, and I look forward to your help.
You can simply achieve this by creating an object in the data method like this :
data() {
return {
display: {
group_filter: '',
btn_show_filter: '',
btn_close_filter: ''
}
}
}
And in methods object, you can assign the values :
methods: {
showfilter() {
this.display.group_filter = 'block';
this.display.btn_show_filter = 'none';
this.display.btn_close_filter = 'inline';
},
closefilter() {
this.display.group_filter = 'none';
this.display.btn_show_filter = 'inline';
this.display.btn_close_filter = 'none';
}
}
And then in the template, you can assign like this :
<div class="job-filter">
<h3>Filter <img id="icon-filter" #click="showfilter" :style="{ 'display': display.btn_show_filter }" src="../assets/recruit/angle-down-svgrepo-com.svg" alt=""> <img id="icon-close-filter" :style="{ 'display': display.btn_close_filter }" #click="closefilter" src="../assets/recruit/close-svgrepo-com.svg" alt=""></h3>
<div class="radio-group" id="group-filter" :style="{ 'display': display.group_filter }"></div>
</div>
I hope this is what you want to achieve.
I have create one demo on stackblitz.
link - https://vue-etrbkr.stackblitz.io
I have attached source code here.
<template>
<div id="app">
<div class="job-filter">
<h3>
Filter
<img
#click="toggleFilter"
class="filterIcon"
:src="
isFilterOpen
? 'https://cdn-icons-png.flaticon.com/512/61/61155.png'
: 'https://iaeste.org/assets/icons/arrow_dropdown-
8e096a444299e295fa03bc919f3cea625987792e13aaf64ec6b0be881a8f7c0a.svg'
"
width="20"
height="20"
/>
</h3>
<div class="radio-group" id="group-filter" v-if="isFilterOpen">
Filter Content
</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
isFilterOpen: false,
};
},
methods: {
toggleFilter() {
this.isFilterOpen = !this.isFilterOpen;
},
},
};
</script>
<style>
.filterIcon {
vertical-align: bottom;
cursor: pointer;
}
</style>

Add Close Button to Vue with Popper.js Modal Component

UPDATED Here is a codesandbox recreating the issue - Modal Does not hide: https://codesandbox.io/s/vue3-popperjs-modal-64762?file=/src/components/Modal.vue
I am new to Vue and cannot figure out how to Close the modal from the Hide Modal button within the component. I am using Vue.js and Popper.js for a modal/dropdown component.
I have tried emitting, rels and passing a prop but cannot get it to function correctly. I stripped it down to the shell code below.
** Main Component **
<template>
<div>
<Head title="Main" />
<div class="flex items-center">
<div class="flex">
<dropdown :auto-close="false" class="focus:z-10 px-4 hover:bg-gray-100 focus:border-white rounded-l focus:ring md:px-6" placement="auto">
<template #default>
<div class="flex">
<button id="open" class="btn-indigo" type="button">Open Modal</button>
</div>
</template>
<template #dropdown>
<div class="mt-2 px-4 py-6 w-screen bg-white rounded shadow-xl" style="maxWidth: 600px">
<h1>Modal Content</h1>
<button id="close" class="btn-indigo" type="button">Hide Modal</button>
</div>
</template>
</dropdown>
</div>
</div>
</div>
</template>
<script>
import { Head } from '#inertiajs/inertia-vue3'
import Dropdown from '#/Shared/Dropdown'
export default {
components: {
Head,
Dropdown,
},
}
</script>
** And Dropdown Component **
<template>
<button type="button" #click="show = true">
<slot />
<teleport v-if="show" to="#dropdown">
<div>
<div style="position: fixed; top: 0; right: 0; left: 0; bottom: 0; z-index: 99998; background: black; opacity: 0.2" #click="show = false" />
<div ref="dropdown" style="position: absolute; z-index: 99999" #click.stop="show = !autoClose">
<slot name="dropdown" />
</div>
</div>
</teleport>
</button>
</template>
<script>
import { createPopper } from '#popperjs/core'
export default {
props: {
placement: {
type: String,
default: 'bottom-end',
},
autoClose: {
type: Boolean,
default: true,
},
},
data() {
return {
show: false,
}
},
watch: {
show(show) {
if (show) {
this.$nextTick(() => {
this.popper = createPopper(this.$el, this.$refs.dropdown, {
placement: this.placement,
modifiers: [
{
name: 'preventOverflow',
options: {
altBoundary: true,
},
},
],
})
})
} else if (this.popper) {
setTimeout(() => this.popper.destroy(), 100)
}
},
},
mounted() {
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
this.show = false
}
})
},
}
</script>

Getting textbox to turn red on change

I'm trying to make my textbox red when someone starts adding text to it. The problem I'm having is it takes long
to hit my onChange method and change the text color red.
Here is my code
<template>
<div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label>Name</label>
<input
type="text"
class="form-control"
v-model="product.name"
#change="onChange"
:style="{ color: conditionalColor}"
>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['product'],
computed: {
conditionalColor(){
return this.dirty ? 'red' : ''
}
},
data() {
return {
dirty: false
}
},
methods: {
onChange(){
console.log('changing');
return this.dirty = true;
}
},
mounted() {
}
}
</script>
Try watching product.name and setting this.dirty = true; there. The first time you edit the input after mounting it will fire and accomplish the same as your onChange method. Additionally, you can save some steps by conditionally applying a CSS class instead of computing it.
<template>
<input type="text" class="form-control" :class="{ 'dirty': dirty }" v-model="product.name">
</template>
<style>
.dirty { color: red; }
</style>
<script>
export default {
props: ['product'],
data() {
return {
dirty: false
}
},
watch: {
'product.name': function() {
this.dirty = true;
}
},
}
</script>
This is also alternative way to style your input.
<template>
<input type="text" class="form-control" :class="{ 'dirty': product.name.length > 0}" v-model="product.name">
</template>
<style>
.dirty { color: red; }
</style>

Dynamically change <style> inside a Single File Component

Is it possible to dynamically change the content of a scoped inside a Single File Component?
You could do it using the v-html directive.
Since I don't know your actual code I will just give you the code for a basic proof of concept.
In the template...
<template>
<div>
<head v-html="styles"></head>
<div class="test">
<p>change this paragraph</p>
</div>
<textarea name="" id="" cols="30" rows="10" v-model="csscode"> </textarea>
</div>
</template>
In the script...
<script>
export default {
data() {
return{
csscode: null,
styles: null,
}
},
watch:{
csscode(val){
this.styles = '<style>' + val + '</style>';
}
}
}
</script>
Inside style tag, no. Its not possible because of build extracts a .css file.
But as an alternative you can use javascript object as an style object.
var app = new Vue({
el: '#app',
data: function(){
return {
textAreaStyle: {border: '2px solid blue', color: 'red', width: '500px', height: '300px'}
}
},
methods: {
updateStyle (event) {
this.$set(this.$data, 'textAreaStyle', JSON.parse(event.target.value));
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>
<div id="app">
<textarea :style="textAreaStyle" #change="updateStyle">{{textAreaStyle}}</textarea>
</div>

Use method in template, out of instance vue

This is warning when i click on go to contact in tab about: "Property or method "switchTo" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
(found in component )."
How do I fix this?
new Vue({
el: '#app',
data: {
currentPage: 'home',
},
methods: {
switchTo: function(page) {
this.currentPage = page;
}
},
components: {
home: {
template: `#home`,
},
about: {
template: `#about`,
},
contact: {
template: '#contact'
}
}
})
.navigation {
margin: 10px 0;
}
.navigation ul {
margin: 0;
padding: 0;
}
.navigation ul li {
display: inline-block;
margin-right: 20px;
}
input, label, button {
display: block
}
input, textarea {
margin-bottom: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<div id="app">
<div class="navigation">
<ul>
<li>Home</li>
<li>About</li>
</ul>
</div>
<div class="pages">
<keep-alive>
<component v-bind:is="currentPage">
</component>
</keep-alive>
</div>
</div>
<template id="home">
<p>home</p>
</template>
<template id="about">
<p>about go to contact</p>
</template>
<template id="contact">
<p>contact</p>
</template>
Just change your about template to this
<template id="about">
<p>about go to contact</p>
</template>
new Vue({
el: '#app',
data: {
currentPage: 'home',
},
methods: {
switchTo: function(page) {
this.currentPage = page;
}
},
components: {
home: {
template: `#home`,
},
about: {
template: `#about`,
},
contact: {
template: '#contact'
}
}
})
.navigation {
margin: 10px 0;
}
.navigation ul {
margin: 0;
padding: 0;
}
.navigation ul li {
display: inline-block;
margin-right: 20px;
}
input, label, button {
display: block
}
input, textarea {
margin-bottom: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<div id="app">
<div class="navigation">
<ul>
<li>Home</li>
<li>About</li>
</ul>
</div>
<div class="pages">
<keep-alive>
<component v-bind:is="currentPage">
</component>
</keep-alive>
</div>
</div>
<template id="home">
<p>home</p>
</template>
<template id="about">
<p>about go to contact</p>
</template>
<template id="contact">
<p>contact</p>
</template>
I already solved a problem like this in this question: Calling methods in Vue build
It's not the same problem so it's not a repeated question, but the answer is the same:
In the created hook, add the component to window.componentInstance like this:
methods: {
foo () {
console.log('bar')
}
},
created () {
window.componentInstance = this
}
Then you can call the method anywhere like this:
window.componentInstance.foo()