how to update the height of a textarea in vue.js when updating the value dynamically? - vue.js

Working with Vue.js, I do use a simple way to set dynamically the height of a text area that resizes when typing. But I am not able to do it when the component mounts or the value updates.
I have already try http://www.jacklmoore.com/autosize/, but it has the same problem.
I have created a sandbox that shows the problem, when typing the box it updates, but not when the value changes dynamically
Live example: https://codesandbox.io/s/53nmll917l

You need a triggerInput() method:
triggerInput() {
this.$nextTick(() => {
this.$refs.resize.$el.dispatchEvent(new Event("input"));
});
}
to use whenever you're changing the value programatically, triggering the resize logic used on <textarea> on "real" input events.
Updated codesandbox.
Note: Without the $nextTick() wrapper, the recently changed value will not have been applied yet and, even though the input is triggered, the element has not yet been updated and the resize happens before value has changed, resulting in the old height and looking like it didn't happen.

Not really feeling the answers posted here. Here is my simple solution:
<textarea
rows="1"
ref="messageInput"
v-model="message"
/>
watch: {
message: function(newItem, oldItem) {
let { messageInput } = this.$refs;
const lineHeightInPixels = 16;
// Reset messageInput Height
messageInput.setAttribute(
`style`,
`height:${lineHeightInPixels}px;overflow-y:hidden;`
);
// Calculate number of lines (soft and hard)
const height = messageInput.style.height;
const scrollHeight = messageInput.scrollHeight;
messageInput.style.height = height;
const count = Math.floor(scrollHeight / lineHeightInPixels);
this.$nextTick(() => {
messageInput.setAttribute(
`style`,
`height:${count*lineHeightInPixels}px;overflow-y:hidden;`
);
});
},
}
<style scoped>
textarea {
height: auto;
line-height: 16px;
}
</style>

Related

Set CSS class dynamically without template

