Make div width from a computed value VUE 3 - vue.js

I am trying to set my div width from a computed value, but I don't know how to archive it.
Case: I have some products which the informations comes from an API, and the length value changes depending on the product I selected, for this reason this component should be Dynamic.
The customer wants somethins like this:
That means, the lenght value should be also my div/span width, when the value is 10, should be 10% green, 29, 29% green etc.
Here's my code:
<li>
<span class="detail-name">foo</span>
<div class="progress" :style="getDataClass" ></div>
<span class="bold-value">{{something}}</span>
</li>
"getDataClass" is a computed value which comes from Store
First, I filter the data:
getData () {
return this.product.find(meta => meta.key === 'length_value').value
},
this function gives me the value '63'. Afther it I add this value to use with Style:
getDataClass() {
return {width: this.getData + '%'}
},
This gives me the value { "width": "63%" }
And this is my SASS (Bulma)
.progress
position: relative
flex: 1
margin: 0 8px
height: 10px
background-color: $color-grey
&::before
content: ''
position: absolute
width: 100%
height: 100%
background-color: $color-green
Can someone please help me ?
I saw it already working on my Vue course, but I am missing something or it does not aplly for this case
I hope I could explain it correctly, thank you very much for your help.

Is getData a method or a computed value?
If it is a method, the following might help:
getDataClass() {
console.log("C:",{width: this.getData() + '%'});
console.log("C:",{width: this.getData + '%'});
return {width: this.getData() + '%'}
}

Related

Vue JS code is not updating inside a loop

I'm stuck with a problem for a couple of days now and I've no idea what might be the solution. I've tried a couple, nothing worked.
I'm trying to enlarge a span text to fit the parent container width.
I've tried this.$forceUpdate();, didn't work.
I've also tried to pause the loop, but I found out later that that's not really possible in JS.
<template>
<span
ref="textRowRef"
v-bind:style="{fontSize: fontSize + 'px'}" >
{{ textRow }}</span>
</template>
// this is the VueJS code
var textRow = this.$refs.textRowRef;
var parentRow = textRow.parentElement;
for(var i = 0; i < 10; i++) {
this.fontSize += 10;
console.log(`Text Row in loop: ${textRow.clientWidth}`);
textRow = this.$refs.textRowRef;
}
console.log(`Text Row: ${textRow.clientWidth}`);
console.log(`Parent Row: ${parentRow.clientWidth}`);
Results in the console:
10 Text Row in loop: 48
Text Row: 48
Parent Row: 378
Here's my attempt.
I set fontSize to 72px then gradually reduce it by 1px until the text fits within the box. For such a simple example the performance is fine but I suspect that in the context of a larger page reducing it by 1px each time might prove too slow.
Each time the fontSize changes it triggers a re-render. The updated hook then measures the text to decide whether it needs to be shrunk even further.
If the text changes it resets the size to 72px and starts the process over again. I've not made any attempt to track the size of the parent element but it would also need to perform a reset if the width of that element changed.
I've added whitespace: nowrap to the <span> to ensure that its width will overflow the parent element. Otherwise the text would just wrap when the width reaches the edge of the parent element.
const MAX_FONT_SIZE = 72
new Vue({
el: '#app',
data () {
return {
fontSize: MAX_FONT_SIZE,
textRow: 'Initial value for the text'
}
},
watch: {
textRow: 'resetSize'
},
mounted () {
this.refreshSize()
},
updated () {
this.refreshSize()
},
methods: {
async refreshSize () {
await this.$nextTick()
const textRow = this.$refs.textRowRef
const parentRow = textRow.parentElement
if (textRow.offsetWidth > parentRow.clientWidth) {
this.fontSize = Math.max(8, this.fontSize - 1)
}
},
resetSize () {
if (this.fontSize === MAX_FONT_SIZE) {
this.refreshSize()
} else {
this.fontSize = MAX_FONT_SIZE
}
}
}
})
#app {
background: #ccc;
border: 1px solid #000;
margin: 20px;
width: 300px;
}
.text-span {
white-space: nowrap;
}
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<span
ref="textRowRef"
class="text-span"
:style="{fontSize: fontSize + 'px'}"
>
{{ textRow }}
</span>
<br>
Edit: <input v-model="textRow">
</div>

Vue inline conditional if?

