Vuejs: autofit div according to remaining space in window - vue.js

Within vueJs (2.5.16) I'm trying to set the style property of a div so it auto fit the remaining space of a window depending of its size:
I intended to use a computed value which would give me live the correct height and bind it into the style property of the wanted div:
Vue.component('menus', {
template: '<div id="menu-div">MY MENU</div>'
});
Vue.component('expandable', {
template: '<div id="expandable-div" :style="{height:expandableHeight}">EXPANDABLE<div>{{expandableHeight}}</div></div>',
computed: {
expandableHeight() {
return ($(window).height() - $("#expandable-div").position().top) + 'px'
}
}
});
var vm = new Vue({
el: '#app'
});
fiddle
Using $(window).height() and $("#expandable-div").position().top from jQuery I thought I could achieve a result since it works in my console.
Unfortunately I have an error:
TypeError: Cannot read property 'top' of undefined

Why not use CSS's flexbox to achieve that?
Vue.component('menus', {
template: '<div id="menu-div">MY MENU</div>'
});
Vue.component('expandable', {
template: '<div id="expandable-div">EXPANDABLE<div>foo</div></div>',
computed: {}
});
var vm = new Vue({
el: '#app'
});
body {
margin: 0;
}
#app {
display: flex;
flex-direction: column;
height: 100vh;
}
#menu-div {
height: 50px;
background: #768ffb;
margin-bottom: 5px;
}
#expandable-div {
background: #76fbc1;
flex-grow: 1;
}
<script src="https://unpkg.com/vue"></script>
<div id="app">
<menus></menus>
<expandable></expandable>
</div>

Related

Is it valid to provide to v-for unclosed tags for iteration in vue.js?

