I have an array of objects that I loop through in my form questionnaire. There are five properties in each object, but only one property requires validation. I set up the validations part of the component as below:
specificGifts: {
$each: {
passThrough: {
required: requiredIf(function(value) {
return this.docs.includes('Will')
})
}
}
},
I saw on vuelidate documents that in my form html, instead of doing the following code below:
<div
v-for="(gift, i) in specificGifts"
:key="i"
>
<v-select
label="How does this specific gift pass if the recipient does not survive?"
v-model="gift.passThrough"
:items="specificGiftPassThrough"
></v-select>
</div>
I should use:
<div
v-for="(gift, i) in $v.specificGifts.$each.$iter"
:key="i"
>
<v-select
label="How does this specific gift pass if the recipient does not survive?"
v-model="gift.passThrough.$model"
:items="specificGiftPassThrough"
></v-select>
</div>
The data part of my vuejs component is as follows:
data(){
return{
specificGifts: []
}
}
However, I then get the following console error "Cannot read property $model of undefined". When I console.log $v.specificGifts.$each.$iter, I also get console errors. Does anyone know what I am doing wrong? Is there a better way to use validation? It seems vuelidate may not be up to speed in that it requires me to hardcode loop through a $v property just so I can use vuelidate, anyhow.
I've been facing the validation of a survey and want to share my solution.
My specific case is I have many questions and any of them has many answers (a group of radio buttons). My objective is to validate every question so that for each, one radio must be checked.
First I get the questions array of objects from API (my "value" properties are different):
[
{
"value":"a",
"name":"first question",
"answers":[
{
"label":"first answer",
"value":"a1"
},
{
"label":"second answer",
"value":"a2"
},
...
]
},
{
"value":"b",
"name":"second question",
"answers":[
{
"label":"first answer",
"value":"b1"
},
{
"label":"second answer",
"value":"b2"
},
...
]
}
...
]
Then, after getting the API response, I prepared another array of objects for the answers:
this.questions.map(function (item) {
return {
questionId: item.value,
answerId: null
};
})
the result is this structure:
[
{
questionId: 'a',
answerId: null,
},
{
questionId: 'b',
answerId: null,
}
...
]
I used quasar framework for the template but I think you can reproduce this logic with basic html:
<q-field v-for="(question, key) in questions" :key="question.value"
filled
:label="question.name"
>
<template v-slot:control>
<q-option-group
v-model="answers[key].answerId"
:options="question.answers"
type="radio"
/>
</template>
</q-field>
Finally my validation rule is:
validations: {
answers: {
$each: {
answerId: {
required
}
}
}
}
i've also had a lot of trouble with Vuelidate.
The best solution i can give you is to change from Vuelidate to VeeValidate.
It's not complicated to change and it's going to save you a lot of time in the long run.
VeeValidate offers a lot of easy ways to use validation including custom Messages for each rule (no more computed's to give error messages) and allow's you to use rules depending on certain conditions.
You can check the documentation here
Related
I am finding difficulty updating the values in the array of objects for nuxt i18n. How I can update the message in the validation array when the language got changed?
I found solutions to update single properties by moving them to the computed method but what to do with the array of objects?
//Template
<div v-for="validation in validations">
<v-alert type="danger">{{validations.message}} </v-alert>
<div>
//component
data() {
validations: []
},
methods() {
submit() {
//pushing validations array on submit method
this.validations.push('part', message: this.$t('error.required'));
this.validations.push('part', message: this.$t('error.mandatory'));
}
}
en.json
error:{
required:"required en",
mandatory:"mandatory en"
}
fr.json
error:{
required:"required fr",
mandatory:"mandatory fr"
}
The following example gives me a blank screen (jsfiddle here). Even the parts which have nothing to do with the loop are not being rendered.
HTML:
<div id="app">
<button #click="objectFromApi">
run objectFromApi function
</button>
<div
v-for="obj in myObject[0].results"
:key="obj.id"
>
<p>
{{ obj.message }}
</p>
</div>
</div>
JavaScript:
new Vue({
el: "#app",
data: {
myObject: []
},
methods: {
objectFromApi: function(){
this.myObject.push(
{
"count": 5,
"results": [
{
"id": 1,
"message": "object 1"
},
{
"id": 2,
"message": "object 2"
}
]
}
)
}
},
//created() {
// this.objectFromApi()
//}
})
Nevertheless it does work if:
1.) Either using objectFromApi function directly in the created life cycle hook (what I don't want!)
created() {
this.objectFromApi()
}
2.) Or (without the use of created life cycle hook) if I go directly into the nested results array and spread the objects out like this (what I also don't want!)
this.myObject.push(
...{
"count": 5,
"next": "http://127.0.0.1:8000/api/someurl/?page=2",
"previous": null,
"results": [
{
"id": 1,
"message": "object 1"
},
{
"id": 2,
"message": "object 2"
}
]
}.results
)
When using option 2.) of course the v-for loop has to look different:
v-for="obj in myObject" instead of v-for="obj in myObject[0].results"
What is wrong with my initial example?
When the component is first rendering the array myObject will be empty.
During rendering it attempts this:
<div
v-for="obj in myObject[0].results"
:key="obj.id"
>
The value of myObject[0] will be undefined. Attempting to access the results property of undefined will result in an error. This error will cause rendering to fail. Nothing will be shown, even the parts that didn't fail.
There are various ways to fix this problem. You could prepopulate the data with suitable empty properties:
data: {
myObject: [
{
results: []
}
]
}
Alternatively, as you've noted, you could change the loop to use v-for="obj in myObject", changing objectFromApi accordingly to only store the results array in myObject. Even if you don't want that exact change some similar change is probably a good idea because the [0] part strongly suggests you've got a problem with your data model. The key thing here is that it avoids trying to access nested objects that don't exist. The use of the spread operator in your second example is largely irrelevant.
Or you could skip the loop in the template:
<template v-if="myObject[0]">
<div
v-for="obj in myObject[0].results"
:key="obj.id"
>
...
</div>
</template>
I'm using v-validate with Vue. I'm trying to figure out how to force v-validate to update rules. For example, I have something like this:
<template>
<div v-for="field in fields">
<input :name="field.name" v-validate="field.rules">
</div>
</template>
<script>
export default {
data() {
fields: [
{
name: "city",
rules: {
included: []
}
}
]
}
}
</script>
As you can see, my "included" array is empty on page load. I get the array from an AJAX request, and then I update my data:
this.fields[0].rules.included = cities
But v-validate doesn't seem to acknowledge the newly-added array. It only works if I hardcode the cities into my data. How can I force v-validate to respond to the updated rules?
Vue.js is unable to track updates on nested reference types.
Try:
let fields = [...this.fields]
fields[0].rules = cities
this.fields = fields
Use Vue.set to track changes : https://v2.vuejs.org/v2/guide/reactivity.html
Vue.set(this.fields[0], 'rules', cities);
I have a auto search module with below json structure. I need to loop through aray of json objects and use key and value as per requirements.
I have tried below code. But with provided json object, I am able to retrieve key, but not value.
lets say, for firt, Json object, I need to retrieve 'Product Apple'., but I`m getting only link.
I tried response.data[key][0] ,but getting full json object. May I Know where I have done wrong.
I have updated plunker below
[{
"/folder1/folder2/folder3/product-1": "Product Apple"
},
{
"/folder1/folder2/folder3/product-2": "Product samsung"
},
{
"/folder1/folder2/folder3/product-3": "Product lenovo"
},
{
"/folder1/folder2/folder3/product-4": "Product Asus"
},
{
"/folder1/folder2/folder3/product-5": "Product Acer"
},
{
"/folder1/folder2/folder3/product-6": "Product Vivo"
},
{
"/folder1/folder2/folder3/product-7": "Product Oppo"
}
]
code here
Since this is marked as duplicate post., I have gone through the provided solution and found that the duplicate post has solution via javascript. But I`m looking to iterate through angularjs 'ng-repeat'.
Please find plunker below for solution I have got
[code here][1]
code here
You probably want to assign that structure to a variable, and then run an *ngFor on it, like this:
// in the component file
let results = [
{
"/folder1/folder2/folder3/product-1":"Product Apple"
},
{
"/folder1/folder2/folder3/product-2":"Product samsung"
},
{
"/folder1/folder2/folder3/product-3":"Product lenovo"
},
{
"/folder1/folder2/folder3/product-4":"Product Asus"
},
{
"/folder1/folder2/folder3/product-5":"Product Acer"
},
{
"/folder1/folder2/folder3/product-6":"Product Vivo"
},
{
"/folder1/folder2/folder3/product-7":"Product Oppo"
}
]
// in the view
<ng-container *ngFor="let result of results">
view logic goes here
</ng-container>
I'm Vue.js newbie and my task is:
make an ajax call (GET) to server, using RESTful API (Laravel on background)
retrieve a (JSON) list of Form CRUD items in array (like checkbox, input text, textarea...) with their properties (value, checked, custom classes...)
render CRUD form with these form items maybe using Vue's loop
I'm wondering if it could be rendered using components somehow. But I don't know the correct way.
Frankly, I exactly don't know how to solve this problem with Vue.js - rendering items from array and each item has it's own markup and properties (checkbox has it's own, textbox, select, textarea...).
I'm building a web application based on CRUD operations and I'm trying to write universal components. The easiest way is to do a special component with hard-written sub-components for each subpage, but I don't like this way if not needed.
Thank you!
EDIT: I don't have much code yet, but this is where I am...
<script>
// ./components/CrutList.vue
export default {
mounted() {},
data() {
return {
items: []
}
},
props: ['resource'],
methods: {
getItems() {
var resource = this.$resource('api/'+this.resource+'{/id}');
resource.get({}).then(function(items){
if(items.body.status == 'success'){
this.items = items.body.items;
}
}).bind(this);
},
deleteItem(item) {
// perform CRUD operation DELETE
alert('delete action');
}
}
}
</script>
My idea is using CrudList component to CRUD listing...
<crud-list resource="orders">
In laravel I do something like this:
return response()->json([
'status' => 'success',
'items' => [
[
'itemComponent' => 'checkbox',
'props' => [
'checked' => true,
'label' => "Checkbox č.1",
'name' => 'checkbox1'
]
],
[
'itemComponent' => 'checkbox',
'props' => [
'checked' => true,
'label' => "Checkbox č.2",
'name' => 'checkbox2'
]
],
[
'itemComponent' => 'checkbox',
'props' => [
'checked' => true,
'name' => 'checkbox3'
]
],
],
]);
...it's very simplified, but it's just example of what I'm doing.
Now the problem is:
take the 'itemComponent' part from the returned array item (this is in a loop),
if it's a checkbox, take (for example) Checkbox.vue component, fill it with properties ('props' part of the array item)
I read about slots, but it's not what I'm looking for. Is there something I can use for dynamic components?
Check out this jsFiddle working example for dynamic forms:
https://jsfiddle.net/mani04/kr8w4n73/1/
You can do it easily by using a lot of v-ifs for each and every form element type you might get from server. It is a bit cumbersome but I can't find any other way.
In the above example, I have the form structure as follows:
var formItems = [{
input_type: "text",
input_label: "Login",
values: {
value: "your_name#example.com"
}
},
{...},
{...}];
Once you have that data, then it is a matter of iterating through formItems, checking input_type and activating the relevant form control.
Here is how my dynamic form template looks like, for the above input:
<div v-for="formItem in formValues">
<div v-if="formItem.input_type == 'text'">
<input type="text" v-model="formItem.values.value">
</div>
<div v-if="formItem.input_type == 'password'">
<input type="password" v-model="formItem.values.value">
</div>
<div v-if="formItem.input_type == 'checkbox'">
<input type="checkbox" v-model="formItem.values.checked">
{{formItem.values.label}}
</div>
</div>
My jsFiddle example uses form-horizontal from bootstrap, and I am also able to display the labels well. If I put that in the example above, it will get cluttered and will not let you see how it works.
Hope it helps! You can change the formItems data structure to meet your needs, and modify the template accordingly.