I have component child Input:
<template>
<div class="basic-input-outer" :style="styles">
<p class="paragraph-small">{{ title }}</p>
<input ref="name" :type="type" class="basic-input">
</div>
</template>
<script>
export default {
name: "Input",
props: ['type', 'styles', 'title', 'focus'],
watch: {
focus: function() {
this.setFocus()
}
},
methods: {
setFocus() {
this.$refs.name.focus()
}
}
}
</script>
<style lang="scss">
#import "../assets/css/components/Input";
</style>
And I have parent component where I work with this input:
<div class="login-options">
<p class="choose" #click="chooseLogin('email')">With Email</p>
<div class="vertical-line" />
<p class="choose" #click="chooseLogin('phone')">With Phone Number</p>
</div>
<div v-if="loginWithEmail">
<Input :focus="emailFocus" :title="'Email'" :type="'email'" />
</div>
<div v-else>
<Input :focus="phoneFocus" :title="'Phone number'" :type="'email'" />
</div>
...
chooseLogin(option) {
if (option === 'email') {
this.loginWithEmail = true
this.emailFocus = true
} else {
this.loginWithEmail = false
this.phoneFocus = true
}
}
So, the problem is, when I trigger the function it only focuses one time on one field, and then stops. I want to make that focus props works that way, so when it's triggered, the field will be focused, and it will be working not just one time, like in this case.
Here is solution. Actually, it was because v-if. v-if just remove element(-s) from DOM, so, the best way to solve such problems is to use conditional styling with display: none:
Input:
<template>
<div class="basic-input-outer" :style="styles">
<p class="paragraph-small">{{ title }}</p>
<input ref="name" :type="type" class="basic-input">
</div>
</template>
<script>
export default {
name: "Input",
props: ['type', 'styles', 'title', 'focus'],
watch: {
focus: function() {
if (this.focus) this.$refs.name.focus()
}
}
}
</script>
<style lang="scss">
#import "../assets/css/components/Input";
</style>
And parent component:
<Input :style="[!loginWithEmail ? {'display': 'none'} : {'': ''}]" :focus="emailFocus" :title="'Email'" :type="'email'" />
<Input :style="[loginWithEmail ? {'display': 'none'} : {'': ''}]" :focus="phoneFocus" :title="'Phone number'" :type="'email'" />
...
chooseLogin(option) {
if (option === 'email') {
this.loginWithEmail = true
this.emailFocus = true
this.phoneFocus = false
} else {
this.loginWithEmail = false
this.emailFocus = false
this.phoneFocus = true
}
}
Related
I have created simple component for button in vuejs,When I clicked on the button then the text should show "I have been clicked". and it does but with this I also getting error that say: 49:7 error Unexpected mutation of "buttonText" prop vue/no-mutating-props
I have tried with this.$emit() but didnot mange to fix the mutation error issue. Can someone explain me what I am doing wrong and why I cannot mutate the buttonText props. Any help will be much appreciated!
here is my code:
App.vue
<template>
<div>
<ButtonDisabled buttonText="ChangedText" />
</div>
</template>
ButtonDisabled.vue:
<template>
<div class="main-container">
<h1>Button Disable if nothing is enter in email field</h1>
<div class="wrapper">
<input type="email" placeholder="email" v-model="email" />
<button :disabled="email.length < 1" class="btn-sub" #click="handleClick">
Subcribe
</button>
<!-- <button :disabled="!email.length">Subscribe</button> -->
<h2>{{ email }}</h2>
<h1>{{ buttonText }}</h1>
</div>
</div>
</template>
<script>
export default {
name: "ButtonDynamic",
props: {
buttonText: {
type: String,
default: "clickMe",
required: false,
}
},
data() {
return {
email: "",
};
},
methods: {
handleClick() {
// this.$emit("change-text-onclick", this.buttonText);
this.buttonText = "I have been clicked!!!";
},
},
};
</script>
<style scoped>
.main-container {
width: 300px;
height: 150px;
margin: 0 auto;
}
.wrapper {
display: inline-flex;
flex-direction: column;
}
.btn-sub {
margin: 10px;
}
</style>
Getting this error:
But my component still works when clicked:
Is it a bad practice to mutate a prop see link, you can mutate an inner component variable inside data or use v-model to have two-way dataflow on your props using v-model.
in this case, you can:
set up your internal data variable with the value of your prop, and then mutate that data variable ( not the prop )
Create and set the prop as a model value and expose it with a emit.
I will show you the easier path to get out of the error, but you need to reconsider your component responsibilities.
Vue.component('mybutton', {
template: `
<div class="main-container">
<h1>Button Disable if nothing is enter in email field</h1>
<div class="wrapper">
<input type="email" placeholder="email" v-model="email" />
<button :disabled="email.length < 1" class="btn-sub" #click="handleClick">
Subcribe
</button>
<!-- <button :disabled="!email.length">Subscribe</button> -->
<h2>{{ email }}</h2>
<h1>{{ inputMessage }}</h1>
</div>
</div>
`,
props: {
buttonText: {
type: String,
default: "clickMe",
required: false,
}
},
data() {
return {
email: "",
inputMessage: this.buttonText
};
},
methods: {
handleClick() {
this.inputMessage = "I have been clicked!!!";
},
},
});
new Vue({
el: '#app',
data(){
return {
message: "Changed Text"
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
<mybutton button-text="Change Text" />
</div>
</div>
I need to make a dynamic v-model using v-for. I have a child component that will be used inside the father component.
I'll put the full code and the warning received.
It is funny because I use the same method in other project (NUXT) and it is working just fine. And this project (vuejs) has the same code and it is not.
It is seems to me it is not listening to 'event' and because of that the v-model is not working well. The v-model value does not update according to the changes on input space.
This is the Child Component
<template>
<div class="row p-1 g-3 d-md-flex justify-content-md-end">
<div class="col-4 d-flex flex-row-reverse">
<label for="inputPassword6" class="col-form-label">{{ profileDataItem }}</label>
</div>
<div class="col-8">
<input
type="text"
class="form-control"
:value="value"
#input="$emit('input', $event.target.value)"
>
</div>
</div>
</template>
<script>
export default {
name: 'RegisterField',
props: {
profileDataItem: {
type: String,
required: true
},
value: {
required: true
},
}
}
</script>
And this is the father component
<template>
<div class="form-register p-3 m-auto">
<form class="login-form p-3 border m-auto">
<h4 class="text-center p-2">Register yourself</h4>
<RegisterField
v-for="(item, index) in profileDataItems"
:key="index"
:profileDataItem="profileItems[index]"
v-model="item.model"
>
</RegisterField>
</form>
</div>
</template>
<script>
import RegisterField from './RegisterField.vue'
import ButtonSubmit from '../ButtonSubmit.vue'
export default {
name: 'RegisterForm',
components: {
RegisterField,
ButtonSubmit
},
data () {
return {
profileItems: ['First Name', 'Last Name', 'Email', 'Password', 'Confirm Password'],
profileDataItems: [
{ model: "firstName" },
{ model: "lastName" },
{ model: "email" },
{ model: "password" },
{ model: "confirmPassword" },
],
payloadProfileData: [],
}
},
}
</script>
Warning on console
[Vue warn]: Missing required prop: "value"
at <RegisterField key=4 profileDataItem="Confirm Password" modelValue="confirmPassword" ... >
at <RegisterForm>
at <SigninPage onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< Proxy {…} > >
at <RouterView>
at <App>
change value prop to modelValue your component's code should be this:
<template>
<div class="row p-1 g-3 d-md-flex justify-content-md-end">
<div class="col-4 d-flex flex-row-reverse">
<label for="inputPassword6" class="col-form-label">{{
profileDataItem
}}</label>
</div>
<div class="col-8">
<input
type="text"
class="form-control"
:value="modelValue"
#input="$emit('input', $event.target.value)"
/>
</div>
</div>
</template>
<script>
export default {
name: 'RegisterField',
props: {
profileDataItem: {
type: String,
required: true,
},
modelValue: {
required: true,
},
},
};
</script>
I have a custom component called HobbyForm which is a simple form with two controls, a checkbox and an input, this component is being called from a parent component called Content, along with other similar 'form' components.
<template>
<form>
<div class="row align-items-center">
<div class="col-1">
<Checkbox id="isHobbyActive" :binary="true" v-model="isActive"/>
</div>
<div class="col-5">
<InputText id="hobby" placeholder="Hobby" type="text" autocomplete="off" v-model="hobby"/>
</div>
</div>
</form>
</template>
<script>
export default {
name: 'HobbyForm',
data() {
return {
hobby: {
isActive: false,
hobby: null
}
}
},
}
</script>
My Content component is something like:
<template>
<language-form></language-form>
<hobby-form v-for="(hobbie, index) in hobbies" :key="index" v-bind="hobbies[index]"></hobby-form>
<Button label="Add Hobby" #click="addHobby"></Button>
</template>
<script>
export default {
name: "Content",
components: {
LanguageForm,
HobbyForm
},
data() {
return {
language: '',
hobbies: [
{
isActive: false,
hobby: null
}
]
};
},
methods: {
addHobby() {
this.hobbies.push({
isActive: false,
hobby: null
});
}
},
};
</script>
The idea is to be able to add more instances of the HobbyForm component to add another hobby record to my hobby data property; but I don't know how to keep track of these values from my parent without using an emit in my child components, since I don't want to manually trigger the emit, I just want to have the data updated in my parent component.
How should I access my child component's data from my parent and add it to my array?
In the current form passing parent data into a child component via v-bind="hobbies[index]" makes no sense as the child component (HobbyForm) has no props so it does not receive any data from the parent...
To make it work:
Remove data() from the child HobbyForm
Instead declare a prop of type Object
Bind form items to the properties of that Object
Pass the object into each HobbyForm
<template>
<form>
<div class="row align-items-center">
<div class="col-1">
<Checkbox id="isHobbyActive" :binary="true" v-model="hobby.isActive"/>
</div>
<div class="col-5">
<InputText id="hobby" placeholder="Hobby" type="text" autocomplete="off" v-model="hobby.hobby"/>
</div>
</div>
</form>
</template>
<script>
export default {
name: 'HobbyForm',
props: {
hobby: {
type: Object,
required: true
}
}
}
</script>
Even tho props are designed to be one way only so child should not mutate prop value, this is something else as you do not mutate prop value, you are changing (via a v-model) the properties of the object passed via a prop (see the note at the bottom of One-Way Data Flow paragraph)
Also change the parent to:
<hobby-form v-for="(hobby, index) in hobbies" :key="index" v-bind:hobby="hobby"></hobby-form>
Demo:
const app = Vue.createApp({
data() {
return {
hobbies: [{
isActive: false,
hobby: null
}]
};
},
methods: {
addHobby() {
this.hobbies.push({
isActive: false,
hobby: null
});
}
},
})
app.component('hobby-form', {
props: {
hobby: {
type: Object,
required: true
}
},
template: `
<form>
<div class="row align-items-center">
<div class="col-1">
<input type="checkbox" id="isHobbyActive" v-model="hobby.isActive"/>
</div>
<div class="col-5">
<input type="text" id="hobby" placeholder="Hobby" autocomplete="off" v-model="hobby.hobby"/>
</div>
</div>
</form>
`
})
app.mount('#app')
<script src="https://unpkg.com/vue#3.1.5/dist/vue.global.js"></script>
<div id='app'>
<hobby-form v-for="(hobby, index) in hobbies" :key="index" v-bind:hobby="hobby"></hobby-form>
<button #click="addHobby">Add Hobby</button>
<hr/>
<pre> {{ hobbies }} </pre>
</div>
Hi everyone i have a big trouble i need to modify the value of checkbox from parent to another parent to child , is not this difficulty i know but i'm at the start and after spent 2 days for this i can't try anything else , now i'll add all the code the child is named "UICheckbox" and i pass it to "ToDoListItem" and this last is passed to "ToDoList" , and every Todos is an in an array into "store.js"
UICheckbox
<template>
<div class="checkbox-container">
<input
type="checkbox"
:id="id"
:v-model="localTodo"
>
<label :for="id">
<p class="font-bold">{{ text }}</p>
</label>
</div>
</template>
<script>
export default {
props: [
"id",
"value",
"text"
],
data() {
return {
localTodo: this.value
}
},
watch: {
localTodo:{
handler(newVal, oldVal) {
this.$emit("change", newVal)
},
deep: true,
}
}
}
</script>
<style>
</style>
ToDoListItem
<template>
<div class="flex flex-row w-full my-2">
<UICheckbox
:v-model="localTodo.checked"
:id="todo.id"
:text="todo.text"
#change="localTodo.cheked = $event"
/>
<delete-button :todo="todo" />
</div>
</template>
ToDoList
<to-do-list-item
:todo="todo"
#click="changeChecked(todo)"
#change="todo.checked = $event"
/>
Inside of the DetailsForm.Vue I am using v-if and then in the Dom is hidding all the template. I dont understand why.
<template>
<div class="DetailsForm">
this is a test
<input type="checkbox" v-model="checked">I have filled all this page<br>
<div class="test" v-if="!$.checked.required">
This field is required
</div>
<button :disabled="!checked">Button</button>
</div>
</template>
<script>
import { required } from 'vuelidate/lib/validators'
export default {
name: 'DetailsForm',
data: function () {
return {
checked: false
}
},
validation: {
checked: {
required
}
}
}