Assume, I have such vue-code:
<template>
<div v-for="(item, index) in items">
<div v-if="item.isComponent">
<component :is="item.value"/>
</div>
<template v-else>
{{item.value}}
</template>
</div>
</template>
<script>
export default {
data: function(){
return {
items: [
{
value: '<p>this is first part of paragraph
},
{
value: 'componentName',
isComponent: true,
},
{
value: 'this is the last part of paragraph</p>
},
],
//...
</script>
items - it's a parsed (which I haven't parsed yet) string for contenteditable tag editor.
If this is invalid, what workaround could be?
UPD.
items is a json which I will get from database which should be saved to database as user input to contenteditable div editor.
Html will get sanitize the Html part. So it is not a good idea. to do such things.
Html should be kept in template of vue.
But let say you wanted to show some data in tag. Instead of doing use computed prop and hide and show p tag. It also prevents the incomplete tag issue.
I am attaching jsfiddle solution[
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
items: [
{
value: '<p>this is first part of paragraph'
},
{
value: 'componentName',
isComponent: true,
},
{
value: 'this is the last part of paragraph</p>'
},
],
},
computed:{
dataVal(){
let val = "";
for(let i=0;i<this.items.length;i++){
val += this.items[i].value + " "
}
return val;
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" v-html="dataVal">
</div>
]1

Pass a variable to a child component

I'm trying to pass a class name as a variable to a child component.
Context: I have two types of header ( 2 different bakgrounds ) used all across the site.
Page1:
<HeaderMain bg="bg" />
data() {
return {
pageTitle: 'patterson.travel',
bg: 'bg-white',
}
}
Page:2
<HeaderMain bg="bg" />
data() {
return {
pageTitle: 'patterson.travel',
bg: 'bg-header',
}
}
HeaderMain :
<header>
<nav class="main md:bg-transparent" :class="bg"></nav>
</header>
But the class never gets applied to the <nav>
I tried adding the variable to HeaderMain component like so (as a default):
data() {
return {
bg: 'bg-red', // default?
}
}
But That is the class it allways has...
So, any idea what I'm missing, here?
( I also tried :bg="bg" )
You have to define the bg property on your HeaderMain component:
// HeaderMain.vue
<script>
export default {
props: ['bg']
}
</script>
You will then be able to use it in your template just like you did:
<header>
<nav class="main md:bg-transparent" :class="bg"></nav>
</header>
In order to access it, you need to define props as an array inside your children component.
Look at the code below.
Vue.component('user-name', {
props: ['name'],
template: '<p>Hi {{ name }}</p>'
})
const LastName = {
template: '<span> Patel </span>'
}
const Header = {
props: ['bg'],
template: '<span :class="bg"> {{bg}} Header </span>'
}
new Vue({
el: "#app",
data: function() {
return {
headerBg: 'header-bg'
}
},
components: {
'last-name': LastName,
'header-view': Header
}
});
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
.header-bg {
background: red;
}
.header-bg {
background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<header-view :bg="headerBg"></header-view>
<user-name name="Varit"></user-name>
<last-name></last-name>
</div>

Vue: Change class with the value of a variable in setInterval

I am learning Vue JS. I want to change class using setInterval. But can’t pass the changing value of Method to Computed Property. After two seconds class will change automatically with the changed value of "changeColor"
My Code:
HTML:
<div>
<button #click="startEffect">Start Effect</button>
<div id="effect" :class="myClass"></div>
</div>
CSS:
.highlight {
background-color: red;
width: 200px !important;
}
.shrink {
background-color: gray;
width: 50px !important;
}
Vue JS:
new Vue({
el: '#exercise',
data: {
changeColor: false
},
methods : {
startEffect: function() {
setInterval(function(){
this.changeColor = !this.changeColor;
//alert(this.changeColor);
}, 2000);
//alert(this.changeColor);
}
},
computed: {
myClass: function() {
return {
highlight: this.changeColor,
shrink: !this.changeColor
}
}
}
})
bind your function to the component...
setInterval(function(){
this.changeColor = !this.changeColor;
}.bind(this), 2000);
and then you can do ...
<div id="effect" :class="[changeColor?'highlight':'shrink']"></div>

vue set img height based on width with dynamic styling

I have an image that should have 50% height of its width.
<img :src="post.image" ref="image" :style="{ height: imageHeight + 'px' }" />
imageHeight() {
let image = this.$refs.image
if(!image) return 0
let height = image.clientWidth * 0.5
return height
}
Unfortunately image is undefined during the evaluation of imageHeight and it does not get reevaluated when the width changes. Is there some way to make it work with a watcher or some other way?
You can use the load event to set a variable. It looks like you're using a computed, but there's no data change for it to respond to.
new Vue({
el: '#app',
data: {
url: 'http://via.placeholder.com/200x200',
imageHeight: null
},
methods: {
setheight(event) {
let image = event.target;
this.imageHeight = image.clientWidth * 0.5;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<img :src="url" #load="setheight" :style="{ height: imageHeight + 'px' }">
<div>{{imageHeight}}</div>
</div>
You could also do
<div :style={ height: myComputedHeight + '%' }></div>
data() {
return {
myCount: 10,
myTotal: 100
};
},
computed: {
myComputedHeight() {
return Math.round((this.myCount / this.myTotal) * 100);
}
}
I had to find a solution to something similar making a square div.
new Vue({
el: "#app",
data: {
initialWidth: 100,
matchedWidth: null
},
mounted() {
this.matchWidth();
},
methods: {
matchWidth() {
this.matchedWidth = this.$refs.box.offsetWidth;
}
},
computed: {
igCardStyle() {
return {
width: `${this.initialWidth}%`,
height: `${this.matchedWidth}px`
};
}
}
});
.box-wrapper {
width: 200px;
}
.box {
background-color: red;
/* border: 1px solid black; */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" ref="app">
<div class="box-wrapper">
<div class="box" :style="igCardStyle" ref="box">
</div>
</div>
<hr>
<p>width: {{initialWidth}}% of .box-wrapper or {{matchedWidth}}px</p>
<p>height: {{matchedWidth}}px</p>
</div>
In this cas you have to watch out for borders present in your $refs.box that could vary the result.
If you need the height to be the half of the width try:
calc(${this.matchedWidth} * 0.5)px on the computed style property.
Hope it helps! BR

Vue.js css transition on class change

I am trying to use the css transition property to fade the #app background from black to white by adding a class of .lightTheme. The adding of .lightTheme class works as expected, but the transition does not.
Do I have to add the Vue transition element? And if so how? #app is not leaving/entering the dom - I just want to animate it's properties (and I don't want to add unnecessary code if possible!
HTML
<div id="app" v-bind:class="{ lightTheme: isActive }">
<button v-on:click="toggleTheme">Change theme</button>
</div>
JS
new Vue({
el: '#app',
data: {
isActive: false
},
methods: {
toggleTheme: function(){
this.isActive = !this.isActive;
// some code to filter users
}
}
});
CSS
#app {
background: black;
transition: 1s;
}
#app.lightTheme {
background: white;
}
Thanks!
To answer your question if you have to use transition for something like this, the answer is no. The solution you proposed should work out of the box.
There is probably something else that is interfering with your code, but the one you posted is correct.
I attached the working snippet.
new Vue({
el: '#app',
data: {
isActive: false
},
methods: {
toggleTheme: function(){
this.isActive = !this.isActive;
}
}
});
#app {
background: black;
transition: 1s;
}
#app.lightTheme {
background: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="app" :class="{ lightTheme: isActive }">
<button #click="toggleTheme">Change theme</button>
</div>