Cannot read component data within v-for scope - javascript

I'm just new to Vue and I'm struggling with accessing my component data within a v-for scope. I get this error using the following code.
TypeError: Cannot read property 'whatever' of undefined
at eval
<template>
<b-row class="my-1" v-for="field in inputFields" :key="field.key">
<b-col>
<b-form-input :type="field.type" :placeholder="this.whatever" required>
</b-form-input> <!-- placeholder ERROR! -->
</b-col>
</b-row>
<b-form-input :placeholder="this.whatever" required>
</b-form-input> <!-- placeholder OK! -->
</template>
<script>
export default {
data() {
return {
whatever: 'this is a string',
userShouldSignup: false,
parameters: {
email: {
label: 'Enter email',
type: 'email',
value: '',
},
password: {
label: 'Enter password',
type: 'password',
value: '',
},
confirmPassword: {
label: 'Confirm password',
type: 'password',
value: '',
},
},
}
},
computed: {
inputFields() {
return this.userShouldSignup ? [
this.parameters.email,
this.parameters.password,
this.parameters.confirmPassword,
] : [
this.parameters.email,
this.parameters.password,
];
},
}
};
</script>
How do I access my data variables within v-for?

Change :placeholder="this.whatever" to :placeholder="whatever". You don't need to use this there because Vue recognize that you want to access its data or computed values. This doesn't work because this in the loop is something else.
Look at this code snippet below (I had to change some things to reproduce your problem):
var app = new Vue({
el: '#app',
data() {
return {
whatever: 'this is a string',
userShouldSignup: false,
parameters: {
email: {
label: 'Enter email',
type: 'email',
value: '',
},
password: {
label: 'Enter password',
type: 'password',
value: '',
},
confirmPassword: {
label: 'Confirm password',
type: 'password',
value: '',
},
},
}
},
computed: {
inputFields() {
return this.userShouldSignup ? [
this.parameters.email,
this.parameters.password,
this.parameters.confirmPassword,
] : [
this.parameters.email,
this.parameters.password,
];
},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="app">
<div class="my-1" v-for="field in inputFields" :key="field.key">
<div>
<input type="text" :placeholder="whatever" required/>
</div>
</div>
<input :placeholder="whatever" required/>
</div>

It's not working because "this" is pointing to "field" (loop), and the "field" doesn't have a "whatever" variable.
You just need to remove "this" keyword. You don't need it at all when access the variables from "data"

Related

Vue component to change text based on data value

This is the data I get and I what to be able to replace the data content with readable text:
From parent:
data: [
{ name: 'discounts_offers', type: 'EMAIL', consent: true },
{ name: 'newsletter', type: 'EMAIL', consent: true },
{ name: 'product_upgrade', type: 'EMAIL', consent: true },
{ name: 'sms_offer', type: 'SMS', consent: true },
{ name: 'post_offer', type: 'POST', consent: true }
]
This is my included component
<CommunicationPreference
v-for="(communication, index) in data"
:key="index"
:communication="communication"
/>
Then the communicationPreference.vue:
<template>
<section>
{{ communication.name }} //This should be Newsletters etc
</section>
</template>
<script>
export default {
props: {
communication: {
type: Object,
default: null,
},
},
data() {
return {}
},
computed: {},
}
</script>
Then what I would like to do is if {{ communication.name }} equals 'discounts_offers' to use the text "Discounts and offers" like the images attached. Any solutions for the best approach to this?
You can use computed property for scenario like this. Something like this.
App.vue
<template>
<CommunicationPreference
v-for="(communication, index) in preferences"
:key="index"
:type="communication.type"
:name="communication.name"
v-model:consent="preferences[index].consent"
/>
</template>
<script>
import CommunicationPreference from "./components/CommunicationPreference.vue";
export default {
name: "App",
components: {
CommunicationPreference,
},
data() {
return {
preferences: [
{ name: "discounts_offers", type: "EMAIL", consent: true },
{ name: "newsletter", type: "EMAIL", consent: true },
{ name: "product_upgrade", type: "EMAIL", consent: true },
{ name: "sms_offer", type: "SMS", consent: true },
{ name: "post_offer", type: "POST", consent: true },
],
};
},
};
</script>
and in CommunicationPreference.vue
<template>
<div>
<label
><input
type="checkbox"
name="preference"
:value="consent"
:checked="consent === true"
#change="$emit('update:consent', $event.target.checked)"
/>{{ label }}</label
>
</div>
</template>
<script>
export default {
name: "CommunicationPreference",
props: {
type: String,
name: String,
consent: Boolean,
},
computed: {
label() {
if (this.name === "newsletter") {
return "Newsletters";
}
if (this.name === "discounts_offers") {
return "Discount and offers";
}
if (this.name === "product_upgrade") {
return "Upgrade recommendations";
}
return this.name;
},
},
};
</script>
Something like that....not tested though.

How to set validate rules in vuelidate to have value same as a field of an object?

Let say I have a vue component with data like this:
data: () => ({
form: {
old_password: {
data: '',
type: 'password',
label: 'Old Password',
},
new_password: {
data: '',
type: 'password',
label: 'New Password',
},
repeat_password: {
data: '',
type: 'password',
label: 'New Password Confirmation',
},
},
}),
The data is formatted in this way as I am using another plug-in, ant-design, to build the form, and therefore formatting the data in another way is not an option. The data field will be storing the actual data.
Then, I have the following validation rules set for vuelidate.
validations: {
form: {
old_password: {
data: { required },
},
new_password: {
data: { required },
},
repeat_password: {
data: { sameAsPassword: sameAs('new_password') },
},
},
},
The required rules works, but the sameAsPassword rule is not working. It always return error, even I am sure I am inputting the same password. I guess it is not comparing to a correct field. How can I set the rule so that it is comparing to the correct field?
new_password is not a sibling of repeat_password.data. From the built in validator docs
Locator can be either a sibling property name or a function. When provided as a function, it receives the model under validation as
argument and this is bound to the component instance so you can access
all its properties and methods, even in the scope of a nested
validation.
So a function needs to be passed to sameAs:
validations: {
form: {
old_password: {
data: { required },
},
new_password: {
data: { required },
},
repeat_password: {
data: {
sameAsPassword: sameAs(function() {
return this.form.new_password.data;
})
},
},
},
},
At the same time, for the this to be working inside the function, the data needed to be changed from arrow function to return data. i.e.
data() {
return {
form: {
old_password: {
data: '',
type: 'password',
label: 'Old Password',
},
new_password: {
data: '',
type: 'password',
label: 'New Password',
},
repeat_password: {
data: '',
type: 'password',
label: 'New Password Confirmation',
},
},
}
},
First of all: it's not a good idea to use arrow function for data. You should use:
data() {
return {
form: {}
}
}
You could get a context issues..
And I don't know if you need that data inside validations...you could try this:
export default {
data() {
return {
form: {
nestedA: '',
nestedB: ''
}
}
},
validations: {
form: {
nestedA: {
required
},
nestedB: {
required
}
}
}
}

Objects gets empty in VueJS Component

I'm trying to build an application in VueJS where I'm having a component something like this:
<template>
<div>
<base-subheader title="Members" icon="la-home" heading="Member Details" description="Home"></base-subheader>
<div class="m-content">
<div class="row">
<div class="col-xl-6">
<nits-form-portlet
title="Add Member/User"
info="Fill the details below to add user, fields which are mandatory are label with * (star) mark."
headIcon="flaticon-multimedia"
headerLine
apiUrl="api/user"
backUrl="members__home"
action="create"
:formElements="form_elements"
>
</nits-form-portlet>
</div>
<div class="col-xl-6">
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "member-add",
data() {
return {
form_elements: [
{
field_name: 'first_name',
config_elements: {
label: 'First Name *',
type: 'text',
inputStyle: 'pill',
placeholder: 'Enter first name of user',
formControl: true,
addonType: 'left-icon',
addon: {leftAddon: 'la-exclamation'},
},
value: '',
nitsFormType: 'nits-input'
},
{
field_name: 'last_name',
config_elements: {
label: 'Last Name',
type: 'text',
inputStyle: 'pill',
placeholder: 'Enter last name of user',
formControl: true,
addonType: 'left-icon',
addon: {leftAddon: 'la-exclamation'},
},
value: '',
nitsFormType: 'nits-input'
},
{
field_name: 'email',
config_elements: {
label: 'Email *',
type: 'email',
inputStyle: 'pill',
placeholder: 'Enter email of user',
formControl: true,
addonType: 'left',
addon: {leftAddon: '#'},
},
value: '',
nitsFormType: 'nits-input'
},
{
field_name: 'password',
config_elements: {
label: 'Password *',
type: 'password',
inputStyle: 'pill',
placeholder: 'Enter password',
formControl: true,
addonType: 'left-icon',
addon: {leftAddon: 'la-expeditedssl'},
},
value: '',
nitsFormType: 'nits-input'
},
{
field_name: 'confirm_password',
config_elements: {
label: 'Confirm Password *',
type: 'password',
inputStyle: 'pill',
placeholder: 'Confirm password should match',
formControl: true,
addonType: 'left-icon',
addon: {leftAddon: 'la-expeditedssl'},
},
value: '',
nitsFormType: 'nits-input'
},
{
field_name: 'role',
config_elements: {
label: 'Select Role *',
inputStyle: 'pill',
options: [
{option: 'Select one'},
{value: '1', option: 'Admin'},
{value: '2', option: 'Subscriber'},
{value: '3', option: 'Analyst'},
{value: '4', option: 'Guest'}
],
addonType: 'left-icon',
addon: {leftAddon: 'la-user-secret'},
},
value: '',
nitsFormType: 'nits-select'
},
{
field_name: 'profile_pic',
config_elements: {
label: 'Profile pic',
},
value: '',
nitsFormType: 'nits-file-input'
}
],
}
}
}
</script>
<style scoped>
</style>
Whenever I try to pass data to my component config_elements which holds an object of all attributes being passed to child component gets lost, if I move from other component to nits-form-portlet> it renders the child components appropriately, but in my Vue-debug tool it shows empty:
But the components are rendered properly:
And same happens to the props inside the component:
But in case of any event or refreshing the page it shows:
Since all the attributes are gone, if I try to configure my config_elements object data, I get the attributes but within a fraction of seconds it again goes to empty. But attributes gets passed and it displays the fields:
I am using render function to render these components so for nits-form-portlet component I have:
return createElement('div', { class: this.getClasses() }, [
createElement('div', { class: 'm-portlet__head' }, [
createElement('div', { class: 'm-portlet__head-caption' }, [element])
]),
createElement('form', { class: 'm-form m-form--fit m-form--label-align-right' }, [
createElement('div', { class: 'm-portlet__body' }, [
createElement('div', { class: 'form-group m-form__group m--margin-top-10' }, [infoElement]),
this.computedFormElements.map(a => {
if(this.error[a.field_name]) {
a.config_elements.error = this.error[a.field_name][0];
return createElement(a.nitsFormType, { attrs: a.config_elements, on: {
input: (event) => {
this.form[a.field_name] = event
}
}})
}
else
return createElement(a.nitsFormType, { attrs: a.config_elements, on: {
input: (event) => {
this.form[a.field_name] = event
}
}})
})
]),
footerElement
])
])
And I think factors affecting the template is this map statement:
this.computedFormElements.map(a => {
if(this.error[a.field_name]) {
a.config_elements.error = this.error[a.field_name][0];
return createElement(a.nitsFormType, { attrs: a.config_elements, on: {
input: (event) => {
this.form[a.field_name] = event
}
}})
}
else
return createElement(a.nitsFormType, { attrs: a.config_elements, on: {
input: (event) => {
this.form[a.field_name] = event
}
}})
})
but still I'm sharing my whole code of render function, Link to code # github hoping someone can help me with this strange situation.
It's a bit hard to tell for me without running the code, but I've noticed something that may be related.
I see that you're overwriting the formElements prop here:
a.config_elements.error = this.error[a.field_name][0];
Vue usually gives you a warning that you can't do that, but maybe because it's abstracted through the computed it's not doing so.
If you want to extend the data for use in the child component, then it's best to do copy of the object (preferably a deep copy using computed, which will allow you to have it dynamically updated)
because config_elements is an object, when you add error variable you are likely triggering an observer, that may be clearing the data in the parent.
Anyway, it's just a guess, but something you may want to resolve anyway.

VueJS 2: cannot set two-way data-binding for input components

Being quite new to Vue, I ran into the issue when I cannot establish binding between input/select tags inside components and the data inside Vue instance.
Example #1:
html
<sidebar-select
:title="UI.sidebar.localeSelect.title"
:name="UI.sidebar.localeSelect.name"
:options="UI.sidebar.localeSelect.options"
:vmodel="selectedLocale">
</sidebar-select>
<sidebar-select
:title="UI.sidebar.currencySelect.title"
:name="UI.sidebar.currencySelect.name"
:options="UI.sidebar.currencySelect.options"
:vmodel="state.currency">
</sidebar-select>
js - component
Vue.component('sidebar-select', {
props: ['title', 'name', 'options', 'vmodel'],
data() {
return {
vmodel: this.value
}
},
template: `<div class="col-xs-12 col-md-6" style="padding-left:0;padding-right:30px">
<div class="cg">
<label :for="name"><h4>{{ title }}</h4></label>
<select class="form-control form-horizontal" style="max-width: 300px"
:name="name"
v-model="vmodel">
<option v-for="option in options" :value="option.value">{{ option.text }}</option>
</select>
</div>
</div>`
});
js - data part
var app = new Vue({
el: '#app',
data: {
selectedLocale: 'ru',
user: {
'ru': {
name: 'Саша',
surname: 'Найдович',
position: 'программист',
phone: '1234567',
email: 'jdlfh#jdlbf.com'
},
'en': {
name: 'Alex',
surname: 'Naidovich',
position: 'frontend',
phone: '1234567',
email: '1234567#email.eu'
}
},
state: {
locale: 'ru',
currency: '€',
/* *** */
},
UI: {
sidebar: {
localeSelect: {
title: 'Язык КП',
name: 'offer-lang',
options: [
{value: 'en', text: 'International - English'},
/* *** */
{value: 'ru', text: 'Русский'}
],
preSelected: 'ru',
stateprop: 'locale'
},
currencySelect: {
title: 'Валюта КП',
name: 'offer-curr',
options: [
{value: '$', text: '$ (Dollar)'},
{value: '€', text: '€ (Euro)'},
{value: '₤', text: '₤ (UK Фунт)'},
{value: '₽', text: '₽ (RUS Рубль)'},
{value: '', text: 'Ввести вручную'}
],
preSelected: '€',
stateprop: 'currency'
},
} /* etc */
}
}
});
There are 2 errors I run into: [Vue warn]: The data property "vmodel" is already declared as a prop. Use prop default value instead. at init and [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: "vmodel" vhen I try to change selects. I wish to know what am I doing wrong and what would be the best-practice way for this case.
UPDATE: the main question is about how to properly use v-model two-way-data-binding when passing v-model as an argument into the props of component.
To be updated: example #2 with text inputs will be added tomorrow (the code is left at work).

How to read value of input field using PhoneJS?

Here is my HTML :
<div data-options="dxView : { name: 'home', title: 'Home' } " >
<div class="home-view" data-options="dxContent : { targetPlaceholder: 'content' } " >
<input data-bind="dxTextBox: { value: username }" />
<input data-bind="dxTextBox: { value: password }" />
<div data-bind="dxButton: { text: 'Login', clickAction: validateLogin }"></div>
</div>
Here is my JavaScript Controller
MyApp.home = function (params) {
var viewModel = {
username: ko.observable(''),
password: ko.observable(''),
validateLogin : function ()
{
console.log(this.username());
userNameTxt = this.username();
alert(this.username());
}
};
return viewModel;
};
The value of text fields is not getting retireved. Blank message is being alerted.
just change in alert "this" to "viewModel". like belo
MyApp.home = function (params) {
var viewModel = {
username: ko.observable(''),
password: ko.observable(''),
validateLogin : function ()
{
console.log(this.username());
userNameTxt = this.username();
alert(viewModel.username());
}
};
return viewModel;
};
userName: {
value: ko.observable(""),
valueUpdateEvent: 'keyup search',
placeholder: "Username",
},
password: {
value: ko.observable(""),
mode: 'password',
placeholder: "type a password",
valueUpdateEvent: 'keyup search',
},
check it

Categories

Resources