For each component with prefix mycomponent- I would like to add a class with the name of the component. I don't want to have to modify the component in order to do this.
My first thought was to use mixins and somehow add the class in beforeCreate but I haven't managed to add classes dynamically without using the template.
Do I have to use $el.classList.add(this.$options.name) in beforeUpdate or similar? Is there some more Vue-ish way to do it?
Here it is, wrapped up as plugin:
const addComponentNameAsClass = {
install(Vue, options) {
const fn = Vue.prototype.$mount;
Vue.prototype.$mount = function() {
fn.apply(this, arguments);
if (this.$options._componentTag?.startsWith("mycomponent-")) {
this.$el.classList.add(this.$options._componentTag);
}
}
}
}
Vue.use(addComponentNameAsClass);
// that's all you need
// see it working:
['a', 'b', 'foo', 'whatever'].forEach(type => {
Vue.component('mycomponent-' + type, {
template: '<div><slot /></div>'
})
});
new Vue({
el: '#app'
})
[class^="mycomponent-"] {
border: 1px solid;
margin-bottom: 4px;
padding: 1rem;
}
.mycomponent-a {
border-color: red;
}
.mycomponent-b {
border-color: blue;
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.12"></script>
<div id="app">
<mycomponent-a>I should get a red border.</mycomponent-a>
<mycomponent-b>I should get a blue one.</mycomponent-b>
<mycomponent-foo>bar</mycomponent-foo>
<mycomponent-whatever>Meh.</mycomponent-whatever>
</div>
Notes:
you should not expect this to work on Vue3. Why? Because whenever you're using internal props starting with _ Vue does not guarantee they'll still be there in the next major version update. But, on the other hand, the name of the component is not saved anywhere else (other than $options._componentTag).
the above won't work if you use components as <MycomponentA></MycomponentA>. However, you can swiftly get around it by running the value of $options._componentTag through a helper function (e.g: kebabCase from lodash).
note on note: if you want the added class to always be kebab-case, you'll have run the value passed to .classList.add() through kebabCase, as well). Otherwise, <MycomponentA> will add MycomponentA class and <mycomponent-a> will add mycomponent-a class, for obvious reasons.
Ref. "Vue-ish way": whatever the end goal of applying this "component" class is, chances are it can be achieved cleaner.
The very idea of placing classes denominating component type doesn't feel Vue-ish at all.
It feels WordPress-ish and Angular-ish. To me, at least.

Vuejs String formatting not working correctly

Hi am trying to style a div dynamically in VueJs.But facing this problem that the this.currentProgressLevel is not applied to the width property inside the currentStyle object.I am attaching the screenshot of code i am using. The width property is not working when i use ${this.currentProgressLevel *75}px Why it might not be working?When i change the this.currentProgressLevel with 0.33 manually it works but then it will be hardcoded, why the value is not taken from the data variable currentProgressLevel? Below is the code i am using:
In script :
data(){
return{
currentProgressLevel:.33,
currentStyle:{
width: ${this.currentProgressLevel *75}px ,
height:‘6px’,
background:‘green’
}
}}
You should turn currentStyle into computed like this:
computed: {
currentStyle () {
return {
width: `${this.currentProgressLevel *75}px`,
height:‘6px’,
background:‘green’
}
}
}
For reactive data, you should move 'currentStyle' to computed. in this case, you just catch the initial value of 'currentProgressLevel'.
computed:{
currentStyle(){
return {
width: ${this.currentProgressLevel *75}px ,
height:‘6px’,
background:‘green’
}
}
}

Vue: get image width + height returns zero on component load

My component is a modal which displays an <img> with some tags on top.
I get the image dimensions using const size = this.$refs.media.getBoundingClientRect().
However, the height and width is 0, so the tags become placed wrong.
So it seems the image hasn't loaded yet? If I delay getting the height+width with setTimeout, I do get the width and height. But it seems like a not too reliable workaround.
I have "v-cloak" on the modal, but that doesn't work.
I can get the html element with this.$refs.media, but it doesn't mean that the image is loaded, apparently.
So, is there a way to check if the image is loaded? Is there some way to delay the component until the image is loaded?
One more thing: if I add a "width" and "height" in inline style on the img, then those values are available directly. But I don't want to set the size that way because it ruins the responsiveness.
The code doesn't say much more, but it is basically:
<modal>
<vue-draggable-resizable // <-- each tag
v-for="( tag, index ) in tags"
:x="convertX(tag.position.x)"
:y="convertY(tag.position.y)"
(other parameters)
convertX (x) {
const size = this.$refs.media.getBoundingClientRect()
return x * (size.width - 100) // <--- 0 width
async mounted () {
this.renderTags(this.media.tags) // <-- needs img width and height
Updated, solution
As the below answer says, I can use the onload event. I also came across the "#load" event, which I tried and it worked. I can't find it in the documentation though(?) (I searched for #load and v-on:load). Using "onload" directly on the image element didn't work though, so #load seems to be the way.
If I have a data property:
data () {
return {
imgLoaded: false
I can set it to true with a method using #load on the image:
<img
#load="whenImgLoaded"
<template v-if="imgLoaded = true">
<vue-draggable-resizable
:x="convertX(tag.position.x)"
:y="convertY(tag.position.y)"
(other parameters)
methods: {
whenImgLoaded(){
this.imgLoaded = true;
this.renderTags(this.media.tags)
}
You could load the image first via javascript, and after it's loaded run the renderTags method.
E.g., do something like this:
async mounted() {
let image = new Image();
image.onload = function () {
this.renderTags(this.media.tags)
};
image.src = 'https://some-image.png'
}

ReactNative: How to measure height of Text Input after programatic change

React Native has documentation for AutoExpandingTextInput: https://facebook.github.io/react-native/docs/textinput.html
The Problem: When the content of the AutoExpandingTextInput is changed programmatically the height never changes.
For example:
componentWillReceiveProps(props) {
this.setState({
richText: this._addHighlights(props.richText)
});
}
//
<AutoExpandingTextInput ref={component => this._text = component}>
{this.state.richText}
</AutoExpandingTextInput>
Say, for example. the user hits a button that adds a link to the text that wraps to the next line; in this case, the AutoExpandingTextInput never expands, because the height only is measured & changed on the onChange event of the TextInput.
I need some work around to get the content height when no onChange is triggered --- or less ideally, a way to programmatically trigger an onChange to the TextInput.
Are there any solutions????
No need to use the AutoExpandingTextInput plugin any more. The functionality you need is supported (sort of) in react-native now and will resize with a programatic update. Try something like this:
_heightChange(event) {
let height = event.nativeEvent.contentSize.height;
if (height < _minHeight) {
height = _minHeight;
} else if (height > _maxHeight) {
height = _maxHeight;
}
if (height !== this.state.height) {
this.setState({height: height});
}
}
render() {
return (
<TextInput
{...this.props}
multiline={true}
onContentSizeChange={this._heightChange.bind(this)}
/>
)
}

Using GSAP (TweenMax) on css varabiles

I'm really into Google's Polymer and I love GSAP - and so far I've been using the two in conjunction without a hitch. Unfortunately I have now hit a problem - how do I use GSAP (TweenMax to be specific) with custom css variables?
For example:
To change someCssProperty of someElement I would
TweenMax.to(someElement, 1, someCssProperty: "value");
but the someCssProperty becomes an issue when I'm trying to animate a css variable, which take on the form --some-custom-css-variable .
I have tried to use
TweenMax.to(someElement, 1, --some-custom-css-Property: "value");
(obviously gives me errors) and I also tried to use TweenMax.to(someElement, 1, "--some-custom-css-Property": "value");
(quotes around the some-custom-Css-property) - however this results in
no change/animation and an invalid tween value error message on the developer console.
So the question is: how would I go about animating custom css variables with TweenMax (GSAP)?
Thanks for any help :)
EDIT:
I have tried using classes through
TweenMax.to("SomeElement", 5, {className:"class2"});
But this changed the element style as if I had declared it in css with a
SomeElement:hover {}
style (as in it does not animate, just changes immediately)
For now, you're going to have to manually update the variable using a generic object.
var docElement = document.documentElement;
var tl = new TimelineMax({ repeat: -1, yoyo: true, onUpdate: updateRoot });
var cs = getComputedStyle(docElement, null);
var blur = {
value: cs.getPropertyValue("--blur")
};
tl.to(blur, 2, { value: "25px" });
function updateRoot() {
docElement.style.setProperty("--blur", blur.value);
}
:root {
--blur: 0px;
}
img {
-webkit-filter: blur(var(--blur));
filter: blur(var(--blur));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.4/TweenMax.min.js"></script>
<img src="http://lorempixel.com/400/400/" />