Vue3js Cutom input component disappearing if value=null - vue.js

I’m using the following component for custom clic-edit input
<template>
<el-input
v-show="edit"
ref="inputField"
type="text"
placeholder="place"
v-model='value'
#blur.native="
local = $event.target.value;
edit = false;
$emit('input', local);
"
#keyup.enter.native="
local = $event.target.value;
edit = false;
$emit('input', local);"
/>
<span v-show="!edit" #click="startEdit" > {{ local }}</span>
</template>
<script>
export default {
props: ["value"],
data() {
return {
edit: false,
local: this.value,
};
},
watch: {
value: function () {
this.local = this.value;
},
},
methods: {
startEdit() {
this.edit = true;
this.$refs.inputField.focus();
},
},
};
</script>
I am then using it, everything's seems ok.
Text switch to input as expected.
But If I pass a null value then the rendered component disappears.
Why ? How can I avoid this ?
<template>
<ClickEdit :value="'velue-test '" />
</template>
When passing or validating an empty value, the component disappears.

Actually your components is being rendered (you can see in devtools console). But because you pass empty or null as value prop to component there is nothing to show for editing. So you must set a default text to show in case of empty value (for example Edit here...). Then after user update the input replace it.
You can see this codesandbox to understand what I mean.

Related

How to make a checkbox selected by default in VueJs?

