label not moving with Vuejs2 + Materializejs - vuejs2

I have a problem with the label of a text input with materializejs + vuejs
the label does not move up if I change the value of the input field with vue (it behaves correctly if I change manually the input)
a simple (not elegant) code showing that is (also on JSfiddle):
correct expected behavior by editing the field manually
not correct behavior by clicking on the last div (starting from an empty field): it changes the value of the input without moving the label
html
<div id=app>
<div class="input-field ">
<input type="text" v-model="value" id="field">
<label for="field">Field</label>
</div>
<div #click="value='newValue'">
clickOnMe
</div>
</div>
Javascript
new Vue({
el: '#app',
data: {
value: ''
}
})

See Prefilling Text Inputs section, seems there's a patch function that needs to be applied
<div #click="clickMe">
methods: {
clickMe() {
this.value = 'newValue';
$(document).ready(function() {
Materialize.updateTextFields();
});
}
}

Related

Change the CSS if dynamically changing image is loaded in Vue 3

I'm trying to change the CSS class of a div based on if an image is loaded. The image URL is taken from an <input> field, so <img src> can change. I've managed to set the CSS class on the first page-load using the #load event. But if I change the image's URL in the input field to a non-existent image, then the CSS doesn't change. How do I track if the input's value has changed and "re-check" if the image is loaded?
In the below example, I want to have green-bg if the image exists and red-bg if the image doesn't exist.
<div id="app">
<div :class="imgLoaded ? 'green-bg' : 'red-bg'">
<img :src=imgURL #load="imgLoaded = true" />
<br/>
<input v-model="imgURL" />
</div>
</div>
<script>
export default {
data() {
return {
imgURL: 'https://picsum.photos/200/300',
imgLoaded: false,
};
}
};
</script>
Link to CodePen
This is a nice place to use a watcher.
watch: {
imgURL(newVal, oldVal) {
if (newVal !== oldVal) this.imgLoaded = false
}
}
Hooking to the input event would work too, but there is an edge case where the new value is similar to the old value. If you then set the imgLoaded to false, the CSS class won't change to green-bg because the load event does not fire (since the url did not change).
When using a watcher you can compare the old value to the new value and only set imgLoaded to false if the values are different.
Here is the pen.
Add to you input:
<input type="text" v-model="imgURL" #input="updateURL">
and create function on methodth section:
updateURL() {
this.imgLoaded = false
}
I managed to solve my issue by using the #error method. imgLoaded would be set to false on error and set to true on load.
<div id="app">
<div :class="imgLoaded ? 'green-bg' : 'red-bg'">
<img :src=imgURL #load="imgLoaded = true" #error="imgLoaded = false" />
<br/>
<input v-model="imgURL" />
</div>
</div>
This works for the case where the CSS class is changed based on whether the image exists or not.

How do I fit the value of one v-model into another v-model?

