Property or method "firstName" is not defined on the instance but referenced during render - vue.js

I keep getting this warning for my inputs on my Signup.vue page. I have warning for all the four inputs in my code:
firstName
lastName
email
password
I keep getting this warning for each instance respectively.
[Vue warn]: Property or method "firstName" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
---> found in
---> <Register> at src/components/Register.vue
<App> at src/App.vue
<Root>
This is my Signup component
<template>
<div id="register">
<form>
<label for="firstName">First Name</label>
<input type="text" id="firstName" v-model="firstName"/>
<label for="lastName">Last Name</label>
<input type="text" id="lastName" #v-model="lastName"/>
<label for="email">Email</label>
<input type="text" id="email" v-model="email"/>
<label for="password">Password</label>
<input type="text" id="password" v-model="password"/>
<button type="submit">Register!</button>
</form>
</div>
</template>
<script>
export default {
data(){
return {
form: {
firstName: '',
lastName: '',
email: '',
password: ''
}
}
},
methods: {
//methid take two parameters input or local parameter and the value of it 'firstname'-john
updateForm (input, value){
this.form[input] = value
let storedForm = this.openStorage() //extract openstorage and place in storedform
if(!storedForm) storedForm = {} //if no data, then make empty object
storedForm[input] = value //store new value
this.saveStorage(storedForm) //save changes in saveStorage function which is connected to local storage
},
mounted() {
if (localStorage.name){
this.name = localStorage.name;
}
},
//opening local storage form
openStorage(){
return JSON.pasrse(localStorage.getItem('form'))
},
//saving local storage data
saveStorage(){
localStorage.setItem('form', JSON.stringify(form))
}
},
watch: {
name(newName){
localStorage.name = newName;
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

While rendering the view, Vue is unable to find the properties for each of your input v-models, since the fields are in the form object. You can either refer to them as form.firstName, form.lastName etc or move them out of the object in the data() section.

Related

Vuejs/Nuxtjs : How to create dynamic V-model names without using the v-for?

I am encountering a tricky issue in my Vuejs/Nuxtjs application. In the application, I am creating multiple Nodes dynamically. These Nodes have the Radio button for which I have assigned a v-model. However, when I change the value of one Vuejs v-model is affecting all other Node Values.
I am aware that this issue is happening because of the same v-model being used for all Nodes. I would like to assign a different V-model to my Radio button but I want to do it without using the v-for.
I have created the sample code in the CodeSandbox
Steps to reproduce:
Drag and drop the Identifiers into the canvas. Now the URN will be selected.
Now Drag and drop another Identifiers into the canvas. Now the first Identifiers Node: URN will disappear. I am unable to handle each Node value independently.
The problem is arising in the file #components/IdentifiersNode.vue and in the radio button.
Code sample based on the Kissu response :
<input
id="identifierTypeURN"
:data="identifierSyntax"
value="URN"
type="radio"
name="instanceIdentifierURN"
#input="instanceIdentifiersSyntaxChange('URN')"
>
<label for="identifierTypeURN">URN</label>
<input
id="identifierTypeWebURI"
:data="identifierSyntax"
value="WebURI"
type="radio"
name="instanceIdentifierWebURI"
#input="instanceIdentifiersSyntaxChange('WebURI')"
>
<label for="identifierTypeWebURI">WebURI</label>
Can someone please check and let me know what am I doing wrong here: https://codesandbox.io/s/cocky-matan-kvqnu?file=/nuxt.config.js
After some effort able to get it working. I was using the Radio button functionalities wrongly. I changed it to something like this and it worked fine:
<template>
<div ref="el">
<div class="header">Identifiers Node: {{ ID }}</div>
<div id="app" class="nodeContainer">
{{ "Value : " + identifierSyntax }}
Syntax:
<input
:id="`identifierTypeURN-${ID}`"
:data="identifierSyntax"
value="URN"
type="radio"
:name="`instanceIdentifier-${ID}`"
:checked="identifierSyntax === 'URN'"
#input="instanceIdentifiersSyntaxChange($event, 'URN')"
/>
<label :for="`identifierTypeURN-${ID}`">URN</label>
<input
:id="`identifierTypeWebURI-${ID}`"
:data="identifierSyntax"
value="WebURI"
type="radio"
:name="`instanceIdentifier-${ID}`"
:checked="identifierSyntax === 'WebURI'"
#input="instanceIdentifiersSyntaxChange($event, 'WebURI')"
/>
<label :for="`identifierTypeWebURI-${ID}`">WebURI</label>
</div>
</div>
</template>
<script>
export default {
data() {
return {
ID: "",
nodeId: "",
bizStep: "",
allNodeInfo: [],
identifierSyntax: "URN",
};
},
mounted() {
console.log("MOUNTED");
this.$nextTick(() => {
const id = this.$el.parentElement.parentElement.id;
const data = this.$df.getNodeFromId(id.slice(5));
this.ID = data.data.ID;
this.nodeId = data.data.nodeId;
this.allNodeInfo = JSON.parse(
JSON.stringify(
this.$store.state.modules.ConfigureIdentifiersInfoStore
.identifiersArray,
null,
4
)
);
this.identifierSyntax = this.allNodeInfo.find(
(node) => node.identifiersId === this.nodeId
).identifierSyntax;
});
},
methods: {
// On change of the IdentifierSyntax change, change the value in the respective node info
instanceIdentifiersSyntaxChange(event, syntaxValue) {
console.log("CHANGED : " + syntaxValue);
console.log(event.target.defaultValue);
this.identifierSyntax = syntaxValue;
// Change the value of the respective syntax within the Node information in IdentifiersNode array
this.$store.commit(
"modules/ConfigureIdentifiersInfoStore/identifiersSyntaxChange",
{ nodeId: this.ID, syntaxValue }
);
},
},
};
</script>
<style>
.header {
background: #494949;
margin-top: -15px;
margin-left: -15px;
margin-right: -15px;
padding: 10px 15px;
margin-bottom: 15px;
}
</style>

Pass object to component - vue js

I've tried a few different things and now must ask for some help (thanks in advance).
I'm creating a simple Vue js app where a component form emits data as a object (this works) to its parent.
the parent then runs a v-for loop and passes the Object data to a component which displays the data (this does not work).
Vue Version
($ vue --version
#vue/cli 4.4.1)
Parent code:
<template>
<div class="MEDS">
<goalForm #goal_data="goal_create($event)"/>
<section>
<div v-for="(goalItem, index) in this.goals_list_container" v-bind:key="index">
<goalItem :goal="goalItem"/>
</div>
</section>
</div>
</template>
// # is an alias to /src
import goalForm from '#/components/Goal_form.vue'
import goalItem from '#/components/Goal_item.vue'
export default {
name: 'MEDS',
components: {
goalForm,
goalItem
},
data(){
return{
// Recievs goals from goalForm on submit, this is an array of objects
goals_list_container: [],
}
},
methods: {
goal_create(payload){
this.goals_list_container.push(payload);
console.log(this.goals_list_container)
}
}
}
GOAL ITEM (that should display the goal)
<template>
<div>
<h3>{{goal.title}}</h3>
</div>
</template>
export default {
prop: ['goal'],
data(){
return {
goal: {}
}
},
watch: {
}
}
Goal_form: (EDITED)
<div>
<form action="" class="goalForm" #submit.prevent="emit_Goal">
<input type="text" name="title" id="title" v-model="title">
<input type="text" name="description" id="description" v-model="des">
<input type="date" name="dueDate" id="dueDate" v-model="dueDate">
<input type="number" name="priority" id="priority" v-model="priority">
<input type="submit" class="submit">
</form>
</div>
</template>
<script>
export default {
data() {
return {
title: null,
des: null,
dueDate: null,
priority: null,
goal_data: {}
}
},
methods: {
push_goal(){
this.goal_data.title = this.title
this.goal_data.des = this.des
this.goal_data.dueDate = this.dueDate
this.goal_data.priority = this.priority
},
emit_Goal(){
// Move goal details into Object to be Emited
this.push_goal()
// Emit the form to the parent on submit
this.$emit('goal_data', this.goal_data)
}
}
}
</script>
<style lang="scss" scoped>
form {
display: flex;
flex-direction: column;
input {
&[name="title"]{
}
}
}
</style>
The for loop seems to work as each submit of the Goal_form creates a new instance of the Goal_item component.... But it does not populate with any data.
I Am either getting this totally wrong or have missed something small but any help would be greatly appreciated -
W

Getting textbox to turn red on change

I'm trying to make my textbox red when someone starts adding text to it. The problem I'm having is it takes long
to hit my onChange method and change the text color red.
Here is my code
<template>
<div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label>Name</label>
<input
type="text"
class="form-control"
v-model="product.name"
#change="onChange"
:style="{ color: conditionalColor}"
>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['product'],
computed: {
conditionalColor(){
return this.dirty ? 'red' : ''
}
},
data() {
return {
dirty: false
}
},
methods: {
onChange(){
console.log('changing');
return this.dirty = true;
}
},
mounted() {
}
}
</script>
Try watching product.name and setting this.dirty = true; there. The first time you edit the input after mounting it will fire and accomplish the same as your onChange method. Additionally, you can save some steps by conditionally applying a CSS class instead of computing it.
<template>
<input type="text" class="form-control" :class="{ 'dirty': dirty }" v-model="product.name">
</template>
<style>
.dirty { color: red; }
</style>
<script>
export default {
props: ['product'],
data() {
return {
dirty: false
}
},
watch: {
'product.name': function() {
this.dirty = true;
}
},
}
</script>
This is also alternative way to style your input.
<template>
<input type="text" class="form-control" :class="{ 'dirty': product.name.length > 0}" v-model="product.name">
</template>
<style>
.dirty { color: red; }
</style>

#submit.prevent not working on vue md-input

when I click 'login button' even I don't fill data in md-input it still running,
I test my onSubmit() method by login with my user and it works!
I don't think I do thing wrong in the method so I guess that my form is incorrect.
here is my code :
my form
<form #submit.prevent="onSubmit">
<login-card header-color="green">
<h4 slot="title" class="title">CCRP Sign On</h4>
<p slot="description" class="description">IT solution by เจ้เก๋ IT-PM</p>
<md-field class="md-form-group" slot="inputs">
<md-icon>account_box</md-icon>
<label>ID...</label>
<md-input v-model.trim="userId" type="text"></md-input>
</md-field>
<md-field class="md-form-group" slot="inputs">
<md-icon>lock_outline</md-icon>
<label>Password...</label>
<md-input v-model.trim="password" type="password"></md-input>
</md-field>
<md-field class="md-form-group">
<md-icon>announcement</md-icon>
<label>Password...</label>
</md-field>
<md-button slot="footer" class="md-simple md-success md-lg" type="submit">Login</md-button>
</login-card>
</form>
in scrpit methods
async onSubmit() {
const authData = {
userId: this.userId,
password: this.password
};
await this.login(authData).then(() => {
if (this.isAuthenticated) {
this.$router.push("dashboard");
} else {
console.log("err");
}
});
},
can you help me solve this?
Your understanding of "prevent" key is quite incorrect.
All it does is not reload the form after submit action. However the submit action will be called irrespective of whether the prevent is used or not.
It is just preventing the default functionality of form getting reloaded after each submit.
On the other hand what you need to do is validate your form before actually submitting it.
Example :
//- Requires "vuelidate" - npm install vuelidate
<script>
import { validationMixin } from "vuelidate";
import { required, email } from "vuelidate/lib/validators";
export default {
name: "FormValidation",
mixins: [validationMixin],
data: () => ({
form: {
email: null,
password: null
},
userSaved: false,
sending: false,
lastUser: null
}),
validations: {
form: {
email: {
required,
email
},
password: {
required
}
}
},
methods: {
getValidationClass(fieldName) {
const field = this.$v.form[fieldName];
if (field) {
return {
"md-invalid": field.$invalid && field.$dirty
};
}
},
clearForm() {
this.$v.$reset();
this.form.email = null;
this.form.password = null;
},
saveUser() {
this.sending = true;
// Instead of this timeout, here you can call your API
window.setTimeout(() => {
this.lastUser = `${this.form.email}`;
this.userSaved = true;
this.sending = false;
this.clearForm();
}, 1500);
},
validateUser() {
this.$v.$touch();
if (!this.$v.$invalid) {
this.saveUser();
}
}
}
};
</script>
<style lang="scss" scoped>
.md-progress-bar {
position: absolute;
top: 0;
right: 0;
left: 0;
}
</style>
<template>
<div>
<!-- Calling validateUser insted of submit action -->
<form novalidate class="md-layout" #submit.prevent="validateUser">
<md-card class="md-layout-item md-size-50 md-small-size-100">
<!-- Title of the form -->
<md-card-header>
<div class="md-title">Login</div>
</md-card-header>
<!-- Inputs for the form -->
<md-card-content>
<md-field :class="getValidationClass('email')">
<label for="email">Email</label>
<md-input
type="email"
name="email"
id="email"
autocomplete="email"
v-model="form.email"
:disabled="sending"
/>
<span class="md-error" v-if="!$v.form.email.required">The email is required</span>
<span class="md-error" v-else-if="!$v.form.email.email">Invalid email</span>
</md-field>
<md-field :class="getValidationClass('password')">
<label for="password">Password</label>
<md-input
type="password"
name="password"
id="password"
autocomplete="password"
v-model="form.password"
:disabled="sending"
/>
<!-- to show errors in case validation fails -->
<span class="md-error" v-if="!$v.form.password.required">The email is required</span>
<span class="md-error" v-else-if="!$v.form.email.email">Invalid email</span>
</md-field>
</md-card-content>
<md-progress-bar md-mode="indeterminate" v-if="sending"/>
<md-card-actions>
<md-button type="submit" class="md-primary" :disabled="sending">Create user</md-button>
</md-card-actions>
</md-card>
<md-snackbar :md-active.sync="userSaved">The user {{ lastUser }} was saved with success!</md-snackbar>
</form>
</div>
</template>

Editing a form with save and cancel options

I'm new to VueJS. I'm trying to create a form with simple Save and Cancel functionality. When binding the model to form fields they get updated immediately as the inputs are changed, but I don't want that tight binding. Instead, I want to be able to save and submit when the "Save" button is pressed and revert the changes when the "Cancel" button is pressed.
What's the suggested Vue way of doing this?
It would also be ideal if we can show the server save status and indicate it on the form if the submission is failed. If you know of any examples or samples that would be hugely helpful. Thanks!
See in JSFiddle
<template>
<div id="app">
<div>
First Name:
<input type="text" v-model="user.firstName" :disabled="!isEditing"
:class="{view: !isEditing}">
</div><div>
Last Name:
<input type="text" v-model="user.lastName" :disabled="!isEditing"
:class="{view: !isEditing}">
</div>
<button #click="isEditing = !isEditing">
{{ isEditing ? 'Save' : 'Edit' }}
</button>
<button v-if="isEditing" #click="isEditing = false">Cancel</button>
</div>
</template>
<script>
var app = new Vue({
el: '#app',
data: {
isEditing: false,
user: {
firstName: 'John',
lastName: 'Smith'
}
}
})
</script>
<style>
.view {
border-color: transparent;
background-color: initial;
color: initial
}
</style>
There's a few ways to handle this. You could create a separate component for the form, pass props to it, and then handle the editing/saving by emitting changes or if you want to keep it in a single component you could use value binding and refs, e.g.
var app = new Vue({
el: '#app',
data: {
isEditing: false,
user: {
firstName: 'John',
lastName: 'Smith'
}
},
methods: {
save() {
this.user.firstName = this.$refs['first_name'].value;
this.user.lastName = this.$refs['last_name'].value;
this.isEditing = !this.isEditing;
}
}
})
.view {
border-color: transparent;
background-color: initial;
color: initial
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<div>
First Name:
<input type="text" ref="first_name" :value="user.firstName" :disabled="!isEditing"
:class="{view: !isEditing}">
</div><div>
Last Name:
<input type="text" ref="last_name" :value="user.lastName" :disabled="!isEditing"
:class="{view: !isEditing}">
</div>
<button #click="isEditing = !isEditing" v-if="!isEditing">
Edit
</button>
<button #click="save" v-else-if="isEditing">
Save
</button>
<button v-if="isEditing" #click="isEditing = false">Cancel</button>
</div>
Or you could use a variable cache mechanism (with suggested edits) e.g.
var app = new Vue({
el: '#app',
data: {
isEditing: false,
user: {
firstName: 'John',
lastName: 'Smith',
}
},
mounted() {
this.cachedUser = Object.assign({}, this.user);
},
methods: {
save() {
this.cachedUser = Object.assign({}, this.user);
this.isEditing = false;
},
cancel() {
this.user = Object.assign({}, this.cachedUser);
this.isEditing = false;
}
}
})
.view {
border-color: transparent;
background-color: initial;
color: initial
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<div>
First Name:
<input type="text" v-model="user.firstName" :disabled="!isEditing"
:class="{view: !isEditing}">
</div><div>
Last Name:
<input type="text" v-model="user.lastName" :disabled="!isEditing"
:class="{view: !isEditing}">
</div>
<button #click="isEditing = !isEditing" v-if="!isEditing">Edit</button>
<button #click="save" v-else-if="isEditing">Save</button>
<button v-if="isEditing" #click="cancel">Cancel</button>
</div>
With any of these options you can set a status message at the start of the save method and then update it whenever you're done with the server call.