EDIT: My problem has shifted somewhat, with a different code focus, so I created a new question.
I have a Beufy Form Field Component, with a Boolean Input Checkbox inside, this Form Field Component allows you to select the option "Later" and this disables the Form Field and Checkbox. I would like to have it so that when "Later" is selected, the Boolean Input Checkbox is ticked/enabled by default.
I've read the Buefy checkbox documentation and I can see that I should use
<b-checkbox :value="true"
but when I attempt add it to my FormField template (the checkbox is a child component of the Form Field component) call it throws errors, this is how the template is rendered:
<FormField
:for="param.editableByOperator"
:label="null"
:disabled="param.populationStrategy.value === 'later'"
:checked="checked"
as="Boolean">
</FormField>
How do I best implement this fix? I'll attach the Checkbox Component
Below:
<template>
<b-checkbox
v-model="localValue"
:name="$props.name"
:checked="checked">
{{label}}
</b-checkbox>
</template>
<script>
import BaseInput from './BaseInput';
export default {
name: 'BooleanInput',
mixins: [BaseInput],
props: {
checked: {
type: Boolean,
default: true,
},
}
};
</script>
edit:
In my component I have found these methods, which set the checkbox as unticked by default. Is there something I can do here which would set the checkbox(editableByOperator) to True when 'Later'(populationStrategy) is set to 'Later.
Methods:
drawMonadParams(monadSlug) {
const monad = this.ccMonad(monadSlug);
monad.params.forEach((x, idx) => {
this.addFormFields(['params', idx], {
value: this.defaultMonadParamValue(x.typeSlug),
populationStrategy: 'now',
editableByOperator: false,
ccRequestParamId: null,
name: x.name,
typeSlug: x.typeSlug,
});
});
},
defaultMonadParamValue(typeSlug) {
return typeSlug === 'boolean' ? false : '';
},
May be try not using the checked prop inside the template . Try a data variable and use that instead.
like this
<b-checkbox :checked="checkValue"></b-checkbox>
props(){
checked:{
type:Boolean,
default:true
}
data(){
return{
checkValue : this.checked
}
}

Vue stored valued through props not being reactive

So I pass value using [props] and stored it in child component's data. However, when passing [props] value changes from parent, it's not updating in child component's data. Is there a fix for this..?
Here is the link to w3 test (I tried to clarify the problem as much as possible here)
<div id='app'>
<div id='parent'>
<button #click='current_value()'>Click to see parent value</button>
<br><br>
<button #click='change_value($event)'>{{ txt }}</button>
<br><br>
<child-comp :test-prop='passing_data'></child-comp>
</div>
<br><br>
<center><code>As you can see, this methods is <b>NOT</b> reactive!</code></center>
</div>
<script>
new Vue({
el: "#parent",
data: {
passing_data: 'Value',
txt: 'Click to change value'
},
methods: {
current_value(){
alert(this.passing_data);
},
change_value(e){
this.passing_data = 'New Vaule!!';
this.txt = 'Now click above button again to see new value';
e.target.style.backgroundColor = 'red';
e.target.style.color = 'white';
}
},
components: {
"child-comp": {
template: `
<button #click='test()'>Click here to see child (stored) value</button>
`,
props: ['test-prop'],
data(){
return {
stored_data: this.testProp
}
},
methods: {
test(){
alert(this.stored_data);
}
},
watch: {
stored_data(){
this.stored_data = this.testProp;
}
}
}
}
});
Props have one way data flow, that's why it doesn't react when you update it from the parent component. Define a clone of your prop at data to make it reactive, and then you can change the value within the child component.
Short answer: you don't need stored_data. Use alert(this.testProp) directly.
Long answer: when child component is created, stored_data get it's value from this.testProp. But data is local, it won't change automatically. That's why you need to watch testProp and set it again. But is not working because of a simple mistake, your watch should be:
watch: {
testProp(){ // here was the mistake
this.stored_data = this.testProp;
}
}

set property and retrieve HTML as string from Vue component

In order to separate my code and make it cleaner, I would like to use a Vue component as an HTML template, pass some parameters to the template and get the resulting HTML back as a string.
I have made a simple example that almost works, but for some reason the returned HTML is not up to date. When I hit "click me" I do get an HTML-string from the "MyDetails"-component, but it shows the value passed from the previous time, I hit the "click me"-button, instead of showing the actual value.
Main.vue
<template>
<div>
<p>
<myDetails ref="myDetails"/>
</p>
<button #click="handleClick">click me</button>
<p>{{message}}</p>
</div>
</template>
<script>
import MyDetails from "/components/MyDetails.vue";
export default {
name: "hello",
components: {
MyDetails
},
methods: {
handleClick() {
this.$refs.myDetails.setMessage(new Date().getTime());
this.message = this.$refs.myDetails.$el.outerHTML;
}
},
data() {
return {
message: ""
};
}
};
</script>
MyDetails.vue
<template>
<div style="background-color:red">
<h1>MyDetails component</h1>
<p>{{message}}</p>
</div>
</template>
<script>
export default {
name: "hello",
data() {
return {
message: ""
};
},
methods: {
setMessage(value) {
this.message = value;
}
}
};
</script>
In the example above "MyDetails" is part of the template from the beginning. Is it possible to load it dynamically in the click-handler instead, so it doesn't show up, before I hit the "click me"-button?
Please see code here: https://codesandbox.io/s/vue-fullcalendar-example-50sv9?fontsize=14&hidenavigation=1&theme=dark
Updating the DOM takes time, you are immediately getting the myDetails outerHTML after you are changing its data, which doesn't give time for the change to propagate. Setting a slight delay as follows will give output as expected:
handleClick() {
this.$refs.myDetails.setMessage(new Date().getTime());
setTimeout(() => {
this.message = this.$refs.myDetails.$el.outerHTML;
}, 100)
}
For demo, see the sandbox here

Element UI dialog component can open for the first time, but it can't open for the second time

I'm building web app with Vue, Nuxt, and Element UI.
I have a problem with the Element dialog component.
It can open for the first time, but it can't open for the second time.
This is the GIF about my problem.
https://gyazo.com/dfca3db76c75dceddccade632feb808f
This is my code.
index.vue
<template>
<div>
<el-button type="text" #click="handleDialogVisible">click to open the Dialog</el-button>
<modal-first :visible=visible></modal-first>
</div>
</template>
<script>
import ModalFirst from './../components/ModalFirst.vue'
export default {
components: {
'modal-first': ModalFirst
},
data() {
return {
visible: false,
};
},
methods: {
handleDialogVisible() {
this.visible = true;
}
}
}
</script>
ModalFirst.vue
<template>
<el-dialog
title="Tips"
:visible.sync="visible"
width="30%"
>
<span>This is a message</span>
<span slot="footer" class="dialog-footer">
<a>Hello</a>
</span>
</el-dialog>
</template>
<script>
export default {
props: [ 'visible' ]
}
</script>
And I can see a warning message on google chrome console after closing the dialog.
The warning message is below.
webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:620 [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "visible"
found in
---> <ModalFirst> at components/ModalFirst.vue
<Pages/index.vue> at pages/index.vue
<Nuxt>
<Layouts/default.vue> at layouts/default.vue
<Root>
This is the screenshot of the warning message.
https://gyazo.com/83c5f7c5a8e4d6816c35b3116c80db0d
In vue , using directly to prop value is not allowed . Especially when your child component will update that prop value , in my option if prop will be use
for display only using directly is not a problem .
In your code , .sync will update syncronously update data so I recommend to create local data.
ModalFirst.vue
<el-dialog
title="Tips"
:visible.sync="localVisible"
width="30%"
>
<script>
export default {
props: [ 'visible' ],
data: function () {
return {
localVisible: this.visible // create local data using prop value
}
}
}
</script>
If you need the parent visible property to be updated, you can create your component to leverage v-model:
ModalFirst.vue
<el-dialog
title="Tips"
:visible.sync="localVisible"
width="30%"
>
<script>
export default {
props: [ 'value' ],
data() {
return {
localVisible: null
}
},
created() {
this.localVisible = this.value;
this.$watch('localVisible', (value, oldValue) => {
if(value !== oldValue) { // Optional
this.$emit('input', value); // Required
}
});
}
}
</script>
index.vue
<template>
<div>
<el-button type="text" #click="handleDialogVisible">click to open the Dialog</el-button>
<modal-first v-model="visible"></modal-first>
</div>
</template>
<script>
import ModalFirst from './../components/ModalFirst.vue'
export default {
components: {
'modal-first': ModalFirst
},
data() {
return {
visible: false,
};
},
methods: {
handleDialogVisible() {
this.visible = true;
}
}
}
</script>
v-model is basically a shorthand for :value and #input
https://v2.vuejs.org/v2/guide/forms.html#Basic-Usage
Side-note:
You can also import your component like so:
components: { ModalFirst },
as ModalFirst will be interpreted as modal-first as well by Vue.js

Vue closing component returns avoid mutating a prop directly

I have an component that I want to use on different pages. Well, it is working well till the first toggle. It shows like it used to, but when I click the 'Close' button, it closes, but console outputs :
[Vue warn]: Avoid mutating a prop directly since the value will be
overwritten whenever the parent component re-renders. Instead, use a
data or computed property based on the prop's value. Prop being
mutated: "visible"
found in
---> at src/components/Snackbar.vue
at src/views/Login.vue
And after that it doesn't show up on click
Any way to fix this?
Snackbar.vue
<template>
<v-snackbar v-model.sync="visible" :timeout="5000" bottom>
{{ content }}
<v-btn flat color="primary" #click.native="visible = false">Close</v-btn>
</v-snackbar>
</template>
<script>
export default {
name: 'snackbar',
props: [
'visible',
'content'
]
}
</script>
Login.vue
<template>
<div class="login">
<Snackbar :visible="snackbar.visible" :content="snackbar.content"></Snackbar>
</div>
</template>
<script>
import Snackbar from '#/components/Snackbar.vue'
export default {
components: {
Snackbar
},
data: function() {
return {
email: '',
password: '',
snackbar: {
visible: false,
content: ''
}
}
},
methods: {
login: function() {
if (this.email != '' && this.password != '') {
// Do something
} else {
this.snackbar.content = 'Fields can\'t be empty';
this.snackbar.visible = true;
}
}
}
}
</script>
The console error is being triggered by this:
#click.native="visible = false"
The component is directly mutating the incoming prop. If you want to keep this level of control where the parent component controls the visibility you'll have to do it by having the click event emit an event, which the parent component receives and sets this.snackbar.visible = false thereby triggering a prop change and the child component is hidden.
<Snackbar :visible="snackbar.visible" :content="snackbar.content"
v-on:requestClose="close"></Snackbar>
<v-btn flat color="primary" #click.native="$emit('requestClose')">Close</v-btn>
methods: {
close: function() {
this.snackbar.visible = false;
}
}