In this example, I'm trying to fit the value from div id="message" into textarea using the Vue v-model construct, but this not work
<template>
<div>
<textarea v-model="text"></textarea>
</div>
<div>
<div id="message" v-model="text2">{{ comment.message }}</div>
<button #click="update(text2);">
Edit
</button>
</div>
</template>
<script>
export default {
data() {
return {
text: [],
text2: null
}
},
methods: {
/* not work */
update(text2) {
this.text = text2;
}
}
<script>
How do I make sure that when I click on the "edit" button, the value of v-model="text2" insert into v-model="text" ?
You cannot use v-model on a <div> because it isn't an input element.
It seems what you want to do is set text to the comment message when you click the edit button so that it can be edited by the textarea. All you have to do is pass comment.message as the argument:
<button #click="update(comment.message)">
A couple of other things:
You cannot have multiple root elements in your template (you have two root <div> elements). You can just wrap everything in a single <div>.
text has initial value [] which isn't compatible with a textarea's v-model; did you mean ''?

updating $vm0 doesn't update data instantly

I have a VueJS- HTML file like this
<!DOCTYPE html>
<body>
<div id="root">
<ul>
<li v-for="name in names" v-text="name"></li>
</ul>
<input type="text" id="input" v-model="newName" />
<button #click="addName">Add Name</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"</script>
<script>
new Vue({
el: '#root',
data: {
newName: '',
names: ['Joe', 'Mary', 'Jane']
},
methods: {
addName() {
this.names.push(this.newName);
this.newName = '';
}
}
});
</script>
</body>
Whenever I do this in console
$vm0.names[0] = 'Gurung'
It doesn't update the first name instantly but I have to add a new name to get it updated. Another thing is this variable works only when I click on Root in the Vue Extension. One guess of mine was that $vm0 was just used by extension but why does doing manipulations with it changes it in DOM too (not instantly as I told earlier).
Due to limitations in JavaScript, Vue cannot detect the following
changes to an array:
When you directly set an item with the index, e.g.
vm.items[indexOfItem] = newValue. When you modify the length of the
array, e.g. vm.items.length = newLength.
Reference
$vm0.$set($vm0.names, 0, 'Grung');

What is the proper way to add same component everytime a button is clicked?

I am creating a form which needs to have a button and when clicked a new component which is only an input field would be added.
For example I have only one field to put in my address, but my adress is very long so I need 2 additional input fields. I click a button twice and two more components with the input appear.
What is a proper way to achieve this functionality?
Here is a simple solution to your question.
https://jsfiddle.net/RiddhiParekh/usd57zkb/6/
Template =>
<div id="vue-instance">
<button #click="addInput">Click to add input</button>
<br>
Address:
<div v-for="i in count">
<input type="text">
</div>
</div>
Script =>
var vm = new Vue({
el: '#vue-instance',
data: {
count:1
},
methods:{
addInput(){
this.count = this.count+1
}
}
});

What is the proper way to use multiple heavy same VueJS components on a page?

I am trying to create custom input as VueJS component. It will have <input type="text"> field and button. This component must implement such behavior: you can type text with autocomplete or press button which opens modal dialog with records from database and then select one. Something like this:
<div class="input-group">
<input type="text" class="form-control" placeholder="Search for...">
<span class="input-group-btn">
<button class="btn btn-default" type="button" #click="openModal">Choose</button>
</span>
</div>
The modal dialog will contain complicated logic and a lot of HTML code. I think I will put modal dialog in other component.
After all my custom input component will be used on page in table rows like:
<tr v-for="item in items">
<td><input-component :item="item"><input-component></td>
</tr>
The table may contain 10-30 rows and that is a question - should I exclude heavy modal dialog code from my custom input component or it is fine in VueJS to have such many tens duplications in DOM?
What variant should I choose:
1) exclude modal dialog, place it once in top of page and call it from custom input components
<body>
<modal-component></modal-component>
<table><tbody>
<tr v-for="item in items">
<td><input-component :item="item"><input-component></td>
</tr>
</tbody></table>
</body>
2) include modal dialog and have tens of its duplicated code in DOM
<body>
<table><tbody>
<tr v-for="item in items">
<td><input-component :item="item"><input-component></td><!--now contains <modal-component></modal-component>-->
</tr>
</tbody></table>
</body>
Use a dynamic component and bind the component type to a reactive property.
Vue.component('customer-dialog', {
template: '<p>Customer Dialog</p>'
})
Vue.component('supplier-dialog', {
template: '<p>Supplier Dialog</p>'
})
var app = new Vue({
el: '#app',
data: {
dialog: null // initial dialog to use
},
methods: {
showDialog: function(d) {
this.dialog = d
// additional dialog initialization code
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<div id="app">
<component :is="dialog"></component>
<button #click="showDialog('customer-dialog')">Customers</button>
<button #click="showDialog('supplier-dialog')">Suppliers</button>
</div>
If you want to keep the switched-out dialogs in memory so that you can preserve their state or avoid re-rendering, you can wrap the dynamic component in a element.
<keep-alive><component :is="dialog"></component></keep-alive>
Just use one modal dialog for the whole page.
Fill in the dialog with relevant data from an array that will look like this
var dialogs = [
{ name: 'john', surname: 'snow' },
{ name: undefined, surname: undefined },
...
]
var currentDialog = 4
var dialogData = dialogs[currentDialog]