I have the following piece of code:
<div
:id="'object' + object.id""
:style="'position:absolute !important; width:' + object.width + '% !important; height:' + object.height + '% !important;'">
<div
This works fine and renders nicely as it should.
I now want to add conditional stuff in there. Something like
if object.background == 'solid'
background-color: #ffffff;
endif
I tried achieving this via the Vue inline if, which gave me this:
[object.background == 'solid' ? background-color: #ffffff important; :
'']
But that just gave a lot of errors, which lead me to think I'm tackling this all wrong.
What would the correct approach be for me to achieve having short conditional statements in my style?
I would use a computed property that returns a style object.
new Vue({
el: "#app",
data: {
width: '200px',
height: '200px',
background: 'solid',
},
computed: {
styleObj() {
return {
position: 'absolute !important',
width: `${this.width} !important`,
height: `${this.height} !important`,
background: this.background === 'solid' ? 'green' : 'yellow',
};
},
},
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.min.js"></script>
<div id="app">
<div :style="styleObj">
</div>
</div>
I don't know if this should be marked as a duplicate of this question: Condition in v-bind:Style
However, this method is documented only for classes binding, on https://v2.vuejs.org/v2/guide/class-and-style.html
That said, you ask:
What would the correct approach be for me to achieve having short conditional statements in my style?
I don't think this is a correct approach. I mean, the correct approach would be to write css classes and apply them conditionally, since those are the documented methods, leave this for cases when you have to apply direct styling to override all other styles that apply to the element in question.
Other correct approach would be to use data, props, or even a computed property or method.
Also, you can have both style and :style attributes and vue will mix them for you, so you can define base style on the normal attribute and "other stuff" on the style binding.
But as i say, leave this for extreme cases where you really need this on the style attribute.

Why should I use v-bind for style

I just started learning Vue and I was wondering, why should I use v-bind for style and not write it regularly in html/css file
Let's say you need to create a progress bar that is not static. You will then need to update the style attribute width for-example.
To accomplish this, we need to programatically edit the width of the element. We 'cannot' to this in plain css, therefore the :style attribute comes in handy.
Let's create an example:
Codepen
HTML
<div id="vue">
<div class="progress-bar">
<div :style="{'width':progress + '%'}" class="progress" />
</div>
<button #click="fakeProgress">Init fake progress</button>
</div>
Css;
.progress-bar, .progress {
border-radius: 20px;
height: 20px;
}
.progress-bar {
width: 250px;
background-color: gray;
}
.progress {
background-color: blue;
width: 0;
transition: all 1s ease;
}
Javascript
new Vue({
el: '#vue',
data: {
progress: 0
},
methods: {
fakeProgress() {
let progress = setInterval(() => {
if(this.progress == 100) {
clearInterval(progress)
} else {
this.progress += 1;
}
}, 50)
}
}
})
As you see here, we bind the progress data attribute to the width value on the fake progress bar. This is just a simple example, but I hope this makes you see its potential. (You could achieve this same effect using the <progress> tag, but that would ruin the explanation.
EDIT; Also want to point out that you are supposed to write all your css as normal as you point out in your question. However, :style is used in cases that you cannot normally use css for. Like the example above where we need css to change from a variable.

css resize: How to resize a dynamic growing div after max height is active

I want to dynamically add items to the list. the list's height should be as high as the items are. but at a certain point, it's 300px, the ul should stop "growing" … i did this with "max-height". so far so good. NOW that i have a scrollbar I want to add a resize functionality, so that the user can decide if it's bigger than max-height or not.
The problem is that it can't resize higher than max-height. Does anybody know how to do this? the best way?
html:
click to add
<ul>
<li>start</li>
</ul>
css:
ul {
background: #f2f2f2;
overflow: auto;
height: auto;
max-height: 300px;
resize: vertical;
}
js/jquery:
$('a').on('click', function (e) {
e.preventDefault();
$('ul').append('<li>item</li>');
});
here is the same on fiddle to see it in action: http://jsfiddle.net/ZwrzX/
thx in advance!
Working fiddle: http://jsfiddle.net/ZwrzX/4/
When the UL height reaches 290px, a link appears besides the add item link and the user can click on it to add 25px to the max-height of the UL, if the user clicks it 2 times, 50px (2 x 25px) is added to the max-height of the UL.
HTML
click to add item
Add 25px to UL Max height
<ul>
<li>start</li>
</ul>
JavaScript
$('#addItem').on('click', function (e) {
e.preventDefault();
$('ul').append('<li>item</li>');
if ($('ul').height() > 290) {
$('#addHeight').css('visibility', 'visible');
}
});
$('#addHeight').on('click', function() {
var currenULtHeight = $('ul').height();
$('ul').css('max-height', currenULtHeight + 25 + 'px')
});
CSS
ul {
background: #f2f2f2;
overflow: auto;
height: auto;
max-height: 300px;
resize: vertical;
}
#addHeight {
visibility: hidden;
}
ok. i tried again on my own and came to the following:
var max_h = $('ul').css('max-height').replace('px', '');
function check_height() {
var sum = 0;
$('li').each(function () {
sum += $(this).height();
});
if (sum >= max_h) $('ul').css({ 'height': max_h, 'max-height': sum });
else $('ul').css({ 'height': 'auto', 'max-height': max_h });
}
$('#addItem').on('click', function (e) {
e.preventDefault();
$('ul').append('<li>item</li>');
check_height();
});
$('#removeItem').on('click', function (e) {
e.preventDefault();
$('li').last().remove();
check_height();
});
fiddle: http://jsfiddle.net/ZwrzX/6/
I was asking if there is a cleaner solution. or a solution with less js?
any further ideas?

100% Height in dGrid

How is it possible to make a dGrid instance take up 100% of the height of its container? I can use CSS to make the ".dgrid" classed div a specific height but when I set it to 100% it doesn't display.
Got it.
.dgrid {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: auto;
}
(With position absolute/relative on container, of course)
I think the supported way to do this is with the .dgrid-autoheight css class.
require([
"dgrid/List",
"dgrid/OnDemandGrid",
"dgrid/Selection",
"dgrid/Keyboard",
"dojo/_base/declare",
"dgrid/test/data/createAsyncStore",
"dgrid/test/data/smallColorData",
"dojo/domReady!"
], function(List, Grid, Selection, Keyboard, declare, createAsyncStore, smallColorData){
window.grid = new (declare([Grid, Selection, Keyboard]))({
className: "dgrid-autoheight",
collection: createAsyncStore({ data: smallColorData }),
columns: {
col1: "Color",
col5: "R",
col6: "G",
col7: "B"
}
}, "grid");
});
This is from the test examples.
#voidstate's answer is good. Another way to do it is like this:
HTML:
<div class="containsDGrid">
<div data-dojo-attach-point="dgrid"></div>
</div>
CSS:
.containsDGrid {
height: 500px;
width: 500px;
}
.dgrid {
height: 100%;
width: 100%;
}
The key is that if you set the height of the dgrid to 100%, the dgrid's parent must have its hight set. For example if we don't set the height of the .containsDGrid div then it will look like this (notice the height of 2px):
For another example see Dojo Dgrid - Use remaining space in block