How to reference a parent prop value in a vue-i18n translation string? - vue.js

I am trying to make a vuetify text field reusable and would like to pass some prop data from a parent component into the validation translation strings as a variable. This prop is a max. character value for validation.
Is it possible to insert a prop or data value into a translation string ? Something like ...
props: {
maxLength: { type: Number, default: 20 },
}
i18n: {messages: {
en: { name_length: "Max. {this.maxLength} characters" },...
}
I've tried:
"Max." + String(this.maxLength) + characters", but it comes out as undefined.
Here's the complete code for reference:
<template>
<v-text-field
v-model="text" :prepend-icon="iconfront"
:rules="nameRules" :name="name"
:label="label" :type="type">
</v-text-field>
</template>
<script>
export default {
props: {
value: {type: String},
iconfront: { type: String },
name: { type: String },
label: { type: String },
type: { type: String, default: 'text' },
minLength: { type: Number, default: 1 },
maxLength: { type: Number, default: 20 },
},
computed: {
text: { get() { return this.value },
set(val) { this.$emit('input', val) }
}
},
data () {
return {
nameRules: [
(v) => !!v || this.$i18n.t("name_rule"),
(v) => v && v.length <= this.maxLength || this.$i18n.t("name_length")
]
}
},
methods: {
onInput(input){
this.$emit('textFieldInput', input)
}
},
i18n: {
messages: {
en: {
name_rule: "required field",
**name_length: "Max. {this.maxLength} characters",**
confirmation_rule: "passwords must match",
email_rule: "email must be valid",
password_length: "Length must be" + String(this.minLength)+ "-" + String(this.minLength) + "characters",
},
de: {
name_rule: "Pflichtfeld",
name_length: "Max. 20 Zeichen",
confirmation_rule: "Passwörter müssen übereinstimmen",
email_rule: "Email muss gültig sein",
password_length: "Länge: 6-20 Zeichen",
},
fr: {
name_rule: "champs requis",
name_length: "20 caractères maximum",
confirmation_rule: "les mots de passe doivent correspondre",
email_rule: "email doit être valide",
password_length: "longueur requise: 6 à 20 caractères",
},
}
}, //end of translations
}
</script>

OK, found the answer after a bit of digging:
props: {
maxLength: { type: Number, default: 20 },
}
i18n: {messages: {
en: { name_length: "Max. {max} characters" },...
}
Then in the template, you can pass the prop into the translation:
$t("name_length", {max: this.maxLength})

Related

Vue-multiselect update field using AJAX

Here is my Vue multiselect component:
<multiselect v-model="selectedcategoryitem"
:options="options"
:multiple="true"
:internal-search="false"
group-values="libs"
group-label="category"
:group-select="true"
placeholder="Type to search"
track-by="value"
label="name"
v-on:select="toggleSelected">
<span slot="noResult">Oops! No elements found. Consider changing the search query.</span>
</multiselect>
And data and method:
data: { selectGoalID: 0
, selectedcategoryitem: []
, queryData: []
, options: [
{
value: 1,
category: 'item1',
libs: [
{ value: "1_1", name: 'name1(E)' },
{ value: "1_2", name: 'name2(P)' },
{ value: "1_3", name: 'name3(T)' },
{ value: "1_4", name: 'name4(F)' },
{ value: "1_5", name: 'name5' },
]
},
{
value: 2,
category: 'item2',
libs: [
{ value: "2_1", name: 'name1' },
{ value: "2_2", name: 'name2' }
]
},
{
value: 3,
category: 'item3',
libs: [
{ value: "3_1", name: 'name1' },
{ value: "3_2", name: 'name2' },
{ value: "3_3", name: 'name3' },
{ value: "3_4", name: 'name4' },
{ value: "3_5", name: 'name5' },
]
},
}
, methods: {
UpdateType: function (goal_id, selectedTypes) {
return $.ajax({
method: "POST"
, url: "#Url.Action("UpdateType", "Predict")"
, data: {
_goal_id: goal_id,
_selectedTypes: selectedTypes
}
, success: function (result) {
if (result.code == "S") {
}
else {
alertResultModel(result);
}
},
error: function (xhr, ajaxOptions, thrownError) {
alertInfo(xhr.statusText);
}
});
}
, toggleSelected: function (value) {
if (value.length > 0) {
this.queryData = JSON.parse(JSON.stringify(value));
}
console.log(this.queryData);
this.UpdateType(this.selectGoalID, this.queryData).then(
function (result) {
if (result.code == "S") {
}
else {
alertResultModel(result);
}
}
, function () {
alertError("Error!!");
}
);
}
}
And when I selected single item, console log return: null,
When i selected multiple items console log return:
(2) [{…}, {…}, __ob__: Observer]
0: {__ob__: Observer}
1: {__ob__: Observer}
length: 2
__ob__: Observer {value: Array(2), dep: Dep, vmCount: 0}
[[Prototype]]: Array
Question is:
Why first selected item is null, but v-model="selectedcategoryitem" selectedcategoryitem.length is 1.
How to convert value to JSON format send to Backend.
Step 1: Create an HTML template
<div id="app">
<multiselect
v-model="selectedcategoryitem"
:options="options"
:multiple="true"
:internal-search="false"
group-values="libs"
group-label="category"
:group-select="true"
placeholder="Type to search"
track-by="value"
label="name"
#input="onChange"
>
</multiselect>
<p>Selected Item: {{ selectedcategoryitem }}</p>
</div>
Step 2: Model data like,
data() {
return {
selectGoalID: 0,
selectedcategoryitem: [],
options: [
{
value: 1,
category: "item1",
libs: [
{ value: "1_1", name: "name1(E)" },
{ value: "1_2", name: "name2(P)" },
{ value: "1_3", name: "name3(T)" },
{ value: "1_4", name: "name4(F)" },
{ value: "1_5", name: "name5" },
],
},
{
value: 2,
category: "item2",
libs: [
{ value: "2_1", name: "name1" },
{ value: "2_2", name: "name2" },
],
},
{
value: 3,
category: "item3",
libs: [
{ value: "3_1", name: "name1" },
{ value: "3_2", name: "name2" },
{ value: "3_3", name: "name3" },
{ value: "3_4", name: "name4" },
{ value: "3_5", name: "name5" },
],
},
],
};
},
Step 3: Create an methods and call REST API call
methods: {
UpdateType: function (goal_id, selectedTypes) {
console.log("selectedTypes", selectedTypes);
return $.ajax({
method: "POST",
url: "#Url.Action('UpdateType', 'Predict')",
data: {
_goal_id: goal_id,
_selectedTypes: JSON.stringify(selectedTypes),
},
success: function (result) {
alert(result);
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.statusText);
},
});
},
onChange() {
console.log("this.selectedcategoryitem", this.selectedcategoryitem);
this.UpdateType(this.selectGoalID, this.selectedcategoryitem).then(
function (result) {
alert(result);
},
function () {
alert("Error!!");
}
);
},
},
You can see the logs for selected items and AJAX call form data
DEMO Link

VueJS date piker Vuetify listener

I need your help to solve a problem I am facing.
I am currently working with a computed on Vue.js, however I want to use a listener instead of this computed.
Do you know how I can switch from one to the other?
Thank you in advance.
import { MUTATION_entreePlanningCreate } from "./graphql/mutations";
export default {
name: "EntreePlanningForm",
data: function () {
return {
dates: ["", ""],
datesRules: [(v) => !!v || "Veuillez selectionner une date"],
label: "",
labelRules: [
(v) => v.length < 20 || "Le champ doit comporter moin de 20 caractère",
],
description: "",
};
},
methods: {
envoyer: function () {
if (this.$refs.form.validate()) {
this.$apollo
.mutate({
mutation: MUTATION_entreePlanningCreate,
variables: {
input: this.input,
},
})
.then((data) => {
console.log(data);
});
}
},
},
computed: {
input() {
return {
dateDebut: this.dates[0],
dateFin: this.dates[1],
label: "",
description: "",
};
},
},
};

Unable to validate a child component from a parent component

I have a selectbox component. I want to reused it in other components.I'm able to reused the selectbox component in other components by adding v-model directive on custom input component but unable to validate selectbox component from other components by using vuetify validation rules.
Test.vue
<v-form ref="form" class="mx-4" v-model="valid">
<Selectbox :name="ward_no" :validationRule="required()" v-model="test.ward_no"/>
<v-btn primary v-on:click="save" class="primary">Submit</v-btn>
export default {
data() {
return {
test: {
ward_no: ''
},
valid: false,
required(propertyType) {
return v => (v && v.length > 0) || `${propertyType} is required`;
},
};
}
Selectbox.vue
<select #change="$emit('input', $event.target.value)" >
<option
v-for="opt in options"
:key="opt.value"
:value="opt.value"
:selected="value === opt.value"
>
{{errorMessage}}
{{ opt.label || "No label" }}
</option>
</select>
export default {
props: {
label: {
type: String,
required: true
},
validationRule: {
type: String,
default: 'text',
validate: (val) => {
// we only cover these types in our input components.
return['requred'].indexOf(val) !== -1;
}
},
name: {
type: String
},
value: {
type: String,
required: true
}
},
data() {
return {
errorMessage: '',
option: "lorem",
options: [
{ label: "lorem", value: "lorem" },
{ label: "ipsum", value: "ipsum" }
]
};
},
methods:{
checkValidationRule(){
if(this.validationRule!="")
{
return this.validationRule.split('|').filter(function(item) {
return item();
})
// this.errorMessage!= ""?this.errorMessage + " | ":"";
}
},
required() {
this.errorMessage!= ""?this.errorMessage + " | ":"";
this.errorMessage += name+" is required";
},
}
};

Vee validate return true, but should return false

Im using Vuetify, and have a form where im using VeeValidate for form validation...
When im using this:
this.$validator.validateAll().then((result) => {
console.log("result form", result);
//result ? this.onSubmit() : scrollTo(0, 250);
});
It always returns true, even if the validation on my input field isn't valid...
The input looks like:
<v-textarea
filled
name="string"
:label="placeholderText"
auto-grow
single-line
:placeholder="placeholderText"
v-model="answer"
:required="isRequired"
v-validate:computedProp="checkRequired"
:error-messages="errors.collect('string')"
data-vv-name="string"
:hint="hintText"
#blur="updateAnswer"
></v-textarea>
The code for the input component:
export default {
$_veeValidate: {
validator: 'new'
},
name: 'String',
props: {
placeholderText: {
default: 'Add a value'
},
hintText: {
default: 'Add a value'
},
isRequired: {
default: true
}
},
data: () => ({
answer: ''
}),
computed: {
checkRequired() {
return this.isRequired ? 'required' : ''
}
},
methods: {
updateAnswer() {
this.$validator.validateAll();
this.$emit('updateAnswer', this.answer);
}
}
}
Im calling this.$validator.validateAll() in another component... The input component is a standalone component... I have it all wrappet in a form tag, and running the validate function on a submit
You have two choice:
Pass to the component the v-validate from the $attrs
Inject the $validator to the component
Parent
export default {
name: "App",
components: {
YourComponent
},
provide() {
return {
$validator: this.$validator
};
},
Child
$_veeValidate: {
validator: "new"
},
inject: ["$validator"],
name: "String",
You can also simplify your html code, see the VeeValidate Syntax
Html
v-validate="{ required: this.isRequired }"
And you can safely remove
:required="isRequired"

Validating a non existent field error when trying to use vee-validate

I am trying to make a multistep form. For that I made some Input, Dropdown Components.
FormInput.vue
<template>
<div class="field">
<label class="label has-text-left"
:for="fieldId">
{{ labelText }}
</label>
<div class="control">
<input
v-model="inputValue"
v-bind="$attrs"
class="input"
:id="fieldId"
#input="event => {$emit('input', event.target.value)}"
:class="{'is-danger': error,
'is-success': !error && instance && instance!==''}" >
</div>
<p class="help has-text-left danger"
v-show="error">
{{ error }}
</p>
</div>
</template>
<script>
export default {
name: 'FormInput',
props: {
labelText: String,
instance: [Number, String],
fieldId: String,
error: String
},
data () {
return {
inputValue: null
}
},
mounted () {
this.inputValue = this.instance
},
$_veeValidate: {
name () {
this.fieldId
},
value () {
this.instance
}
},
watch: {
instance: function(newValue) {
this.inputValue = newValue
}
}
}
</script>
I am using this FormInput components in many forms. Eg.
QuerySportsFitnessForm.vue
<div class="modal-form" v-if="step === 5">
<FormDropdown
v-if="form.standard !== 'dietician'"
labelText="What is the gender of the student??"
v-model="form.gender"
:options="genderArray"
:instance="form.gender"
ref="gender"></FormDropdown>
<FormInput
type="text"
labelText="Your locality"
v-model="form.location"
:instance="form.location"
fieldId="location"
v-validate="'required'"
name="location"
:error="errors.first('location')"
placeholder="eg.Heerapura"></FormInput>
<FormInput
type="textarea"
labelText="Your full address"
v-model="form.address"
:instance="form.address"
fieldId="address"
v-validate="'required|min:10'"
name="address"
:error="errors.first('address')"
placeholder="Enter your postal address"></FormInput>
<FormInput
type="textarea"
labelText="Anything else you would like to share with us?"
v-model="form.comment"
:instance="form.comment"
fieldId="comment"
placeholder="Comment here"></FormInput>
</div>
<script>
import axios from 'axios'
import FormInput from './FormInput'
import FormDropdown from './FormDropdown'
import FormCheckbox from './FormCheckbox'
export default {
components: {
FormDropdown,
FormInput,
FormCheckbox
},
name: 'QuerySportsFitnessForm',
props: {
},
data () {
return {
modalStatus: false,
step: 1,
progressValue: 0,
form: {
fees: 1000,
category: 'Sports and Fitness',
standard: '',
subjects: [],
level: '',
type_coaching: '',
travel: '',
number_of_student: '',
coaching_location: '',
days: '',
gender_preference: '',
location: '',
address: '',
name: '',
age: '',
contact: '',
email: '',
gender: '',
comment: ''
},
typeCoachingArray: [
{ value: "batch", text: "In a Batch"},
{ value: "1-on-1", text: "1-on-1 Sessions"},
{ value: "flexible", text: "Flexible to get best tutor"}
],
travelArray: [
{ value: "0-1", text: "0-1 Km"},
{ value: "0-3", text: "0-3 Km"},
{ value: "0-5", text: "0-5 Km"},
{ value: "more than 5", text: "More than 5 Km"}
],
coachingLocationArray: [
{ value: "at home", text: "At Home"},
{ value: "at tutor", text: "At Tutor's Location"},
{ value: "flexible", text: "Flexible to get best tutor"},
],
genderPreferenceArray: [
{ value: "male", text: "Male" },
{ value: "female", text: "Female" },
{ value: "none", text: "No preference" }
],
daysArray: [
{ value: "1", text: "1 day a week" },
{ value: "2", text: "2 days a week" },
{ value: "3", text: "3 days a week" },
{ value: "4", text: "4 days a week" },
{ value: "5", text: "5 days a week" },
{ value: "6", text: "6 days a week" },
{ value: "7", text: "7 days a week" }
],
levelArray: [
{ value: "beginner", text: "Beginner" },
{ value: "intermediate", text: "Intermediate" },
{ value: "advanced", text: "Advanced" }
],
genderArray: [
{ value: "male", text: "Male" },
{ value: "female", text: "Female" }
],
numberOfStudentArray: [
{ value: "One", text: "One" },
{ value: "Two", text: "Two" },
{ value: "Three", text: "Three" },
{ value: "More than Three", text: "More than Three" },
],
dieticianSubjects: [
"Weight Loss",
"Weight Gain",
"Health Condition",
"Sports Conditioning Diets"
],
martialArtsSubjects: [
"Karate",
"Taekwondo",
"Wing Chun",
],
trainerSubjects: [
"General Fitness",
"Intermediate Bodybuilding",
"Hardcore Bodybuilding"
],
yogaSubjects: [
"Power Yoga",
"Therapeutic Yoga",
"Yoga for Senior Citizen",
"Pregnancy Yoga",
"Yoga for Everyone"
]
}
},
mounted () {
this.form.standard = this.$route.params.standard
if (['dietician', 'trainer', 'yoga', 'martial-arts'].indexOf(this.form.standard) !== -1) {
this.step = 1
}
else {
this.step = 2
}
},
methods: {
modalToggle() {
this.modalStatus = !this.modalStatus
},
/*addToInstance method is corrosponding to checkbox component*/
addToInstance(e) {
if (this.form.subjects.indexOf(e) ===-1) {
this.form.subjects.push(e)
}
else {
this.form.subjects.splice(this.form.subjects.indexOf(e), 1)
}
},
prev() {
this.progressValue -= 25
this.step--;
},
next() {
if (this.step === 1) {
this.$validator.validate('subjects', this.form.subjects)
if (this.form.subjects.length!==0) {
this.step++
this.progressValue += 20
}
}
else if (this.step === 2) {
this.$refs.level.dropdownToggle()
this.$refs.genderPreference.dropdownToggle()
this.$refs.days.dropdownToggle()
if (['dietician', 'trainer', 'yoga', 'zumba'].indexOf(this.form.standard) === -1) {
if (this.form.level !== '' && this.form.days !== '') {
this.step++
this.progressValue += 20
}
}
else if (['dietician', 'trainer', 'yoga'].indexOf(this.form.standard) !== -1) {
if(this.form.gender_preference !== '' && this.form.days !== '') {
this.step++
this.progressValue +=20
}
}
else if (this.form.standard === 'zumba') {
if (this.form.days !== '') {
this.step++
this.progressValue += 20
}
}
}
else if (this.step === 3) {
if (this.form.standard !== 'dietician') {
this.$refs.typeCoaching.dropdownToggle()
}
if (['chess', 'skating', 'trainer', 'yoga', 'zumba'].indexOf(this.form.standard) !== -1 && this.form.type_coaching!=='batch') {
this.$refs.coachingLocation.dropdownToggle()
}
if (this.form.coaching_location !== 'at home') {
this.$refs.travel.dropdownToggle()
}
if (this.form.standard !== 'dietician') {
if (this.form.type_coaching !== '') {
if (['chess', 'skating', 'trainer', 'yoga', 'zumba'].indexOf(this.form.standard) !== -1) {
if (this.form.type_coaching !== 'batch' && this.form.coaching_location !=='') {
if (this.form.coaching_location !== 'at home' && this.form.travel !== '') {
this.step++
this.progressValue += 20
}
else if (this.form.coaching_location === 'at home' && this.form.travel === '') {
this.step++
this.progressValue += 20
}
}
else if (this.form.type_coaching === 'batch' && this.form.travel !== '') {
this.step++
this.progressValue += 20
}
}
else if (['chess', 'skating', 'trainer', 'yoga', 'zumba'].indexOf(this.form.standard) === -1) {
if (this.form.travel !== '') {
this.step++
this.progressValue += 20
}
}
}
}
else if (this.form.standard === 'dietician') {
if (this.form.coaching_location !== 'at home' && this.form.travel !== '') {
this.step++
this.progressValue += 20
}
else if (this.form.coaching_location === 'at home' && this.form.travel === '') {
this.step++
this.progressValue += 20
}
}
}
else if (this.step === 4) {
this.$validator.validate('name', this.form.name)
this.$validator.validate('contact', this.form.contact)
this.$validator.validate('email', this.form.email)
this.$validator.validate('age', this.form.age).then(() => {
if (this.errors.items.length === 0) {
this.step++
this.progressValue += 20
}
})
}
},
//Problem with this block I think
validateBeforeSubmit() {
this.$refs.gender.dropdownToggle()
this.$validator.validate('location', this.form.location)
this.$validator.validate('address', this.form.address)
if (this.form.standard === 'dietician') {
if (this.errors.items.length === 0) {
this.addQuery()
this.modalToggle()
}
}
else if (this.form.standard !== 'dietician') {
if (this.errors.items.length === 0 && this.form.gender !== '') {
this.addQuery()
this.modalToggle()
}
}
},
emptyCoachingLocation() {
this.form.coaching_location = ''
},
emptyTravel() {
this.form.travel = ''
},
addQuery() {
axios({
method: 'post',
url: 'http://127.0.0.1:8000/api/hobby-query/',
data: {
fees: this.form.fees,
category: this.form.category,
standard: this.form.standard,
subjects: this.form.subjects,
level: this.form.level,
type_coaching: this.form.type_coaching,
travel: this.form.travel,
number_of_student: this.form.number_of_student,
coaching_location: this.form.coaching_location,
days: parseInt(this.form.days),
gender_preference: this.form.gender_preference,
location: this.form.location,
address: this.form.address,
name: this.form.name,
age: parseInt(this.form.age),
contact: this.form.contact,
email: this.form.email,
student_gender: this.form.gender,
comment: this.form.comment
}
}).then(() => {
this.form.subjects = [],
this.form.level = '',
this.form.type_coaching = '',
this.form.travel = '',
this.form.number_of_student = '',
this.form.coaching_location = '',
this.form.days = '',
this.form.gender_preference = '',
this.form.location = '',
this.form.address = '',
this.form.name = '',
this.form.age = '',
this.form.contact = '',
this.form.email = '',
this.form.gender = '',
this.form.comment = ''
})
.catch((error) => {
console.log(error);
});
}
}
}
</script>
I only included one div for example. When I am click "next" button method next() works without any problem. When I click submit button it fires up validateBeforeSubmit() method. And I get this error 2 times in console of developer tools.
Uncaught (in promise) Error: [vee-validate] Validating a non-existent field: "". Use "attach()" first.
at createError (vee-validate.esm.js?00d1:297)
at Validator._handleFieldNotFound (vee-validate.esm.js?00d1:2282)
at Validator.validate (vee-validate.esm.js?00d1:1959)
at ScopedValidator.validate (vee-validate.esm.js?00d1:3276)
at VueComponent.validateBeforeSubmit (QuerySportsFitnessForm.vue?cb73:448)
at invoker (vue.esm.js?efeb:2027)
at HTMLButtonElement.fn._withTask.fn._withTask (vue.esm.js?efeb:1826)
createError # vee-validate.esm.js?00d1:297
_handleFieldNotFound # vee-validate.esm.js?00d1:2282
validate # vee-validate.esm.js?00d1:1959
validate # vee-validate.esm.js?00d1:3276
validateBeforeSubmit # QuerySportsFitnessForm.vue?cb73:448
invoker # vue.esm.js?efeb:2027
fn._withTask.fn._withTask # vue.esm.js?efeb:1826
I tried to give .then promise after validate method call also but it didn't changed anything. like this
validateBeforeSubmit() {
this.$refs.gender.dropdownToggle()
this.$validator.validate('location', this.form.location)
this.$validator.validate('address', this.form.address).then(() {
if (this.form.standard === 'dietician') {
if (this.errors.items.length === 0) {
this.addQuery()
this.modalToggle()
}
}
else if (this.form.standard !== 'dietician') {
if (this.errors.items.length === 0 && this.form.gender !== '') {
this.addQuery()
this.modalToggle()
}
}
})
}
What am i doing wrong, I am not able to figure it out myself
I think this issue is happening because either location or address is not present in html due to step === 5 condition and you still try to validate that part. You can put below condition to get rid of this error===>
if (this.$validator.fields.find({ name: 'location' })) {
this.$validator.validate('location');
}
The problem seems to be that the validation's value change listener on the input was getting fired when cleared the variable, which then tried to validate the input, but the input was no longer in the validator's field list because it was detached when it was hidden.
this.$validator.pause()
this.$nextTick(() => {
this.$validator.errors.clear()
this.$validator.fields.items.forEach(field => field.reset())
this.$validator.fields.items.forEach(field => this.errors.remove(field))
this.$validator.resume()
})
This code will solve the issue.